2005-04-17 02:20:36 +04:00
/*
* Joystick device driver for the input driver suite .
*
* Copyright ( c ) 1999 - 2002 Vojtech Pavlik
* Copyright ( c ) 1999 Colin Van Dyke
*
* 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 .
*/
# include <asm/io.h>
# include <asm/system.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/joystick.h>
# include <linux/input.h>
# include <linux/kernel.h>
# include <linux/major.h>
2009-10-04 16:11:37 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/miscdevice.h>
# include <linux/module.h>
# include <linux/poll.h>
# include <linux/init.h>
# include <linux/device.h>
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Joystick device interfaces " ) ;
MODULE_SUPPORTED_DEVICE ( " input/js " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define JOYDEV_MINOR_BASE 0
# define JOYDEV_MINORS 16
# define JOYDEV_BUFFER_SIZE 64
struct joydev {
int exist ;
int open ;
int minor ;
struct input_handle handle ;
wait_queue_head_t wait ;
2007-04-12 09:30:00 +04:00
struct list_head client_list ;
2007-08-30 08:22:32 +04:00
spinlock_t client_lock ; /* protects client_list */
struct mutex mutex ;
2007-06-15 07:32:24 +04:00
struct device dev ;
2010-05-21 09:52:58 +04:00
struct js_corr corr [ ABS_CNT ] ;
2005-04-17 02:20:36 +04:00
struct JS_DATA_SAVE_TYPE glue ;
int nabs ;
int nkey ;
__u16 keymap [ KEY_MAX - BTN_MISC + 1 ] ;
__u16 keypam [ KEY_MAX - BTN_MISC + 1 ] ;
2010-05-21 09:52:58 +04:00
__u8 absmap [ ABS_CNT ] ;
__u8 abspam [ ABS_CNT ] ;
__s16 abs [ ABS_CNT ] ;
2005-04-17 02:20:36 +04:00
} ;
2007-04-12 09:30:00 +04:00
struct joydev_client {
2005-04-17 02:20:36 +04:00
struct js_event buffer [ JOYDEV_BUFFER_SIZE ] ;
int head ;
int tail ;
int startup ;
2007-08-30 08:22:32 +04:00
spinlock_t buffer_lock ; /* protects access to buffer, head and tail */
2005-04-17 02:20:36 +04:00
struct fasync_struct * fasync ;
struct joydev * joydev ;
struct list_head node ;
} ;
static struct joydev * joydev_table [ JOYDEV_MINORS ] ;
2007-08-30 08:22:32 +04:00
static DEFINE_MUTEX ( joydev_table_mutex ) ;
2005-04-17 02:20:36 +04:00
static int joydev_correct ( int value , struct js_corr * corr )
{
switch ( corr - > type ) {
2007-08-30 08:22:32 +04:00
case JS_CORR_NONE :
break ;
case JS_CORR_BROKEN :
value = value > corr - > coef [ 0 ] ? ( value < corr - > coef [ 1 ] ? 0 :
( ( corr - > coef [ 3 ] * ( value - corr - > coef [ 1 ] ) ) > > 14 ) ) :
( ( corr - > coef [ 2 ] * ( value - corr - > coef [ 0 ] ) ) > > 14 ) ;
break ;
default :
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:48:47 +04:00
return value < - 32767 ? - 32767 : ( value > 32767 ? 32767 : value ) ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:32 +04:00
static void joydev_pass_event ( struct joydev_client * client ,
struct js_event * event )
{
struct joydev * joydev = client - > joydev ;
/*
* IRQs already disabled , just acquire the lock
*/
spin_lock ( & client - > buffer_lock ) ;
client - > buffer [ client - > head ] = * event ;
if ( client - > startup = = joydev - > nabs + joydev - > nkey ) {
client - > head + + ;
client - > head & = JOYDEV_BUFFER_SIZE - 1 ;
if ( client - > tail = = client - > head )
client - > startup = 0 ;
}
spin_unlock ( & client - > buffer_lock ) ;
kill_fasync ( & client - > fasync , SIGIO , POLL_IN ) ;
}
static void joydev_event ( struct input_handle * handle ,
unsigned int type , unsigned int code , int value )
2005-04-17 02:20:36 +04:00
{
struct joydev * joydev = handle - > private ;
2007-04-12 09:30:00 +04:00
struct joydev_client * client ;
2005-04-17 02:20:36 +04:00
struct js_event event ;
switch ( type ) {
2007-08-30 08:22:32 +04:00
case EV_KEY :
if ( code < BTN_MISC | | value = = 2 )
return ;
event . type = JS_EVENT_BUTTON ;
event . number = joydev - > keymap [ code - BTN_MISC ] ;
event . value = value ;
break ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
case EV_ABS :
event . type = JS_EVENT_AXIS ;
event . number = joydev - > absmap [ code ] ;
event . value = joydev_correct ( value ,
& joydev - > corr [ event . number ] ) ;
if ( event . value = = joydev - > abs [ event . number ] )
2005-04-17 02:20:36 +04:00
return ;
2007-08-30 08:22:32 +04:00
joydev - > abs [ event . number ] = event . value ;
break ;
default :
return ;
2005-04-17 02:20:36 +04:00
}
2005-07-11 10:08:56 +04:00
event . time = jiffies_to_msecs ( jiffies ) ;
2005-04-17 02:20:36 +04:00
2007-10-13 23:46:55 +04:00
rcu_read_lock ( ) ;
2007-08-30 08:22:32 +04:00
list_for_each_entry_rcu ( client , & joydev - > client_list , node )
joydev_pass_event ( client , & event ) ;
2007-10-13 23:46:55 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
wake_up_interruptible ( & joydev - > wait ) ;
}
static int joydev_fasync ( int fd , struct file * file , int on )
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
2006-06-26 09:48:47 +04:00
2009-02-02 00:52:56 +03:00
return fasync_helper ( fd , file , on , & client - > fasync ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-15 07:32:24 +04:00
static void joydev_free ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2007-06-15 07:32:24 +04:00
struct joydev * joydev = container_of ( dev , struct joydev , dev ) ;
2008-04-01 08:22:53 +04:00
input_put_device ( joydev - > handle . dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( joydev ) ;
}
2007-08-30 08:22:32 +04:00
static void joydev_attach_client ( struct joydev * joydev ,
struct joydev_client * client )
{
spin_lock ( & joydev - > client_lock ) ;
list_add_tail_rcu ( & client - > node , & joydev - > client_list ) ;
spin_unlock ( & joydev - > client_lock ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:32 +04:00
}
static void joydev_detach_client ( struct joydev * joydev ,
struct joydev_client * client )
{
spin_lock ( & joydev - > client_lock ) ;
list_del_rcu ( & client - > node ) ;
spin_unlock ( & joydev - > client_lock ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:32 +04:00
}
static int joydev_open_device ( struct joydev * joydev )
{
int retval ;
retval = mutex_lock_interruptible ( & joydev - > mutex ) ;
if ( retval )
return retval ;
if ( ! joydev - > exist )
retval = - ENODEV ;
2007-10-12 22:18:40 +04:00
else if ( ! joydev - > open + + ) {
2007-08-30 08:22:32 +04:00
retval = input_open_device ( & joydev - > handle ) ;
2007-10-12 22:18:40 +04:00
if ( retval )
joydev - > open - - ;
}
2007-08-30 08:22:32 +04:00
mutex_unlock ( & joydev - > mutex ) ;
return retval ;
}
static void joydev_close_device ( struct joydev * joydev )
{
mutex_lock ( & joydev - > mutex ) ;
if ( joydev - > exist & & ! - - joydev - > open )
input_close_device ( & joydev - > handle ) ;
mutex_unlock ( & joydev - > mutex ) ;
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device .
*/
static void joydev_hangup ( struct joydev * joydev )
{
struct joydev_client * client ;
spin_lock ( & joydev - > client_lock ) ;
list_for_each_entry ( client , & joydev - > client_list , node )
kill_fasync ( & client - > fasync , SIGIO , POLL_HUP ) ;
spin_unlock ( & joydev - > client_lock ) ;
wake_up_interruptible ( & joydev - > wait ) ;
}
2007-04-12 09:30:00 +04:00
static int joydev_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
struct joydev * joydev = client - > joydev ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
joydev_detach_client ( joydev , client ) ;
2007-04-12 09:30:00 +04:00
kfree ( client ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
joydev_close_device ( joydev ) ;
2007-06-15 07:32:24 +04:00
put_device ( & joydev - > dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int joydev_open ( struct inode * inode , struct file * file )
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client ;
struct joydev * joydev ;
2005-04-17 02:20:36 +04:00
int i = iminor ( inode ) - JOYDEV_MINOR_BASE ;
2007-04-12 09:30:15 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
if ( i > = JOYDEV_MINORS )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
error = mutex_lock_interruptible ( & joydev_table_mutex ) ;
if ( error )
return error ;
2007-04-12 09:30:00 +04:00
joydev = joydev_table [ i ] ;
2007-08-30 08:22:32 +04:00
if ( joydev )
get_device ( & joydev - > dev ) ;
mutex_unlock ( & joydev_table_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
if ( ! joydev )
return - ENODEV ;
2007-06-15 07:32:24 +04:00
2007-04-12 09:30:00 +04:00
client = kzalloc ( sizeof ( struct joydev_client ) , GFP_KERNEL ) ;
2007-06-15 07:32:24 +04:00
if ( ! client ) {
error = - ENOMEM ;
goto err_put_joydev ;
}
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
spin_lock_init ( & client - > buffer_lock ) ;
2007-04-12 09:30:00 +04:00
client - > joydev = joydev ;
2007-08-30 08:22:32 +04:00
joydev_attach_client ( joydev , client ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
error = joydev_open_device ( joydev ) ;
if ( error )
goto err_free_client ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
file - > private_data = client ;
2010-02-04 11:30:42 +03:00
nonseekable_open ( inode , file ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2007-06-15 07:32:24 +04:00
err_free_client :
2007-08-30 08:22:32 +04:00
joydev_detach_client ( joydev , client ) ;
2007-06-15 07:32:24 +04:00
kfree ( client ) ;
err_put_joydev :
put_device ( & joydev - > dev ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:32 +04:00
static int joydev_generate_startup_event ( struct joydev_client * client ,
struct input_dev * input ,
struct js_event * event )
2005-04-17 02:20:36 +04:00
{
2007-08-30 08:22:32 +04:00
struct joydev * joydev = client - > joydev ;
int have_event ;
spin_lock_irq ( & client - > buffer_lock ) ;
have_event = client - > startup < joydev - > nabs + joydev - > nkey ;
if ( have_event ) {
event - > time = jiffies_to_msecs ( jiffies ) ;
if ( client - > startup < joydev - > nkey ) {
event - > type = JS_EVENT_BUTTON | JS_EVENT_INIT ;
event - > number = client - > startup ;
event - > value = ! ! test_bit ( joydev - > keypam [ event - > number ] ,
input - > key ) ;
} else {
event - > type = JS_EVENT_AXIS | JS_EVENT_INIT ;
event - > number = client - > startup - joydev - > nkey ;
event - > value = joydev - > abs [ event - > number ] ;
}
client - > startup + + ;
}
spin_unlock_irq ( & client - > buffer_lock ) ;
return have_event ;
}
static int joydev_fetch_next_event ( struct joydev_client * client ,
struct js_event * event )
{
int have_event ;
spin_lock_irq ( & client - > buffer_lock ) ;
have_event = client - > head ! = client - > tail ;
if ( have_event ) {
* event = client - > buffer [ client - > tail + + ] ;
client - > tail & = JOYDEV_BUFFER_SIZE - 1 ;
}
spin_unlock_irq ( & client - > buffer_lock ) ;
return have_event ;
}
/*
* Old joystick interface
*/
static ssize_t joydev_0x_read ( struct joydev_client * client ,
struct input_dev * input ,
char __user * buf )
{
struct joydev * joydev = client - > joydev ;
struct JS_DATA_TYPE data ;
int i ;
spin_lock_irq ( & input - > event_lock ) ;
/*
* Get device state
*/
for ( data . buttons = i = 0 ; i < 32 & & i < joydev - > nkey ; i + + )
data . buttons | =
test_bit ( joydev - > keypam [ i ] , input - > key ) ? ( 1 < < i ) : 0 ;
data . x = ( joydev - > abs [ 0 ] / 256 + 128 ) > > joydev - > glue . JS_CORR . x ;
data . y = ( joydev - > abs [ 1 ] / 256 + 128 ) > > joydev - > glue . JS_CORR . y ;
/*
* Reset reader ' s event queue
*/
spin_lock ( & client - > buffer_lock ) ;
client - > startup = 0 ;
client - > tail = client - > head ;
spin_unlock ( & client - > buffer_lock ) ;
spin_unlock_irq ( & input - > event_lock ) ;
if ( copy_to_user ( buf , & data , sizeof ( struct JS_DATA_TYPE ) ) )
return - EFAULT ;
return sizeof ( struct JS_DATA_TYPE ) ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:32 +04:00
static inline int joydev_data_pending ( struct joydev_client * client )
{
struct joydev * joydev = client - > joydev ;
return client - > startup < joydev - > nabs + joydev - > nkey | |
client - > head ! = client - > tail ;
}
static ssize_t joydev_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
struct joydev * joydev = client - > joydev ;
2005-04-17 02:20:36 +04:00
struct input_dev * input = joydev - > handle . dev ;
2007-08-30 08:22:32 +04:00
struct js_event event ;
int retval ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
if ( ! joydev - > exist )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
if ( count < sizeof ( struct js_event ) )
return - EINVAL ;
2007-08-30 08:22:32 +04:00
if ( count = = sizeof ( struct JS_DATA_TYPE ) )
return joydev_0x_read ( client , input , buf ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
if ( ! joydev_data_pending ( client ) & & ( file - > f_flags & O_NONBLOCK ) )
2006-06-26 09:48:47 +04:00
return - EAGAIN ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
retval = wait_event_interruptible ( joydev - > wait ,
2007-08-30 08:22:32 +04:00
! joydev - > exist | | joydev_data_pending ( client ) ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
return retval ;
2007-04-12 09:30:00 +04:00
if ( ! joydev - > exist )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-08-30 08:22:32 +04:00
while ( retval + sizeof ( struct js_event ) < = count & &
joydev_generate_startup_event ( client , input , & event ) ) {
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( buf + retval , & event , sizeof ( struct js_event ) ) )
return - EFAULT ;
retval + = sizeof ( struct js_event ) ;
}
2007-08-30 08:22:32 +04:00
while ( retval + sizeof ( struct js_event ) < = count & &
joydev_fetch_next_event ( client , & event ) ) {
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
if ( copy_to_user ( buf + retval , & event , sizeof ( struct js_event ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
retval + = sizeof ( struct js_event ) ;
}
return retval ;
}
/* No kernel lock - fine */
static unsigned int joydev_poll ( struct file * file , poll_table * wait )
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
struct joydev * joydev = client - > joydev ;
2006-06-26 09:48:47 +04:00
2007-04-12 09:30:00 +04:00
poll_wait ( file , & joydev - > wait , wait ) ;
2007-08-30 08:22:32 +04:00
return ( joydev_data_pending ( client ) ? ( POLLIN | POLLRDNORM ) : 0 ) |
( joydev - > exist ? 0 : ( POLLHUP | POLLERR ) ) ;
2005-04-17 02:20:36 +04:00
}
2009-08-26 06:24:22 +04:00
static int joydev_handle_JSIOCSAXMAP ( struct joydev * joydev ,
void __user * argp , size_t len )
{
__u8 * abspam ;
int i ;
int retval = 0 ;
len = min ( len , sizeof ( joydev - > abspam ) ) ;
/* Validate the map. */
abspam = kmalloc ( len , GFP_KERNEL ) ;
if ( ! abspam )
return - ENOMEM ;
if ( copy_from_user ( abspam , argp , len ) ) {
retval = - EFAULT ;
goto out ;
}
for ( i = 0 ; i < joydev - > nabs ; i + + ) {
if ( abspam [ i ] > ABS_MAX ) {
retval = - EINVAL ;
goto out ;
}
}
memcpy ( joydev - > abspam , abspam , len ) ;
out :
kfree ( abspam ) ;
return retval ;
}
static int joydev_handle_JSIOCSBTNMAP ( struct joydev * joydev ,
void __user * argp , size_t len )
{
__u16 * keypam ;
int i ;
int retval = 0 ;
len = min ( len , sizeof ( joydev - > keypam ) ) ;
/* Validate the map. */
keypam = kmalloc ( len , GFP_KERNEL ) ;
if ( ! keypam )
return - ENOMEM ;
if ( copy_from_user ( keypam , argp , len ) ) {
retval = - EFAULT ;
goto out ;
}
for ( i = 0 ; i < joydev - > nkey ; i + + ) {
if ( keypam [ i ] > KEY_MAX | | keypam [ i ] < BTN_MISC ) {
retval = - EINVAL ;
goto out ;
}
}
memcpy ( joydev - > keypam , keypam , len ) ;
for ( i = 0 ; i < joydev - > nkey ; i + + )
joydev - > keymap [ keypam [ i ] - BTN_MISC ] = i ;
out :
kfree ( keypam ) ;
return retval ;
}
2007-08-30 08:22:32 +04:00
static int joydev_ioctl_common ( struct joydev * joydev ,
unsigned int cmd , void __user * argp )
2005-04-17 02:20:36 +04:00
{
struct input_dev * dev = joydev - > handle . dev ;
2009-08-12 12:12:08 +04:00
size_t len ;
2005-04-17 02:20:36 +04:00
int i , j ;
2009-08-12 12:12:08 +04:00
const char * name ;
2005-04-17 02:20:36 +04:00
2009-08-12 12:12:08 +04:00
/* Process fixed-sized commands. */
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2007-08-30 08:22:32 +04:00
case JS_SET_CAL :
return copy_from_user ( & joydev - > glue . JS_CORR , argp ,
2005-05-29 11:26:31 +04:00
sizeof ( joydev - > glue . JS_CORR ) ) ? - EFAULT : 0 ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JS_GET_CAL :
return copy_to_user ( argp , & joydev - > glue . JS_CORR ,
2005-05-29 11:26:31 +04:00
sizeof ( joydev - > glue . JS_CORR ) ) ? - EFAULT : 0 ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JS_SET_TIMEOUT :
return get_user ( joydev - > glue . JS_TIMEOUT , ( s32 __user * ) argp ) ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JS_GET_TIMEOUT :
return put_user ( joydev - > glue . JS_TIMEOUT , ( s32 __user * ) argp ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
case JSIOCGVERSION :
return put_user ( JS_VERSION , ( __u32 __user * ) argp ) ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JSIOCGAXES :
return put_user ( joydev - > nabs , ( __u8 __user * ) argp ) ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JSIOCGBUTTONS :
return put_user ( joydev - > nkey , ( __u8 __user * ) argp ) ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JSIOCSCORR :
if ( copy_from_user ( joydev - > corr , argp ,
sizeof ( joydev - > corr [ 0 ] ) * joydev - > nabs ) )
return - EFAULT ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
for ( i = 0 ; i < joydev - > nabs ; i + + ) {
j = joydev - > abspam [ i ] ;
joydev - > abs [ i ] = joydev_correct ( dev - > abs [ j ] ,
& joydev - > corr [ i ] ) ;
}
return 0 ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:32 +04:00
case JSIOCGCORR :
return copy_to_user ( argp , joydev - > corr ,
sizeof ( joydev - > corr [ 0 ] ) * joydev - > nabs ) ? - EFAULT : 0 ;
2009-08-12 12:12:08 +04:00
}
/*
* Process variable - sized commands ( the axis and button map commands
* are considered variable - sized to decouple them from the values of
* ABS_MAX and KEY_MAX ) .
*/
switch ( cmd & ~ IOCSIZE_MASK ) {
case ( JSIOCSAXMAP & ~ IOCSIZE_MASK ) :
2009-08-26 06:24:22 +04:00
return joydev_handle_JSIOCSAXMAP ( joydev , argp , _IOC_SIZE ( cmd ) ) ;
2007-08-30 08:22:32 +04:00
2009-08-12 12:12:08 +04:00
case ( JSIOCGAXMAP & ~ IOCSIZE_MASK ) :
len = min_t ( size_t , _IOC_SIZE ( cmd ) , sizeof ( joydev - > abspam ) ) ;
2009-08-26 06:24:22 +04:00
return copy_to_user ( argp , joydev - > abspam , len ) ? - EFAULT : len ;
2009-08-12 12:12:08 +04:00
case ( JSIOCSBTNMAP & ~ IOCSIZE_MASK ) :
2009-08-26 06:24:22 +04:00
return joydev_handle_JSIOCSBTNMAP ( joydev , argp , _IOC_SIZE ( cmd ) ) ;
2007-08-30 08:22:32 +04:00
2009-08-12 12:12:08 +04:00
case ( JSIOCGBTNMAP & ~ IOCSIZE_MASK ) :
len = min_t ( size_t , _IOC_SIZE ( cmd ) , sizeof ( joydev - > keypam ) ) ;
2009-08-26 06:24:22 +04:00
return copy_to_user ( argp , joydev - > keypam , len ) ? - EFAULT : len ;
2007-08-30 08:22:32 +04:00
2009-08-12 12:12:08 +04:00
case JSIOCGNAME ( 0 ) :
name = dev - > name ;
if ( ! name )
return 0 ;
len = min_t ( size_t , _IOC_SIZE ( cmd ) , strlen ( name ) + 1 ) ;
return copy_to_user ( argp , name , len ) ? - EFAULT : len ;
2005-04-17 02:20:36 +04:00
}
2009-08-12 12:12:08 +04:00
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-05-29 11:26:31 +04:00
# ifdef CONFIG_COMPAT
2007-08-30 08:22:32 +04:00
static long joydev_compat_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
2005-05-29 11:26:31 +04:00
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
struct joydev * joydev = client - > joydev ;
2005-05-29 11:26:31 +04:00
void __user * argp = ( void __user * ) arg ;
s32 tmp32 ;
struct JS_DATA_SAVE_TYPE_32 ds32 ;
2007-08-30 08:22:32 +04:00
int retval ;
2005-05-29 11:26:31 +04:00
2007-08-30 08:22:32 +04:00
retval = mutex_lock_interruptible ( & joydev - > mutex ) ;
if ( retval )
return retval ;
if ( ! joydev - > exist ) {
retval = - ENODEV ;
goto out ;
}
switch ( cmd ) {
2006-06-26 09:48:47 +04:00
2005-05-29 11:26:31 +04:00
case JS_SET_TIMELIMIT :
2007-08-30 08:22:32 +04:00
retval = get_user ( tmp32 , ( s32 __user * ) arg ) ;
if ( retval = = 0 )
2005-05-29 11:26:31 +04:00
joydev - > glue . JS_TIMELIMIT = tmp32 ;
break ;
2007-08-30 08:22:32 +04:00
2005-05-29 11:26:31 +04:00
case JS_GET_TIMELIMIT :
tmp32 = joydev - > glue . JS_TIMELIMIT ;
2007-08-30 08:22:32 +04:00
retval = put_user ( tmp32 , ( s32 __user * ) arg ) ;
2005-05-29 11:26:31 +04:00
break ;
case JS_SET_ALL :
2007-08-30 08:22:32 +04:00
retval = copy_from_user ( & ds32 , argp ,
sizeof ( ds32 ) ) ? - EFAULT : 0 ;
if ( retval = = 0 ) {
2005-05-29 11:26:31 +04:00
joydev - > glue . JS_TIMEOUT = ds32 . JS_TIMEOUT ;
joydev - > glue . BUSY = ds32 . BUSY ;
joydev - > glue . JS_EXPIRETIME = ds32 . JS_EXPIRETIME ;
joydev - > glue . JS_TIMELIMIT = ds32 . JS_TIMELIMIT ;
joydev - > glue . JS_SAVE = ds32 . JS_SAVE ;
joydev - > glue . JS_CORR = ds32 . JS_CORR ;
}
break ;
case JS_GET_ALL :
ds32 . JS_TIMEOUT = joydev - > glue . JS_TIMEOUT ;
ds32 . BUSY = joydev - > glue . BUSY ;
ds32 . JS_EXPIRETIME = joydev - > glue . JS_EXPIRETIME ;
ds32 . JS_TIMELIMIT = joydev - > glue . JS_TIMELIMIT ;
ds32 . JS_SAVE = joydev - > glue . JS_SAVE ;
ds32 . JS_CORR = joydev - > glue . JS_CORR ;
2007-08-30 08:22:32 +04:00
retval = copy_to_user ( argp , & ds32 , sizeof ( ds32 ) ) ? - EFAULT : 0 ;
2005-05-29 11:26:31 +04:00
break ;
default :
2007-08-30 08:22:32 +04:00
retval = joydev_ioctl_common ( joydev , cmd , argp ) ;
break ;
2005-05-29 11:26:31 +04:00
}
2007-08-30 08:22:32 +04:00
out :
mutex_unlock ( & joydev - > mutex ) ;
return retval ;
2005-05-29 11:26:31 +04:00
}
# endif /* CONFIG_COMPAT */
2007-08-30 08:22:32 +04:00
static long joydev_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
2005-05-29 11:26:31 +04:00
{
2007-04-12 09:30:00 +04:00
struct joydev_client * client = file - > private_data ;
struct joydev * joydev = client - > joydev ;
2005-05-29 11:26:31 +04:00
void __user * argp = ( void __user * ) arg ;
2007-08-30 08:22:32 +04:00
int retval ;
2005-05-29 11:26:31 +04:00
2007-08-30 08:22:32 +04:00
retval = mutex_lock_interruptible ( & joydev - > mutex ) ;
if ( retval )
return retval ;
2005-05-29 11:26:31 +04:00
2007-08-30 08:22:32 +04:00
if ( ! joydev - > exist ) {
retval = - ENODEV ;
goto out ;
2005-05-29 11:26:31 +04:00
}
2007-08-30 08:22:32 +04:00
switch ( cmd ) {
case JS_SET_TIMELIMIT :
retval = get_user ( joydev - > glue . JS_TIMELIMIT ,
( long __user * ) arg ) ;
break ;
case JS_GET_TIMELIMIT :
retval = put_user ( joydev - > glue . JS_TIMELIMIT ,
( long __user * ) arg ) ;
break ;
case JS_SET_ALL :
retval = copy_from_user ( & joydev - > glue , argp ,
sizeof ( joydev - > glue ) ) ? - EFAULT : 0 ;
break ;
case JS_GET_ALL :
retval = copy_to_user ( argp , & joydev - > glue ,
sizeof ( joydev - > glue ) ) ? - EFAULT : 0 ;
break ;
default :
retval = joydev_ioctl_common ( joydev , cmd , argp ) ;
break ;
}
out :
mutex_unlock ( & joydev - > mutex ) ;
return retval ;
2005-05-29 11:26:31 +04:00
}
2006-09-14 09:31:59 +04:00
static const struct file_operations joydev_fops = {
2007-08-30 08:22:32 +04:00
. owner = THIS_MODULE ,
. read = joydev_read ,
. poll = joydev_poll ,
. open = joydev_open ,
. release = joydev_release ,
. unlocked_ioctl = joydev_ioctl ,
2005-05-29 11:26:31 +04:00
# ifdef CONFIG_COMPAT
2007-08-30 08:22:32 +04:00
. compat_ioctl = joydev_compat_ioctl ,
2005-05-29 11:26:31 +04:00
# endif
2007-08-30 08:22:32 +04:00
. fasync = joydev_fasync ,
2005-04-17 02:20:36 +04:00
} ;
2007-08-30 08:22:32 +04:00
static int joydev_install_chrdev ( struct joydev * joydev )
{
joydev_table [ joydev - > minor ] = joydev ;
return 0 ;
}
static void joydev_remove_chrdev ( struct joydev * joydev )
{
mutex_lock ( & joydev_table_mutex ) ;
joydev_table [ joydev - > minor ] = NULL ;
mutex_unlock ( & joydev_table_mutex ) ;
}
/*
* Mark device non - existant . This disables writes , ioctls and
* prevents new users from opening the device . Already posted
* blocking reads will stay , however new ones will fail .
*/
static void joydev_mark_dead ( struct joydev * joydev )
{
mutex_lock ( & joydev - > mutex ) ;
joydev - > exist = 0 ;
mutex_unlock ( & joydev - > mutex ) ;
}
static void joydev_cleanup ( struct joydev * joydev )
{
struct input_handle * handle = & joydev - > handle ;
joydev_mark_dead ( joydev ) ;
joydev_hangup ( joydev ) ;
joydev_remove_chrdev ( joydev ) ;
/* joydev is marked dead so noone else accesses joydev->open */
if ( joydev - > open )
input_close_device ( handle ) ;
}
2010-02-03 08:08:26 +03:00
static bool joydev_match ( struct input_handler * handler , struct input_dev * dev )
{
/* Avoid touchpads and touchscreens */
if ( test_bit ( EV_KEY , dev - > evbit ) & & test_bit ( BTN_TOUCH , dev - > keybit ) )
return false ;
/* Avoid tablets, digitisers and similar devices */
if ( test_bit ( EV_KEY , dev - > evbit ) & & test_bit ( BTN_DIGI , dev - > keybit ) )
return false ;
return true ;
}
2007-04-12 09:29:46 +04:00
static int joydev_connect ( struct input_handler * handler , struct input_dev * dev ,
const struct input_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct joydev * joydev ;
int i , j , t , minor ;
2007-04-12 09:29:46 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
for ( minor = 0 ; minor < JOYDEV_MINORS ; minor + + )
if ( ! joydev_table [ minor ] )
break ;
2005-04-17 02:20:36 +04:00
if ( minor = = JOYDEV_MINORS ) {
printk ( KERN_ERR " joydev: no more free joydev devices \n " ) ;
2007-04-12 09:29:46 +04:00
return - ENFILE ;
2005-04-17 02:20:36 +04:00
}
2007-04-12 09:29:46 +04:00
joydev = kzalloc ( sizeof ( struct joydev ) , GFP_KERNEL ) ;
if ( ! joydev )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
INIT_LIST_HEAD ( & joydev - > client_list ) ;
2007-08-30 08:22:32 +04:00
spin_lock_init ( & joydev - > client_lock ) ;
mutex_init ( & joydev - > mutex ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & joydev - > wait ) ;
2009-05-10 03:08:04 +04:00
dev_set_name ( & joydev - > dev , " js%d " , minor ) ;
2007-08-30 08:22:32 +04:00
joydev - > exist = 1 ;
2005-04-17 02:20:36 +04:00
joydev - > minor = minor ;
2007-08-30 08:22:32 +04:00
2005-04-17 02:20:36 +04:00
joydev - > exist = 1 ;
2008-04-01 08:22:53 +04:00
joydev - > handle . dev = input_get_device ( dev ) ;
2009-05-10 03:08:04 +04:00
joydev - > handle . name = dev_name ( & joydev - > dev ) ;
2005-04-17 02:20:36 +04:00
joydev - > handle . handler = handler ;
joydev - > handle . private = joydev ;
2010-05-21 09:52:58 +04:00
for ( i = 0 ; i < ABS_CNT ; i + + )
2005-04-17 02:20:36 +04:00
if ( test_bit ( i , dev - > absbit ) ) {
joydev - > absmap [ i ] = joydev - > nabs ;
joydev - > abspam [ joydev - > nabs ] = i ;
joydev - > nabs + + ;
}
for ( i = BTN_JOYSTICK - BTN_MISC ; i < KEY_MAX - BTN_MISC + 1 ; i + + )
if ( test_bit ( i + BTN_MISC , dev - > keybit ) ) {
joydev - > keymap [ i ] = joydev - > nkey ;
joydev - > keypam [ joydev - > nkey ] = i + BTN_MISC ;
joydev - > nkey + + ;
}
2005-05-28 11:11:49 +04:00
for ( i = 0 ; i < BTN_JOYSTICK - BTN_MISC ; i + + )
2005-04-17 02:20:36 +04:00
if ( test_bit ( i + BTN_MISC , dev - > keybit ) ) {
joydev - > keymap [ i ] = joydev - > nkey ;
joydev - > keypam [ joydev - > nkey ] = i + BTN_MISC ;
joydev - > nkey + + ;
}
for ( i = 0 ; i < joydev - > nabs ; i + + ) {
j = joydev - > abspam [ i ] ;
if ( dev - > absmax [ j ] = = dev - > absmin [ j ] ) {
joydev - > corr [ i ] . type = JS_CORR_NONE ;
joydev - > abs [ i ] = dev - > abs [ j ] ;
continue ;
}
joydev - > corr [ i ] . type = JS_CORR_BROKEN ;
joydev - > corr [ i ] . prec = dev - > absfuzz [ j ] ;
2007-08-30 08:22:32 +04:00
joydev - > corr [ i ] . coef [ 0 ] =
( dev - > absmax [ j ] + dev - > absmin [ j ] ) / 2 - dev - > absflat [ j ] ;
joydev - > corr [ i ] . coef [ 1 ] =
( dev - > absmax [ j ] + dev - > absmin [ j ] ) / 2 + dev - > absflat [ j ] ;
t = ( dev - > absmax [ j ] - dev - > absmin [ j ] ) / 2 - 2 * dev - > absflat [ j ] ;
if ( t ) {
joydev - > corr [ i ] . coef [ 2 ] = ( 1 < < 29 ) / t ;
joydev - > corr [ i ] . coef [ 3 ] = ( 1 < < 29 ) / t ;
joydev - > abs [ i ] = joydev_correct ( dev - > abs [ j ] ,
joydev - > corr + i ) ;
}
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:32 +04:00
joydev - > dev . devt = MKDEV ( INPUT_MAJOR , JOYDEV_MINOR_BASE + minor ) ;
2007-06-15 07:32:24 +04:00
joydev - > dev . class = & input_class ;
joydev - > dev . parent = & dev - > dev ;
joydev - > dev . release = joydev_free ;
device_initialize ( & joydev - > dev ) ;
2007-04-12 09:29:46 +04:00
2007-08-30 08:22:32 +04:00
error = input_register_handle ( & joydev - > handle ) ;
2007-04-12 09:29:46 +04:00
if ( error )
2007-06-15 07:32:24 +04:00
goto err_free_joydev ;
2007-04-12 09:29:46 +04:00
2007-08-30 08:22:32 +04:00
error = joydev_install_chrdev ( joydev ) ;
2007-04-12 09:29:46 +04:00
if ( error )
2007-08-30 08:22:32 +04:00
goto err_unregister_handle ;
error = device_add ( & joydev - > dev ) ;
if ( error )
goto err_cleanup_joydev ;
2007-04-12 09:29:46 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:32 +04:00
err_cleanup_joydev :
joydev_cleanup ( joydev ) ;
err_unregister_handle :
input_unregister_handle ( & joydev - > handle ) ;
2007-04-12 09:29:46 +04:00
err_free_joydev :
2007-06-15 07:32:24 +04:00
put_device ( & joydev - > dev ) ;
2007-04-12 09:29:46 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
static void joydev_disconnect ( struct input_handle * handle )
{
struct joydev * joydev = handle - > private ;
2007-06-15 07:32:24 +04:00
device_del ( & joydev - > dev ) ;
2007-08-30 08:22:32 +04:00
joydev_cleanup ( joydev ) ;
input_unregister_handle ( handle ) ;
2007-06-15 07:32:24 +04:00
put_device ( & joydev - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-14 09:31:59 +04:00
static const struct input_device_id joydev_ids [ ] = {
2005-04-17 02:20:36 +04:00
{
2007-08-30 08:22:32 +04:00
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT ,
2007-10-19 10:40:32 +04:00
. evbit = { BIT_MASK ( EV_ABS ) } ,
. absbit = { BIT_MASK ( ABS_X ) } ,
2005-04-17 02:20:36 +04:00
} ,
{
2007-08-30 08:22:32 +04:00
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT ,
2007-10-19 10:40:32 +04:00
. evbit = { BIT_MASK ( EV_ABS ) } ,
. absbit = { BIT_MASK ( ABS_WHEEL ) } ,
2005-04-17 02:20:36 +04:00
} ,
{
2007-08-30 08:22:32 +04:00
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT ,
2007-10-19 10:40:32 +04:00
. evbit = { BIT_MASK ( EV_ABS ) } ,
. absbit = { BIT_MASK ( ABS_THROTTLE ) } ,
2005-04-17 02:20:36 +04:00
} ,
2010-04-25 08:41:05 +04:00
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT ,
. evbit = { BIT_MASK ( EV_KEY ) } ,
. keybit = { [ BIT_WORD ( BTN_JOYSTICK ) ] = BIT_MASK ( BTN_JOYSTICK ) } ,
} ,
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT ,
. evbit = { BIT_MASK ( EV_KEY ) } ,
. keybit = { [ BIT_WORD ( BTN_GAMEPAD ) ] = BIT_MASK ( BTN_GAMEPAD ) } ,
} ,
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT ,
. evbit = { BIT_MASK ( EV_KEY ) } ,
. keybit = { [ BIT_WORD ( BTN_TRIGGER_HAPPY ) ] = BIT_MASK ( BTN_TRIGGER_HAPPY ) } ,
} ,
2006-06-26 09:48:47 +04:00
{ } /* Terminating entry */
2005-04-17 02:20:36 +04:00
} ;
MODULE_DEVICE_TABLE ( input , joydev_ids ) ;
static struct input_handler joydev_handler = {
2007-08-30 08:22:32 +04:00
. event = joydev_event ,
2010-02-03 08:08:26 +03:00
. match = joydev_match ,
2007-08-30 08:22:32 +04:00
. connect = joydev_connect ,
. disconnect = joydev_disconnect ,
. fops = & joydev_fops ,
. minor = JOYDEV_MINOR_BASE ,
. name = " joydev " ,
. id_table = joydev_ids ,
2005-04-17 02:20:36 +04:00
} ;
static int __init joydev_init ( void )
{
2006-09-14 09:32:39 +04:00
return input_register_handler ( & joydev_handler ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit joydev_exit ( void )
{
input_unregister_handler ( & joydev_handler ) ;
}
module_init ( joydev_init ) ;
module_exit ( joydev_exit ) ;