2005-04-17 02:20:36 +04:00
/*
* Event char devices , giving access to raw input device events .
*
* Copyright ( c ) 1999 - 2002 Vojtech Pavlik
*
* 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 .
*/
# define EVDEV_MINOR_BASE 64
# define EVDEV_MINORS 32
# define EVDEV_BUFFER_SIZE 64
# include <linux/poll.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/major.h>
# include <linux/device.h>
2005-05-29 11:26:43 +04:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04:00
struct evdev {
int exist ;
int open ;
int minor ;
char name [ 16 ] ;
struct input_handle handle ;
wait_queue_head_t wait ;
2007-04-12 09:30:00 +04:00
struct evdev_client * grab ;
struct list_head client_list ;
2007-06-15 07:32:24 +04:00
struct device dev ;
2005-04-17 02:20:36 +04:00
} ;
2007-04-12 09:30:00 +04:00
struct evdev_client {
2005-04-17 02:20:36 +04:00
struct input_event buffer [ EVDEV_BUFFER_SIZE ] ;
int head ;
int tail ;
struct fasync_struct * fasync ;
struct evdev * evdev ;
struct list_head node ;
} ;
static struct evdev * evdev_table [ EVDEV_MINORS ] ;
static void evdev_event ( struct input_handle * handle , unsigned int type , unsigned int code , int value )
{
struct evdev * evdev = handle - > private ;
2007-04-12 09:30:00 +04:00
struct evdev_client * client ;
2005-04-17 02:20:36 +04:00
if ( evdev - > grab ) {
2007-04-12 09:30:00 +04:00
client = evdev - > grab ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
do_gettimeofday ( & client - > buffer [ client - > head ] . time ) ;
client - > buffer [ client - > head ] . type = type ;
client - > buffer [ client - > head ] . code = code ;
client - > buffer [ client - > head ] . value = value ;
client - > head = ( client - > head + 1 ) & ( EVDEV_BUFFER_SIZE - 1 ) ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
kill_fasync ( & client - > fasync , SIGIO , POLL_IN ) ;
2005-04-17 02:20:36 +04:00
} else
2007-04-12 09:30:00 +04:00
list_for_each_entry ( client , & evdev - > client_list , node ) {
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
do_gettimeofday ( & client - > buffer [ client - > head ] . time ) ;
client - > buffer [ client - > head ] . type = type ;
client - > buffer [ client - > head ] . code = code ;
client - > buffer [ client - > head ] . value = value ;
client - > head = ( client - > head + 1 ) & ( EVDEV_BUFFER_SIZE - 1 ) ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
kill_fasync ( & client - > fasync , SIGIO , POLL_IN ) ;
2005-04-17 02:20:36 +04:00
}
wake_up_interruptible ( & evdev - > wait ) ;
}
static int evdev_fasync ( int fd , struct file * file , int on )
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
2005-04-17 02:20:36 +04:00
int retval ;
2006-06-26 09:48:47 +04:00
2007-04-12 09:30:00 +04:00
retval = fasync_helper ( fd , file , on , & client - > fasync ) ;
2006-06-26 09:48:47 +04:00
2005-04-17 02:20:36 +04:00
return retval < 0 ? retval : 0 ;
}
2006-06-26 09:48:47 +04:00
static int evdev_flush ( struct file * file , fl_owner_t id )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2006-06-26 09:48:47 +04:00
2007-04-12 09:30:00 +04:00
if ( ! evdev - > exist )
2006-06-26 09:48:47 +04:00
return - ENODEV ;
2007-04-12 09:30:00 +04:00
return input_flush_device ( & evdev - > handle , file ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-15 07:32:24 +04:00
static void evdev_free ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2007-06-15 07:32:24 +04:00
struct evdev * evdev = container_of ( dev , struct evdev , dev ) ;
2005-04-17 02:20:36 +04:00
evdev_table [ evdev - > minor ] = NULL ;
kfree ( evdev ) ;
}
2007-04-12 09:30:00 +04:00
static int evdev_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
if ( evdev - > grab = = client ) {
input_release_device ( & evdev - > handle ) ;
evdev - > grab = NULL ;
2005-04-17 02:20:36 +04:00
}
evdev_fasync ( - 1 , file , 0 ) ;
2007-04-12 09:30:00 +04:00
list_del ( & client - > node ) ;
kfree ( client ) ;
2005-04-17 02:20:36 +04:00
2007-06-15 07:32:24 +04:00
if ( ! - - evdev - > open & & evdev - > exist )
input_close_device ( & evdev - > handle ) ;
put_device ( & evdev - > dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-12 09:30:00 +04:00
static int evdev_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client ;
struct evdev * evdev ;
2005-04-17 02:20:36 +04:00
int i = iminor ( inode ) - EVDEV_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 > = EVDEV_MINORS )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-04-12 09:30:00 +04:00
evdev = evdev_table [ i ] ;
if ( ! evdev | | ! evdev - > exist )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-06-15 07:32:24 +04:00
get_device ( & evdev - > dev ) ;
2007-04-12 09:30:00 +04:00
client = kzalloc ( sizeof ( struct evdev_client ) , GFP_KERNEL ) ;
2007-06-15 07:32:24 +04:00
if ( ! client ) {
error = - ENOMEM ;
goto err_put_evdev ;
}
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
client - > evdev = evdev ;
list_add_tail ( & client - > node , & evdev - > client_list ) ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:15 +04:00
if ( ! evdev - > open + + & & evdev - > exist ) {
error = input_open_device ( & evdev - > handle ) ;
2007-06-15 07:32:24 +04:00
if ( error )
goto err_free_client ;
2007-04-12 09:30:15 +04:00
}
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
file - > private_data = client ;
2005-04-17 02:20:36 +04:00
return 0 ;
2007-06-15 07:32:24 +04:00
err_free_client :
list_del ( & client - > node ) ;
kfree ( client ) ;
err_put_evdev :
put_device ( & evdev - > dev ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2005-05-29 11:26:43 +04:00
# ifdef CONFIG_COMPAT
2005-12-11 20:40:37 +03:00
2005-05-29 11:26:43 +04:00
struct input_event_compat {
struct compat_timeval time ;
__u16 type ;
__u16 code ;
__s32 value ;
} ;
2006-01-12 00:44:06 +03:00
/* Note to the author of this code: did it ever occur to
you why the ifdefs are needed ? Think about it again . - AK */
2005-05-29 11:26:43 +04:00
# ifdef CONFIG_X86_64
2006-01-12 00:44:06 +03:00
# define COMPAT_TEST is_compat_task()
2005-05-29 11:26:43 +04:00
# elif defined(CONFIG_IA64)
2006-01-12 12:06:06 +03:00
# define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
2006-01-06 11:19:28 +03:00
# elif defined(CONFIG_S390)
2005-05-29 11:26:43 +04:00
# define COMPAT_TEST test_thread_flag(TIF_31BIT)
2005-09-04 02:56:23 +04:00
# elif defined(CONFIG_MIPS)
# define COMPAT_TEST (current->thread.mflags & MF_32BIT_ADDR)
2005-05-29 11:26:43 +04:00
# else
# define COMPAT_TEST test_thread_flag(TIF_32BIT)
# endif
2005-12-11 20:40:37 +03:00
static inline size_t evdev_event_size ( void )
2005-05-29 11:26:43 +04:00
{
2005-12-11 20:40:37 +03:00
return COMPAT_TEST ?
sizeof ( struct input_event_compat ) : sizeof ( struct input_event ) ;
}
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
static int evdev_event_from_user ( const char __user * buffer , struct input_event * event )
{
if ( COMPAT_TEST ) {
struct input_event_compat compat_event ;
if ( copy_from_user ( & compat_event , buffer , sizeof ( struct input_event_compat ) ) )
return - EFAULT ;
event - > time . tv_sec = compat_event . time . tv_sec ;
event - > time . tv_usec = compat_event . time . tv_usec ;
event - > type = compat_event . type ;
event - > code = compat_event . code ;
event - > value = compat_event . value ;
} else {
if ( copy_from_user ( event , buffer , sizeof ( struct input_event ) ) )
2005-05-29 11:26:43 +04:00
return - EFAULT ;
}
2005-12-11 20:40:37 +03:00
return 0 ;
2005-05-29 11:26:43 +04:00
}
2005-12-11 20:40:37 +03:00
static int evdev_event_to_user ( char __user * buffer , const struct input_event * event )
2005-04-17 02:20:36 +04:00
{
2005-12-11 20:40:37 +03:00
if ( COMPAT_TEST ) {
struct input_event_compat compat_event ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
compat_event . time . tv_sec = event - > time . tv_sec ;
compat_event . time . tv_usec = event - > time . tv_usec ;
compat_event . type = event - > type ;
compat_event . code = event - > code ;
compat_event . value = event - > value ;
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
if ( copy_to_user ( buffer , & compat_event , sizeof ( struct input_event_compat ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
} else {
if ( copy_to_user ( buffer , event , sizeof ( struct input_event ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
2005-12-11 20:40:37 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-12-11 20:40:37 +03:00
# else
static inline size_t evdev_event_size ( void )
2005-05-29 11:26:43 +04:00
{
2005-12-11 20:40:37 +03:00
return sizeof ( struct input_event ) ;
}
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
static int evdev_event_from_user ( const char __user * buffer , struct input_event * event )
{
if ( copy_from_user ( event , buffer , sizeof ( struct input_event ) ) )
return - EFAULT ;
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
return 0 ;
}
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
static int evdev_event_to_user ( char __user * buffer , const struct input_event * event )
{
if ( copy_to_user ( buffer , event , sizeof ( struct input_event ) ) )
return - EFAULT ;
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
return 0 ;
}
# endif /* CONFIG_COMPAT */
2007-04-12 09:30:00 +04:00
static ssize_t evdev_write ( struct file * file , const char __user * buffer , size_t count , loff_t * ppos )
2005-12-11 20:40:37 +03:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2005-12-11 20:40:37 +03:00
struct input_event event ;
int retval = 0 ;
2005-05-29 11:26:43 +04:00
2007-04-12 09:30:00 +04:00
if ( ! evdev - > exist )
2005-05-29 11:26:43 +04:00
return - ENODEV ;
2005-12-11 20:40:37 +03:00
while ( retval < count ) {
if ( evdev_event_from_user ( buffer + retval , & event ) )
return - EFAULT ;
2007-04-12 09:30:00 +04:00
input_inject_event ( & evdev - > handle , event . type , event . code , event . value ) ;
2005-12-11 20:40:37 +03:00
retval + = evdev_event_size ( ) ;
2005-05-29 11:26:43 +04:00
}
return retval ;
}
2007-04-12 09:30:00 +04:00
static ssize_t evdev_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2005-04-17 02:20:36 +04:00
int retval ;
2005-12-11 20:40:37 +03:00
if ( count < evdev_event_size ( ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-04-12 09:30:00 +04:00
if ( client - > head = = client - > tail & & evdev - > exist & & ( file - > f_flags & O_NONBLOCK ) )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
2007-04-12 09:30:00 +04:00
retval = wait_event_interruptible ( evdev - > wait ,
client - > head ! = client - > tail | | ! evdev - > exist ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
return retval ;
2007-04-12 09:30:00 +04:00
if ( ! evdev - > exist )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-04-12 09:30:00 +04:00
while ( client - > head ! = client - > tail & & retval + evdev_event_size ( ) < = count ) {
2005-12-11 20:40:37 +03:00
2007-04-12 09:30:00 +04:00
struct input_event * event = ( struct input_event * ) client - > buffer + client - > tail ;
2005-12-11 20:40:37 +03:00
if ( evdev_event_to_user ( buffer + retval , event ) )
return - EFAULT ;
2007-04-12 09:30:00 +04:00
client - > tail = ( client - > tail + 1 ) & ( EVDEV_BUFFER_SIZE - 1 ) ;
2005-12-11 20:40:37 +03:00
retval + = evdev_event_size ( ) ;
2005-04-17 02:20:36 +04:00
}
return retval ;
}
/* No kernel lock - fine */
static unsigned int evdev_poll ( struct file * file , poll_table * wait )
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2006-06-26 09:48:47 +04:00
2007-04-12 09:30:00 +04:00
poll_wait ( file , & evdev - > wait , wait ) ;
return ( ( client - > head = = client - > tail ) ? 0 : ( POLLIN | POLLRDNORM ) ) |
( evdev - > exist ? 0 : ( POLLHUP | POLLERR ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-12-11 20:40:37 +03:00
# ifdef CONFIG_COMPAT
# define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
# define NBITS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
# ifdef __BIG_ENDIAN
static int bits_to_user ( unsigned long * bits , unsigned int maxbit ,
unsigned int maxlen , void __user * p , int compat )
{
int len , i ;
if ( compat ) {
len = NBITS_COMPAT ( maxbit ) * sizeof ( compat_long_t ) ;
2007-05-11 09:12:15 +04:00
if ( len > maxlen )
2005-12-11 20:40:37 +03:00
len = maxlen ;
for ( i = 0 ; i < len / sizeof ( compat_long_t ) ; i + + )
if ( copy_to_user ( ( compat_long_t __user * ) p + i ,
( compat_long_t * ) bits +
i + 1 - ( ( i % 2 ) < < 1 ) ,
sizeof ( compat_long_t ) ) )
return - EFAULT ;
} else {
len = NBITS ( maxbit ) * sizeof ( long ) ;
if ( len > maxlen )
len = maxlen ;
if ( copy_to_user ( p , bits , len ) )
return - EFAULT ;
}
return len ;
}
# else
static int bits_to_user ( unsigned long * bits , unsigned int maxbit ,
unsigned int maxlen , void __user * p , int compat )
{
int len = compat ?
NBITS_COMPAT ( maxbit ) * sizeof ( compat_long_t ) :
NBITS ( maxbit ) * sizeof ( long ) ;
if ( len > maxlen )
len = maxlen ;
return copy_to_user ( p , bits , len ) ? - EFAULT : len ;
}
# endif /* __BIG_ENDIAN */
# else
static int bits_to_user ( unsigned long * bits , unsigned int maxbit ,
unsigned int maxlen , void __user * p , int compat )
{
int len = NBITS ( maxbit ) * sizeof ( long ) ;
if ( len > maxlen )
len = maxlen ;
return copy_to_user ( p , bits , len ) ? - EFAULT : len ;
}
# endif /* CONFIG_COMPAT */
static int str_to_user ( const char * str , unsigned int maxlen , void __user * p )
{
int len ;
if ( ! str )
return - ENOENT ;
len = strlen ( str ) + 1 ;
if ( len > maxlen )
len = maxlen ;
return copy_to_user ( p , str , len ) ? - EFAULT : len ;
}
static long evdev_ioctl_handler ( struct file * file , unsigned int cmd ,
void __user * p , int compat_mode )
2005-04-17 02:20:36 +04:00
{
2007-04-12 09:30:00 +04:00
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
2005-04-17 02:20:36 +04:00
struct input_dev * dev = evdev - > handle . dev ;
struct input_absinfo abs ;
2006-07-19 09:40:22 +04:00
struct ff_effect effect ;
2005-12-11 20:40:37 +03:00
int __user * ip = ( int __user * ) p ;
2005-04-17 02:20:36 +04:00
int i , t , u , v ;
2006-07-19 09:40:22 +04:00
int error ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( ! evdev - > exist )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case EVIOCGVERSION :
return put_user ( EV_VERSION , ip ) ;
case EVIOCGID :
2005-12-11 20:40:37 +03:00
if ( copy_to_user ( p , & dev - > id , sizeof ( struct input_id ) ) )
return - EFAULT ;
2006-04-29 09:13:21 +04:00
return 0 ;
case EVIOCGREP :
if ( ! test_bit ( EV_REP , dev - > evbit ) )
return - ENOSYS ;
if ( put_user ( dev - > rep [ REP_DELAY ] , ip ) )
return - EFAULT ;
if ( put_user ( dev - > rep [ REP_PERIOD ] , ip + 1 ) )
return - EFAULT ;
return 0 ;
case EVIOCSREP :
if ( ! test_bit ( EV_REP , dev - > evbit ) )
return - ENOSYS ;
if ( get_user ( u , ip ) )
return - EFAULT ;
if ( get_user ( v , ip + 1 ) )
return - EFAULT ;
2006-07-06 08:22:43 +04:00
input_inject_event ( & evdev - > handle , EV_REP , REP_DELAY , u ) ;
input_inject_event ( & evdev - > handle , EV_REP , REP_PERIOD , v ) ;
2005-12-11 20:40:37 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
case EVIOCGKEYCODE :
2005-12-11 20:40:37 +03:00
if ( get_user ( t , ip ) )
return - EFAULT ;
2007-03-15 05:50:42 +03:00
error = dev - > getkeycode ( dev , t , & v ) ;
if ( error )
return error ;
if ( put_user ( v , ip + 1 ) )
2005-12-11 20:40:37 +03:00
return - EFAULT ;
2007-03-15 05:50:42 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
case EVIOCSKEYCODE :
2007-03-15 05:50:42 +03:00
if ( get_user ( t , ip ) | | get_user ( v , ip + 1 ) )
2005-12-11 20:40:37 +03:00
return - EFAULT ;
2007-03-15 05:50:42 +03:00
return dev - > setkeycode ( dev , t , v ) ;
2005-04-17 02:20:36 +04:00
case EVIOCSFF :
2006-07-19 09:40:22 +04:00
if ( copy_from_user ( & effect , p , sizeof ( effect ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-07-19 09:40:22 +04:00
error = input_ff_upload ( dev , & effect , file ) ;
2005-12-11 20:40:37 +03:00
2006-07-19 09:40:22 +04:00
if ( put_user ( effect . id , & ( ( ( struct ff_effect __user * ) p ) - > id ) ) )
return - EFAULT ;
return error ;
case EVIOCRMFF :
return input_ff_erase ( dev , ( int ) ( unsigned long ) p , file ) ;
2005-04-17 02:20:36 +04:00
case EVIOCGEFFECTS :
2006-07-19 09:40:22 +04:00
i = test_bit ( EV_FF , dev - > evbit ) ? dev - > ff - > max_effects : 0 ;
if ( put_user ( i , ip ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
case EVIOCGRAB :
2005-12-11 20:40:37 +03:00
if ( p ) {
2005-04-17 02:20:36 +04:00
if ( evdev - > grab )
return - EBUSY ;
if ( input_grab_device ( & evdev - > handle ) )
return - EBUSY ;
2007-04-12 09:30:00 +04:00
evdev - > grab = client ;
2005-04-17 02:20:36 +04:00
return 0 ;
} else {
2007-04-12 09:30:00 +04:00
if ( evdev - > grab ! = client )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
input_release_device ( & evdev - > handle ) ;
evdev - > grab = NULL ;
return 0 ;
}
default :
2005-05-29 11:30:15 +04:00
if ( _IOC_TYPE ( cmd ) ! = ' E ' )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-05-29 11:30:15 +04:00
if ( _IOC_DIR ( cmd ) = = _IOC_READ ) {
if ( ( _IOC_NR ( cmd ) & ~ EV_MAX ) = = _IOC_NR ( EVIOCGBIT ( 0 , 0 ) ) ) {
2007-05-03 08:53:18 +04:00
unsigned long * bits ;
2005-05-29 11:30:15 +04:00
int len ;
switch ( _IOC_NR ( cmd ) & EV_MAX ) {
case 0 : bits = dev - > evbit ; len = EV_MAX ; break ;
case EV_KEY : bits = dev - > keybit ; len = KEY_MAX ; break ;
case EV_REL : bits = dev - > relbit ; len = REL_MAX ; break ;
case EV_ABS : bits = dev - > absbit ; len = ABS_MAX ; break ;
case EV_MSC : bits = dev - > mscbit ; len = MSC_MAX ; break ;
case EV_LED : bits = dev - > ledbit ; len = LED_MAX ; break ;
case EV_SND : bits = dev - > sndbit ; len = SND_MAX ; break ;
case EV_FF : bits = dev - > ffbit ; len = FF_MAX ; break ;
2005-09-07 02:19:06 +04:00
case EV_SW : bits = dev - > swbit ; len = SW_MAX ; break ;
2005-05-29 11:30:15 +04:00
default : return - EINVAL ;
}
2005-12-11 20:40:37 +03:00
return bits_to_user ( bits , len , _IOC_SIZE ( cmd ) , p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
}
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGKEY ( 0 ) ) )
return bits_to_user ( dev - > key , KEY_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGLED ( 0 ) ) )
return bits_to_user ( dev - > led , LED_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGSND ( 0 ) ) )
return bits_to_user ( dev - > snd , SND_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGSW ( 0 ) ) )
return bits_to_user ( dev - > sw , SW_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-09-07 02:19:06 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGNAME ( 0 ) ) )
return str_to_user ( dev - > name , _IOC_SIZE ( cmd ) , p ) ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGPHYS ( 0 ) ) )
return str_to_user ( dev - > phys , _IOC_SIZE ( cmd ) , p ) ;
2005-04-17 02:20:36 +04:00
2005-12-11 20:40:37 +03:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGUNIQ ( 0 ) ) )
return str_to_user ( dev - > uniq , _IOC_SIZE ( cmd ) , p ) ;
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
if ( ( _IOC_NR ( cmd ) & ~ ABS_MAX ) = = _IOC_NR ( EVIOCGABS ( 0 ) ) ) {
2005-04-17 02:20:36 +04:00
2007-05-03 08:53:18 +04:00
t = _IOC_NR ( cmd ) & ABS_MAX ;
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
abs . value = dev - > abs [ t ] ;
abs . minimum = dev - > absmin [ t ] ;
abs . maximum = dev - > absmax [ t ] ;
abs . fuzz = dev - > absfuzz [ t ] ;
abs . flat = dev - > absflat [ t ] ;
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
if ( copy_to_user ( p , & abs , sizeof ( struct input_absinfo ) ) )
return - EFAULT ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
2005-05-29 11:30:15 +04:00
if ( _IOC_DIR ( cmd ) = = _IOC_WRITE ) {
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
if ( ( _IOC_NR ( cmd ) & ~ ABS_MAX ) = = _IOC_NR ( EVIOCSABS ( 0 ) ) ) {
2005-04-17 02:20:36 +04:00
2007-05-03 08:53:18 +04:00
t = _IOC_NR ( cmd ) & ABS_MAX ;
2005-05-29 11:30:15 +04:00
if ( copy_from_user ( & abs , p , sizeof ( struct input_absinfo ) ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
dev - > abs [ t ] = abs . value ;
dev - > absmin [ t ] = abs . minimum ;
dev - > absmax [ t ] = abs . maximum ;
dev - > absfuzz [ t ] = abs . fuzz ;
dev - > absflat [ t ] = abs . flat ;
2005-04-17 02:20:36 +04:00
2005-05-29 11:30:15 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
}
return - EINVAL ;
}
2005-12-11 20:40:37 +03:00
static long evdev_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
return evdev_ioctl_handler ( file , cmd , ( void __user * ) arg , 0 ) ;
}
2005-05-29 11:30:15 +04:00
2005-12-11 20:40:37 +03:00
# ifdef CONFIG_COMPAT
2005-05-29 11:26:43 +04:00
static long evdev_ioctl_compat ( struct file * file , unsigned int cmd , unsigned long arg )
{
2005-12-11 20:40:37 +03:00
return evdev_ioctl_handler ( file , cmd , compat_ptr ( arg ) , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-05-29 11:26:43 +04:00
# endif
2005-04-17 02:20:36 +04:00
2006-09-14 09:31:59 +04:00
static const struct file_operations evdev_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. read = evdev_read ,
. write = evdev_write ,
. poll = evdev_poll ,
. open = evdev_open ,
. release = evdev_release ,
2005-05-29 11:26:43 +04:00
. unlocked_ioctl = evdev_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = evdev_ioctl_compat ,
# endif
2005-04-17 02:20:36 +04:00
. fasync = evdev_fasync ,
. flush = evdev_flush
} ;
2007-04-12 09:29:46 +04:00
static int evdev_connect ( struct input_handler * handler , struct input_dev * dev ,
const struct input_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct evdev * evdev ;
int minor ;
2007-04-12 09:29:46 +04:00
int error ;
2005-04-17 02:20:36 +04:00
for ( minor = 0 ; minor < EVDEV_MINORS & & evdev_table [ minor ] ; minor + + ) ;
if ( minor = = EVDEV_MINORS ) {
printk ( KERN_ERR " evdev: no more free evdev 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
evdev = kzalloc ( sizeof ( struct evdev ) , GFP_KERNEL ) ;
if ( ! evdev )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:30:00 +04:00
INIT_LIST_HEAD ( & evdev - > client_list ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & evdev - > wait ) ;
evdev - > exist = 1 ;
evdev - > minor = minor ;
evdev - > handle . dev = dev ;
evdev - > handle . name = evdev - > name ;
evdev - > handle . handler = handler ;
evdev - > handle . private = evdev ;
2007-06-15 07:32:24 +04:00
snprintf ( evdev - > name , sizeof ( evdev - > name ) , " event%d " , minor ) ;
2005-04-17 02:20:36 +04:00
2007-06-15 07:32:24 +04:00
snprintf ( evdev - > dev . bus_id , sizeof ( evdev - > dev . bus_id ) ,
" event%d " , minor ) ;
evdev - > dev . class = & input_class ;
evdev - > dev . parent = & dev - > dev ;
evdev - > dev . devt = MKDEV ( INPUT_MAJOR , EVDEV_MINOR_BASE + minor ) ;
evdev - > dev . release = evdev_free ;
device_initialize ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
2007-06-15 07:32:24 +04:00
evdev_table [ minor ] = evdev ;
2005-10-28 09:25:43 +04:00
2007-06-15 07:32:24 +04:00
error = device_add ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
if ( error )
2007-06-15 07:32:24 +04:00
goto err_free_evdev ;
2007-04-12 09:29:46 +04:00
error = input_register_handle ( & evdev - > handle ) ;
if ( error )
2007-06-15 07:32:24 +04:00
goto err_delete_evdev ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:29:46 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-06-15 07:32:24 +04:00
err_delete_evdev :
device_del ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
err_free_evdev :
2007-06-15 07:32:24 +04:00
put_device ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
static void evdev_disconnect ( struct input_handle * handle )
{
struct evdev * evdev = handle - > private ;
2007-04-12 09:30:00 +04:00
struct evdev_client * client ;
2005-04-17 02:20:36 +04:00
2007-04-12 09:29:46 +04:00
input_unregister_handle ( handle ) ;
2007-06-15 07:32:24 +04:00
device_del ( & evdev - > dev ) ;
2005-04-17 02:20:36 +04:00
evdev - > exist = 0 ;
if ( evdev - > open ) {
2006-07-19 09:40:22 +04:00
input_flush_device ( handle , NULL ) ;
2005-04-17 02:20:36 +04:00
input_close_device ( handle ) ;
2007-04-12 09:30:00 +04:00
list_for_each_entry ( client , & evdev - > client_list , node )
kill_fasync ( & client - > fasync , SIGIO , POLL_HUP ) ;
2007-06-04 07:29:36 +04:00
wake_up_interruptible ( & evdev - > wait ) ;
2007-06-15 07:32:24 +04:00
}
put_device ( & evdev - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-14 09:31:59 +04:00
static const struct input_device_id evdev_ids [ ] = {
2005-04-17 02:20:36 +04:00
{ . driver_info = 1 } , /* Matches all devices */
{ } , /* Terminating zero entry */
} ;
MODULE_DEVICE_TABLE ( input , evdev_ids ) ;
static struct input_handler evdev_handler = {
. event = evdev_event ,
. connect = evdev_connect ,
. disconnect = evdev_disconnect ,
. fops = & evdev_fops ,
. minor = EVDEV_MINOR_BASE ,
. name = " evdev " ,
. id_table = evdev_ids ,
} ;
static int __init evdev_init ( void )
{
2006-09-14 09:32:39 +04:00
return input_register_handler ( & evdev_handler ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit evdev_exit ( void )
{
input_unregister_handler ( & evdev_handler ) ;
}
module_init ( evdev_init ) ;
module_exit ( evdev_exit ) ;
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Input driver event char devices " ) ;
MODULE_LICENSE ( " GPL " ) ;