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>
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/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/major.h>
# include <linux/device.h>
2008-10-17 06:31:42 +04:00
# include "input-compat.h"
2005-04-17 02:20:36 +04:00
struct evdev {
int exist ;
int open ;
int minor ;
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-08-30 08:22:18 +04:00
spinlock_t client_lock ; /* protects client_list */
struct mutex mutex ;
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 ;
2007-08-30 08:22:18 +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 evdev * evdev ;
struct list_head node ;
} ;
static struct evdev * evdev_table [ EVDEV_MINORS ] ;
2007-08-30 08:22:18 +04:00
static DEFINE_MUTEX ( evdev_table_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
static void evdev_pass_event ( struct evdev_client * client ,
struct input_event * event )
{
/*
* Interrupts are disabled , just acquire the lock
*/
spin_lock ( & client - > buffer_lock ) ;
client - > buffer [ client - > head + + ] = * event ;
client - > head & = EVDEV_BUFFER_SIZE - 1 ;
spin_unlock ( & client - > buffer_lock ) ;
2010-01-06 04:56:04 +03:00
if ( event - > type = = EV_SYN )
kill_fasync ( & client - > fasync , SIGIO , POLL_IN ) ;
2007-08-30 08:22:18 +04:00
}
/*
2007-10-13 23:46:55 +04:00
* Pass incoming event to all connected clients .
2007-08-30 08:22:18 +04:00
*/
static void evdev_event ( struct input_handle * handle ,
unsigned int type , unsigned int code , int value )
2005-04-17 02:20:36 +04:00
{
struct evdev * evdev = handle - > private ;
2007-04-12 09:30:00 +04:00
struct evdev_client * client ;
2007-08-30 08:22:18 +04:00
struct input_event event ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
do_gettimeofday ( & event . time ) ;
event . type = type ;
event . code = code ;
event . value = value ;
2005-04-17 02:20:36 +04:00
2007-10-13 23:46:55 +04:00
rcu_read_lock ( ) ;
2007-08-30 08:22:18 +04:00
client = rcu_dereference ( evdev - > grab ) ;
if ( client )
evdev_pass_event ( client , & event ) ;
else
list_for_each_entry_rcu ( client , & evdev - > client_list , node )
evdev_pass_event ( client , & event ) ;
2005-04-17 02:20:36 +04:00
2007-10-13 23:46:55 +04:00
rcu_read_unlock ( ) ;
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 ;
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
}
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 ;
2007-08-30 08:22:18 +04:00
int retval ;
retval = mutex_lock_interruptible ( & evdev - > mutex ) ;
if ( retval )
return retval ;
2006-06-26 09:48:47 +04:00
2007-04-12 09:30:00 +04:00
if ( ! evdev - > exist )
2007-08-30 08:22:18 +04:00
retval = - ENODEV ;
else
retval = input_flush_device ( & evdev - > handle , file ) ;
2006-06-26 09:48:47 +04:00
2007-08-30 08:22:18 +04:00
mutex_unlock ( & evdev - > mutex ) ;
return retval ;
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 ) ;
2008-04-01 08:22:53 +04:00
input_put_device ( evdev - > handle . dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( evdev ) ;
}
2007-08-30 08:22:18 +04:00
/*
* Grabs an event device ( along with underlying input device ) .
* This function is called with evdev - > mutex taken .
*/
static int evdev_grab ( struct evdev * evdev , struct evdev_client * client )
{
int error ;
if ( evdev - > grab )
return - EBUSY ;
error = input_grab_device ( & evdev - > handle ) ;
if ( error )
return error ;
rcu_assign_pointer ( evdev - > grab , client ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:18 +04:00
return 0 ;
}
static int evdev_ungrab ( struct evdev * evdev , struct evdev_client * client )
{
if ( evdev - > grab ! = client )
return - EINVAL ;
rcu_assign_pointer ( evdev - > grab , NULL ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:18 +04:00
input_release_device ( & evdev - > handle ) ;
return 0 ;
}
static void evdev_attach_client ( struct evdev * evdev ,
struct evdev_client * client )
{
spin_lock ( & evdev - > client_lock ) ;
list_add_tail_rcu ( & client - > node , & evdev - > client_list ) ;
spin_unlock ( & evdev - > client_lock ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:18 +04:00
}
static void evdev_detach_client ( struct evdev * evdev ,
struct evdev_client * client )
{
spin_lock ( & evdev - > client_lock ) ;
list_del_rcu ( & client - > node ) ;
spin_unlock ( & evdev - > client_lock ) ;
2007-10-13 23:46:55 +04:00
synchronize_rcu ( ) ;
2007-08-30 08:22:18 +04:00
}
static int evdev_open_device ( struct evdev * evdev )
{
int retval ;
retval = mutex_lock_interruptible ( & evdev - > mutex ) ;
if ( retval )
return retval ;
if ( ! evdev - > exist )
retval = - ENODEV ;
2007-10-12 22:18:40 +04:00
else if ( ! evdev - > open + + ) {
2007-08-30 08:22:18 +04:00
retval = input_open_device ( & evdev - > handle ) ;
2007-10-12 22:18:40 +04:00
if ( retval )
evdev - > open - - ;
}
2007-08-30 08:22:18 +04:00
mutex_unlock ( & evdev - > mutex ) ;
return retval ;
}
static void evdev_close_device ( struct evdev * evdev )
{
mutex_lock ( & evdev - > mutex ) ;
if ( evdev - > exist & & ! - - evdev - > open )
input_close_device ( & evdev - > handle ) ;
mutex_unlock ( & evdev - > mutex ) ;
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device .
*/
static void evdev_hangup ( struct evdev * evdev )
{
struct evdev_client * client ;
spin_lock ( & evdev - > client_lock ) ;
list_for_each_entry ( client , & evdev - > client_list , node )
kill_fasync ( & client - > fasync , SIGIO , POLL_HUP ) ;
spin_unlock ( & evdev - > client_lock ) ;
wake_up_interruptible ( & evdev - > wait ) ;
}
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-08-30 08:22:18 +04:00
mutex_lock ( & evdev - > mutex ) ;
if ( evdev - > grab = = client )
evdev_ungrab ( evdev , client ) ;
mutex_unlock ( & evdev - > mutex ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
evdev_detach_client ( evdev , client ) ;
2007-04-12 09:30:00 +04:00
kfree ( client ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
evdev_close_device ( evdev ) ;
2007-06-15 07:32:24 +04:00
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 * evdev ;
2007-08-30 08:22:18 +04:00
struct evdev_client * client ;
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-08-30 08:22:18 +04:00
error = mutex_lock_interruptible ( & evdev_table_mutex ) ;
if ( error )
return error ;
2007-04-12 09:30:00 +04:00
evdev = evdev_table [ i ] ;
2007-08-30 08:22:18 +04:00
if ( evdev )
get_device ( & evdev - > dev ) ;
mutex_unlock ( & evdev_table_mutex ) ;
2007-04-12 09:30:00 +04:00
2007-08-30 08:22:18 +04:00
if ( ! evdev )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
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-08-30 08:22:18 +04:00
spin_lock_init ( & client - > buffer_lock ) ;
2007-04-12 09:30:00 +04:00
client - > evdev = evdev ;
2007-08-30 08:22:18 +04:00
evdev_attach_client ( evdev , client ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
error = evdev_open_device ( evdev ) ;
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:18 +04:00
evdev_detach_client ( evdev , client ) ;
2007-06-15 07:32:24 +04:00
kfree ( client ) ;
err_put_evdev :
put_device ( & evdev - > dev ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:18 +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 ;
2007-08-30 08:22:18 +04:00
int retval ;
2005-05-29 11:26:43 +04:00
2007-08-30 08:22:18 +04:00
retval = mutex_lock_interruptible ( & evdev - > mutex ) ;
if ( retval )
return retval ;
if ( ! evdev - > exist ) {
retval = - ENODEV ;
goto out ;
}
2005-05-29 11:26:43 +04:00
2005-12-11 20:40:37 +03:00
while ( retval < count ) {
2008-10-17 06:31:42 +04:00
if ( input_event_from_user ( buffer + retval , & event ) ) {
2007-08-30 08:22:18 +04:00
retval = - EFAULT ;
goto out ;
}
input_inject_event ( & evdev - > handle ,
event . type , event . code , event . value ) ;
2008-10-17 06:31:42 +04:00
retval + = input_event_size ( ) ;
2005-05-29 11:26:43 +04:00
}
2007-08-30 08:22:18 +04:00
out :
mutex_unlock ( & evdev - > mutex ) ;
2005-05-29 11:26:43 +04:00
return retval ;
}
2007-08-30 08:22:18 +04:00
static int evdev_fetch_next_event ( struct evdev_client * client ,
struct input_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 & = EVDEV_BUFFER_SIZE - 1 ;
}
spin_unlock_irq ( & client - > buffer_lock ) ;
return have_event ;
}
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 ;
2007-08-30 08:22:18 +04:00
struct input_event event ;
2005-04-17 02:20:36 +04:00
int retval ;
2008-10-17 06:31:42 +04:00
if ( count < input_event_size ( ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-08-30 08:22:18 +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 ;
2008-10-17 06:31:42 +04:00
while ( retval + input_event_size ( ) < = count & &
2007-08-30 08:22:18 +04:00
evdev_fetch_next_event ( client , & event ) ) {
2005-12-11 20:40:37 +03:00
2008-10-17 06:31:42 +04:00
if ( input_event_to_user ( buffer + retval , & event ) )
2005-12-11 20:40:37 +03:00
return - EFAULT ;
2008-10-17 06:31:42 +04:00
retval + = input_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)
2007-10-19 10:40:32 +04:00
# define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
2005-12-11 20:40:37 +03:00
# 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 ) {
2007-10-19 10:40:32 +04:00
len = BITS_TO_LONGS_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 {
2007-10-19 10:40:32 +04:00
len = BITS_TO_LONGS ( maxbit ) * sizeof ( long ) ;
2005-12-11 20:40:37 +03:00
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 ?
2007-10-19 10:40:32 +04:00
BITS_TO_LONGS_COMPAT ( maxbit ) * sizeof ( compat_long_t ) :
BITS_TO_LONGS ( maxbit ) * sizeof ( long ) ;
2005-12-11 20:40:37 +03:00
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 )
{
2007-10-19 10:40:32 +04:00
int len = BITS_TO_LONGS ( maxbit ) * sizeof ( long ) ;
2005-12-11 20:40:37 +03:00
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 ;
}
2008-08-08 19:46:53 +04:00
# define OLD_KEY_MAX 0x1ff
2008-08-05 19:42:42 +04:00
static int handle_eviocgbit ( struct input_dev * dev , unsigned int cmd , void __user * p , int compat_mode )
{
2008-08-08 19:46:53 +04:00
static unsigned long keymax_warn_time ;
2008-08-05 19:42:42 +04:00
unsigned long * bits ;
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 ;
case EV_SW : bits = dev - > swbit ; len = SW_MAX ; break ;
default : return - EINVAL ;
}
2008-08-08 19:46:53 +04:00
/*
* Work around bugs in userspace programs that like to do
* EVIOCGBIT ( EV_KEY , KEY_MAX ) and not realize that ' len '
* should be in bytes , not in bits .
*/
if ( ( _IOC_NR ( cmd ) & EV_MAX ) = = EV_KEY & & _IOC_SIZE ( cmd ) = = OLD_KEY_MAX ) {
len = OLD_KEY_MAX ;
if ( printk_timed_ratelimit ( & keymax_warn_time , 10 * 1000 ) )
printk ( KERN_WARNING
2008-08-19 19:28:23 +04:00
" evdev.c(EVIOCGBIT): Suspicious buffer size %u, "
" limiting output to %zu bytes. See "
2008-08-08 19:46:53 +04:00
" http://userweb.kernel.org/~dtor/eviocgbit-bug.html \n " ,
OLD_KEY_MAX ,
BITS_TO_LONGS ( OLD_KEY_MAX ) * sizeof ( long ) ) ;
}
2008-08-05 19:42:42 +04:00
return bits_to_user ( bits , len , _IOC_SIZE ( cmd ) , p , compat_mode ) ;
}
2008-08-08 19:46:53 +04:00
# undef OLD_KEY_MAX
2008-08-05 19:42:42 +04:00
2007-08-30 08:22:18 +04:00
static long evdev_do_ioctl ( 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
switch ( cmd ) {
2007-08-30 08:22:18 +04:00
case EVIOCGVERSION :
return put_user ( EV_VERSION , ip ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
case EVIOCGID :
if ( copy_to_user ( p , & dev - > id , sizeof ( struct input_id ) ) )
return - EFAULT ;
return 0 ;
2006-04-29 09:13:21 +04:00
2007-08-30 08:22:18 +04:00
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 ;
2006-04-29 09:13:21 +04:00
2007-08-30 08:22:18 +04:00
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-04-29 09:13:21 +04:00
2007-08-30 08:22:18 +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
2007-08-30 08:22:18 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
case EVIOCGKEYCODE :
if ( get_user ( t , ip ) )
return - EFAULT ;
2007-03-15 05:50:42 +03:00
2007-11-04 07:41:12 +03:00
error = input_get_keycode ( dev , t , & v ) ;
2007-08-30 08:22:18 +04:00
if ( error )
return error ;
2007-03-15 05:50:42 +03:00
2007-08-30 08:22:18 +04:00
if ( put_user ( v , ip + 1 ) )
return - EFAULT ;
2007-03-15 05:50:42 +03:00
2007-08-30 08:22:18 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
case EVIOCSKEYCODE :
if ( get_user ( t , ip ) | | get_user ( v , ip + 1 ) )
return - EFAULT ;
2005-12-11 20:40:37 +03:00
2007-11-04 07:41:12 +03:00
return input_set_keycode ( dev , t , v ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
case EVIOCRMFF :
return input_ff_erase ( dev , ( int ) ( unsigned long ) p , file ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
case EVIOCGEFFECTS :
i = test_bit ( EV_FF , dev - > evbit ) ?
dev - > ff - > max_effects : 0 ;
if ( put_user ( i , ip ) )
return - EFAULT ;
return 0 ;
case EVIOCGRAB :
if ( p )
return evdev_grab ( evdev , client ) ;
else
return evdev_ungrab ( evdev , client ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
default :
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
if ( _IOC_TYPE ( cmd ) ! = ' E ' )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
if ( _IOC_DIR ( cmd ) = = _IOC_READ ) {
2005-05-29 11:30:15 +04:00
2008-08-05 19:42:42 +04:00
if ( ( _IOC_NR ( cmd ) & ~ EV_MAX ) = = _IOC_NR ( EVIOCGBIT ( 0 , 0 ) ) )
return handle_eviocgbit ( dev , cmd , p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04: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
2007-08-30 08:22:18 +04: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
2007-08-30 08:22:18 +04:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGSND ( 0 ) ) )
return bits_to_user ( dev - > snd , SND_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-09-07 02:19:06 +04:00
2007-08-30 08:22:18 +04:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGSW ( 0 ) ) )
return bits_to_user ( dev - > sw , SW_MAX , _IOC_SIZE ( cmd ) ,
p , compat_mode ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCGNAME ( 0 ) ) )
2009-07-14 09:22:49 +04:00
return str_to_user ( dev - > name , _IOC_SIZE ( cmd ) , p ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04: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
2007-08-30 08:22:18 +04: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
2007-08-30 08:22:18 +04:00
if ( ( _IOC_NR ( cmd ) & ~ ABS_MAX ) = = _IOC_NR ( EVIOCGABS ( 0 ) ) ) {
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
t = _IOC_NR ( cmd ) & ABS_MAX ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +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 ] ;
2009-06-11 10:27:24 +04:00
abs . resolution = dev - > absres [ t ] ;
2005-05-29 11:30:15 +04:00
2009-06-11 10:27:24 +04:00
if ( copy_to_user ( p , & abs , min_t ( size_t ,
_IOC_SIZE ( cmd ) ,
sizeof ( struct input_absinfo ) ) ) )
2007-08-30 08:22:18 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:18 +04:00
}
if ( _IOC_DIR ( cmd ) = = _IOC_WRITE ) {
2005-04-17 02:20:36 +04:00
2008-06-02 09:08:10 +04:00
if ( _IOC_NR ( cmd ) = = _IOC_NR ( EVIOCSFF ) ) {
2008-10-17 06:31:42 +04:00
if ( input_ff_effect_from_user ( p , _IOC_SIZE ( cmd ) , & effect ) )
2008-06-02 09:08:10 +04:00
return - EFAULT ;
error = input_ff_upload ( dev , & effect , file ) ;
if ( put_user ( effect . id , & ( ( ( struct ff_effect __user * ) p ) - > id ) ) )
return - EFAULT ;
return error ;
}
2007-08-30 08:22:18 +04:00
if ( ( _IOC_NR ( cmd ) & ~ ABS_MAX ) = = _IOC_NR ( EVIOCSABS ( 0 ) ) ) {
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
t = _IOC_NR ( cmd ) & ABS_MAX ;
2005-05-29 11:30:15 +04:00
2009-06-11 10:27:24 +04:00
if ( copy_from_user ( & abs , p , min_t ( size_t ,
_IOC_SIZE ( cmd ) ,
sizeof ( struct input_absinfo ) ) ) )
2007-08-30 08:22:18 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +04:00
/*
* Take event lock to ensure that we are not
* changing device parameters in the middle
* of event .
*/
spin_lock_irq ( & dev - > event_lock ) ;
2005-04-17 02:20:36 +04:00
2007-08-30 08:22:18 +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 ;
2009-06-11 10:27:24 +04:00
dev - > absres [ t ] = _IOC_SIZE ( cmd ) < sizeof ( struct input_absinfo ) ?
0 : abs . resolution ;
2007-08-30 08:22:18 +04:00
spin_unlock_irq ( & dev - > event_lock ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-30 08:22:18 +04:00
}
2005-04-17 02:20:36 +04:00
}
return - EINVAL ;
}
2007-08-30 08:22:18 +04:00
static long evdev_ioctl_handler ( struct file * file , unsigned int cmd ,
void __user * p , int compat_mode )
{
struct evdev_client * client = file - > private_data ;
struct evdev * evdev = client - > evdev ;
int retval ;
retval = mutex_lock_interruptible ( & evdev - > mutex ) ;
if ( retval )
return retval ;
if ( ! evdev - > exist ) {
retval = - ENODEV ;
goto out ;
}
retval = evdev_do_ioctl ( file , cmd , p , compat_mode ) ;
out :
mutex_unlock ( & evdev - > mutex ) ;
return retval ;
}
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
2007-08-30 08:22:18 +04:00
static long evdev_ioctl_compat ( struct file * file ,
unsigned int cmd , unsigned long arg )
2005-05-29 11:26:43 +04:00
{
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 = {
2007-08-30 08:22:18 +04:00
. owner = THIS_MODULE ,
. read = evdev_read ,
. write = evdev_write ,
. poll = evdev_poll ,
. open = evdev_open ,
. release = evdev_release ,
. unlocked_ioctl = evdev_ioctl ,
2005-05-29 11:26:43 +04:00
# ifdef CONFIG_COMPAT
2007-08-30 08:22:18 +04:00
. compat_ioctl = evdev_ioctl_compat ,
2005-05-29 11:26:43 +04:00
# endif
2007-08-30 08:22:18 +04:00
. fasync = evdev_fasync ,
. flush = evdev_flush
2005-04-17 02:20:36 +04:00
} ;
2007-08-30 08:22:18 +04:00
static int evdev_install_chrdev ( struct evdev * evdev )
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table [ evdev - > minor ] = evdev ;
return 0 ;
}
static void evdev_remove_chrdev ( struct evdev * evdev )
{
/*
* Lock evdev table to prevent race with evdev_open ( )
*/
mutex_lock ( & evdev_table_mutex ) ;
evdev_table [ evdev - > minor ] = NULL ;
mutex_unlock ( & evdev_table_mutex ) ;
}
/*
* Mark device non - existent . 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 evdev_mark_dead ( struct evdev * evdev )
{
mutex_lock ( & evdev - > mutex ) ;
evdev - > exist = 0 ;
mutex_unlock ( & evdev - > mutex ) ;
}
static void evdev_cleanup ( struct evdev * evdev )
{
struct input_handle * handle = & evdev - > handle ;
evdev_mark_dead ( evdev ) ;
evdev_hangup ( evdev ) ;
evdev_remove_chrdev ( evdev ) ;
/* evdev is marked dead so no one else accesses evdev->open */
if ( evdev - > open ) {
input_flush_device ( handle , NULL ) ;
input_close_device ( handle ) ;
}
}
/*
* Create new evdev device . Note that input core serializes calls
* to connect and disconnect so we don ' t need to lock evdev_table here .
*/
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
2007-08-30 08:22:18 +04:00
for ( minor = 0 ; minor < EVDEV_MINORS ; minor + + )
if ( ! evdev_table [ minor ] )
break ;
2005-04-17 02:20:36 +04:00
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 ) ;
2007-08-30 08:22:18 +04:00
spin_lock_init ( & evdev - > client_lock ) ;
mutex_init ( & evdev - > mutex ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & evdev - > wait ) ;
2009-05-10 03:08:04 +04:00
dev_set_name ( & evdev - > dev , " event%d " , minor ) ;
2005-04-17 02:20:36 +04:00
evdev - > exist = 1 ;
evdev - > minor = minor ;
2007-08-30 08:22:18 +04:00
2008-04-01 08:22:53 +04:00
evdev - > handle . dev = input_get_device ( dev ) ;
2009-05-10 03:08:04 +04:00
evdev - > handle . name = dev_name ( & evdev - > dev ) ;
2005-04-17 02:20:36 +04:00
evdev - > handle . handler = handler ;
evdev - > handle . private = evdev ;
2007-08-30 08:22:18 +04:00
evdev - > dev . devt = MKDEV ( INPUT_MAJOR , EVDEV_MINOR_BASE + minor ) ;
2007-06-15 07:32:24 +04:00
evdev - > dev . class = & input_class ;
evdev - > dev . parent = & dev - > dev ;
evdev - > dev . release = evdev_free ;
device_initialize ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
2007-08-30 08:22:18 +04:00
error = input_register_handle ( & evdev - > handle ) ;
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
2007-08-30 08:22:18 +04:00
error = evdev_install_chrdev ( evdev ) ;
if ( error )
goto err_unregister_handle ;
error = device_add ( & evdev - > dev ) ;
2007-04-12 09:29:46 +04:00
if ( error )
2007-08-30 08:22:18 +04:00
goto err_cleanup_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-08-30 08:22:18 +04:00
err_cleanup_evdev :
evdev_cleanup ( evdev ) ;
err_unregister_handle :
input_unregister_handle ( & evdev - > handle ) ;
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-06-15 07:32:24 +04:00
device_del ( & evdev - > dev ) ;
2007-08-30 08:22:18 +04:00
evdev_cleanup ( evdev ) ;
input_unregister_handle ( handle ) ;
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 = {
2007-08-30 08:22:18 +04:00
. event = evdev_event ,
. connect = evdev_connect ,
. disconnect = evdev_disconnect ,
. fops = & evdev_fops ,
. minor = EVDEV_MINOR_BASE ,
. name = " evdev " ,
. id_table = evdev_ids ,
2005-04-17 02:20:36 +04:00
} ;
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 " ) ;