2005-04-17 02:20:36 +04:00
/*
* The input core
*
* 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 .
*/
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/smp_lock.h>
# include <linux/input.h>
# include <linux/module.h>
# include <linux/random.h>
# include <linux/major.h>
# include <linux/proc_fs.h>
2006-04-02 09:09:34 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/interrupt.h>
# include <linux/poll.h>
# include <linux/device.h>
2006-02-19 08:21:46 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@suse.cz> " ) ;
MODULE_DESCRIPTION ( " Input core " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define INPUT_DEVICES 256
static LIST_HEAD ( input_dev_list ) ;
static LIST_HEAD ( input_handler_list ) ;
static struct input_handler * input_table [ 8 ] ;
2006-07-06 08:22:43 +04:00
/**
* input_event ( ) - report new input event
2006-11-03 07:26:55 +03:00
* @ dev : device that generated the event
2006-07-06 08:22:43 +04:00
* @ type : type of the event
* @ code : event code
* @ value : value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event ( )
*/
2005-04-17 02:20:36 +04:00
void input_event ( struct input_dev * dev , unsigned int type , unsigned int code , int value )
{
struct input_handle * handle ;
if ( type > EV_MAX | | ! test_bit ( type , dev - > evbit ) )
return ;
add_input_randomness ( type , code , value ) ;
switch ( type ) {
case EV_SYN :
switch ( code ) {
case SYN_CONFIG :
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case SYN_REPORT :
2006-06-26 09:48:47 +04:00
if ( dev - > sync )
return ;
2005-04-17 02:20:36 +04:00
dev - > sync = 1 ;
break ;
}
break ;
case EV_KEY :
if ( code > KEY_MAX | | ! test_bit ( code , dev - > keybit ) | | ! ! test_bit ( code , dev - > key ) = = value )
return ;
if ( value = = 2 )
break ;
change_bit ( code , dev - > key ) ;
if ( test_bit ( EV_REP , dev - > evbit ) & & dev - > rep [ REP_PERIOD ] & & dev - > rep [ REP_DELAY ] & & dev - > timer . data & & value ) {
dev - > repeat_key = code ;
mod_timer ( & dev - > timer , jiffies + msecs_to_jiffies ( dev - > rep [ REP_DELAY ] ) ) ;
}
break ;
2005-09-07 02:19:06 +04:00
case EV_SW :
if ( code > SW_MAX | | ! test_bit ( code , dev - > swbit ) | | ! ! test_bit ( code , dev - > sw ) = = value )
return ;
change_bit ( code , dev - > sw ) ;
break ;
2005-04-17 02:20:36 +04:00
case EV_ABS :
if ( code > ABS_MAX | | ! test_bit ( code , dev - > absbit ) )
return ;
if ( dev - > absfuzz [ code ] ) {
if ( ( value > dev - > abs [ code ] - ( dev - > absfuzz [ code ] > > 1 ) ) & &
( value < dev - > abs [ code ] + ( dev - > absfuzz [ code ] > > 1 ) ) )
return ;
if ( ( value > dev - > abs [ code ] - dev - > absfuzz [ code ] ) & &
( value < dev - > abs [ code ] + dev - > absfuzz [ code ] ) )
value = ( dev - > abs [ code ] * 3 + value ) > > 2 ;
if ( ( value > dev - > abs [ code ] - ( dev - > absfuzz [ code ] < < 1 ) ) & &
( value < dev - > abs [ code ] + ( dev - > absfuzz [ code ] < < 1 ) ) )
value = ( dev - > abs [ code ] + value ) > > 1 ;
}
if ( dev - > abs [ code ] = = value )
return ;
dev - > abs [ code ] = value ;
break ;
case EV_REL :
if ( code > REL_MAX | | ! test_bit ( code , dev - > relbit ) | | ( value = = 0 ) )
return ;
break ;
case EV_MSC :
if ( code > MSC_MAX | | ! test_bit ( code , dev - > mscbit ) )
return ;
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case EV_LED :
if ( code > LED_MAX | | ! test_bit ( code , dev - > ledbit ) | | ! ! test_bit ( code , dev - > led ) = = value )
return ;
change_bit ( code , dev - > led ) ;
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case EV_SND :
if ( code > SND_MAX | | ! test_bit ( code , dev - > sndbit ) )
return ;
2006-04-29 09:13:48 +04:00
if ( ! ! test_bit ( code , dev - > snd ) ! = ! ! value )
change_bit ( code , dev - > snd ) ;
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case EV_REP :
2006-06-26 09:48:47 +04:00
if ( code > REP_MAX | | value < 0 | | dev - > rep [ code ] = = value )
return ;
2005-04-17 02:20:36 +04:00
dev - > rep [ code ] = value ;
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case EV_FF :
2006-07-19 09:40:22 +04:00
if ( value < 0 )
return ;
2006-06-26 09:48:47 +04:00
if ( dev - > event )
dev - > event ( dev , type , code , value ) ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( type ! = EV_SYN )
dev - > sync = 0 ;
if ( dev - > grab )
dev - > grab - > handler - > event ( dev - > grab , type , code , value ) ;
else
list_for_each_entry ( handle , & dev - > h_list , d_node )
if ( handle - > open )
handle - > handler - > event ( handle , type , code , value ) ;
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_event ) ;
2005-04-17 02:20:36 +04:00
2006-07-06 08:22:43 +04:00
/**
* input_inject_event ( ) - send input event from input handler
* @ handle : input handle to send event through
* @ type : type of the event
* @ code : event code
* @ value : value of the event
*
* Similar to input_event ( ) but will ignore event if device is " grabbed " and handle
* injecting event is not the one that owns the device .
*/
void input_inject_event ( struct input_handle * handle , unsigned int type , unsigned int code , int value )
{
if ( ! handle - > dev - > grab | | handle - > dev - > grab = = handle )
input_event ( handle - > dev , type , code , value ) ;
}
EXPORT_SYMBOL ( input_inject_event ) ;
2005-04-17 02:20:36 +04:00
static void input_repeat_key ( unsigned long data )
{
struct input_dev * dev = ( void * ) data ;
if ( ! test_bit ( dev - > repeat_key , dev - > key ) )
return ;
input_event ( dev , EV_KEY , dev - > repeat_key , 2 ) ;
input_sync ( dev ) ;
if ( dev - > rep [ REP_PERIOD ] )
mod_timer ( & dev - > timer , jiffies + msecs_to_jiffies ( dev - > rep [ REP_PERIOD ] ) ) ;
}
int input_grab_device ( struct input_handle * handle )
{
if ( handle - > dev - > grab )
return - EBUSY ;
handle - > dev - > grab = handle ;
return 0 ;
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_grab_device ) ;
2005-04-17 02:20:36 +04:00
void input_release_device ( struct input_handle * handle )
{
2006-07-15 09:17:38 +04:00
struct input_dev * dev = handle - > dev ;
2006-07-06 08:21:03 +04:00
2006-07-15 09:17:38 +04:00
if ( dev - > grab = = handle ) {
dev - > grab = NULL ;
list_for_each_entry ( handle , & dev - > h_list , d_node )
2006-07-06 08:21:03 +04:00
if ( handle - > handler - > start )
handle - > handler - > start ( handle ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_release_device ) ;
2005-04-17 02:20:36 +04:00
int input_open_device ( struct input_handle * handle )
{
2005-05-29 11:29:25 +04:00
struct input_dev * dev = handle - > dev ;
int err ;
2006-02-19 08:21:46 +03:00
err = mutex_lock_interruptible ( & dev - > mutex ) ;
2005-05-29 11:29:25 +04:00
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
handle - > open + + ;
2005-05-29 11:29:25 +04:00
if ( ! dev - > users + + & & dev - > open )
err = dev - > open ( dev ) ;
if ( err )
handle - > open - - ;
2006-02-19 08:21:46 +03:00
mutex_unlock ( & dev - > mutex ) ;
2005-05-29 11:29:25 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_open_device ) ;
2005-04-17 02:20:36 +04:00
int input_flush_device ( struct input_handle * handle , struct file * file )
{
if ( handle - > dev - > flush )
return handle - > dev - > flush ( handle - > dev , file ) ;
return 0 ;
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_flush_device ) ;
2005-04-17 02:20:36 +04:00
void input_close_device ( struct input_handle * handle )
{
2005-05-29 11:29:25 +04:00
struct input_dev * dev = handle - > dev ;
2005-04-17 02:20:36 +04:00
input_release_device ( handle ) ;
2005-05-29 11:29:25 +04:00
2006-02-19 08:21:46 +03:00
mutex_lock ( & dev - > mutex ) ;
2005-05-29 11:29:25 +04:00
if ( ! - - dev - > users & & dev - > close )
dev - > close ( dev ) ;
2005-04-17 02:20:36 +04:00
handle - > open - - ;
2005-05-29 11:29:25 +04:00
2006-02-19 08:21:46 +03:00
mutex_unlock ( & dev - > mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_close_device ) ;
2005-04-17 02:20:36 +04:00
static void input_link_handle ( struct input_handle * handle )
{
list_add_tail ( & handle - > d_node , & handle - > dev - > h_list ) ;
list_add_tail ( & handle - > h_node , & handle - > handler - > h_list ) ;
}
# define MATCH_BIT(bit, max) \
for ( i = 0 ; i < NBITS ( max ) ; i + + ) \
if ( ( id - > bit [ i ] & dev - > bit [ i ] ) ! = id - > bit [ i ] ) \
break ; \
if ( i ! = NBITS ( max ) ) \
continue ;
2006-09-14 09:31:59 +04:00
static const struct input_device_id * input_match_device ( const struct input_device_id * id ,
struct input_dev * dev )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( ; id - > flags | | id - > driver_info ; id + + ) {
if ( id - > flags & INPUT_DEVICE_ID_MATCH_BUS )
2006-04-26 08:14:19 +04:00
if ( id - > bustype ! = dev - > id . bustype )
2005-04-17 02:20:36 +04:00
continue ;
if ( id - > flags & INPUT_DEVICE_ID_MATCH_VENDOR )
2006-04-26 08:14:19 +04:00
if ( id - > vendor ! = dev - > id . vendor )
2005-04-17 02:20:36 +04:00
continue ;
if ( id - > flags & INPUT_DEVICE_ID_MATCH_PRODUCT )
2006-04-26 08:14:19 +04:00
if ( id - > product ! = dev - > id . product )
2005-04-17 02:20:36 +04:00
continue ;
if ( id - > flags & INPUT_DEVICE_ID_MATCH_VERSION )
2006-04-26 08:14:19 +04:00
if ( id - > version ! = dev - > id . version )
2005-04-17 02:20:36 +04:00
continue ;
MATCH_BIT ( evbit , EV_MAX ) ;
MATCH_BIT ( keybit , KEY_MAX ) ;
MATCH_BIT ( relbit , REL_MAX ) ;
MATCH_BIT ( absbit , ABS_MAX ) ;
MATCH_BIT ( mscbit , MSC_MAX ) ;
MATCH_BIT ( ledbit , LED_MAX ) ;
MATCH_BIT ( sndbit , SND_MAX ) ;
MATCH_BIT ( ffbit , FF_MAX ) ;
2005-09-24 11:02:29 +04:00
MATCH_BIT ( swbit , SW_MAX ) ;
2005-04-17 02:20:36 +04:00
return id ;
}
return NULL ;
}
2005-06-30 09:50:29 +04:00
# ifdef CONFIG_PROC_FS
static struct proc_dir_entry * proc_bus_input_dir ;
static DECLARE_WAIT_QUEUE_HEAD ( input_devices_poll_wait ) ;
static int input_devices_state ;
static inline void input_wakeup_procfs_readers ( void )
{
input_devices_state + + ;
wake_up ( & input_devices_poll_wait ) ;
}
2006-04-02 09:09:34 +04:00
static unsigned int input_proc_devices_poll ( struct file * file , poll_table * wait )
2005-06-30 09:50:29 +04:00
{
int state = input_devices_state ;
2006-06-26 09:48:47 +04:00
2005-06-30 09:50:29 +04:00
poll_wait ( file , & input_devices_poll_wait , wait ) ;
if ( state ! = input_devices_state )
return POLLIN | POLLRDNORM ;
2006-06-26 09:48:47 +04:00
2005-06-30 09:50:29 +04:00
return 0 ;
}
2006-04-02 09:09:34 +04:00
static struct list_head * list_get_nth_element ( struct list_head * list , loff_t * pos )
{
struct list_head * node ;
loff_t i = 0 ;
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
list_for_each ( node , list )
if ( i + + = = * pos )
return node ;
return NULL ;
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static struct list_head * list_get_next_element ( struct list_head * list , struct list_head * element , loff_t * pos )
2005-06-30 09:50:29 +04:00
{
2006-04-02 09:09:34 +04:00
if ( element - > next = = list )
return NULL ;
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
+ + ( * pos ) ;
return element - > next ;
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static void * input_devices_seq_start ( struct seq_file * seq , loff_t * pos )
{
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
return list_get_nth_element ( & input_dev_list , pos ) ;
}
2005-09-15 11:01:54 +04:00
2006-04-02 09:09:34 +04:00
static void * input_devices_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
return list_get_next_element ( & input_dev_list , v , pos ) ;
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static void input_devices_seq_stop ( struct seq_file * seq , void * v )
{
/* release lock here */
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static void input_seq_print_bitmap ( struct seq_file * seq , const char * name ,
unsigned long * bitmap , int max )
{
int i ;
2005-09-15 11:01:54 +04:00
2006-04-02 09:09:34 +04:00
for ( i = NBITS ( max ) - 1 ; i > 0 ; i - - )
if ( bitmap [ i ] )
break ;
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
seq_printf ( seq , " B: %s= " , name ) ;
for ( ; i > = 0 ; i - - )
seq_printf ( seq , " %lx%s " , bitmap [ i ] , i > 0 ? " " : " " ) ;
seq_putc ( seq , ' \n ' ) ;
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static int input_devices_seq_show ( struct seq_file * seq , void * v )
{
struct input_dev * dev = container_of ( v , struct input_dev , node ) ;
const char * path = kobject_get_path ( & dev - > cdev . kobj , GFP_KERNEL ) ;
struct input_handle * handle ;
seq_printf ( seq , " I: Bus=%04x Vendor=%04x Product=%04x Version=%04x \n " ,
dev - > id . bustype , dev - > id . vendor , dev - > id . product , dev - > id . version ) ;
seq_printf ( seq , " N: Name= \" %s \" \n " , dev - > name ? dev - > name : " " ) ;
seq_printf ( seq , " P: Phys=%s \n " , dev - > phys ? dev - > phys : " " ) ;
seq_printf ( seq , " S: Sysfs=%s \n " , path ? path : " " ) ;
seq_printf ( seq , " H: Handlers= " ) ;
list_for_each_entry ( handle , & dev - > h_list , d_node )
seq_printf ( seq , " %s " , handle - > name ) ;
seq_putc ( seq , ' \n ' ) ;
input_seq_print_bitmap ( seq , " EV " , dev - > evbit , EV_MAX ) ;
if ( test_bit ( EV_KEY , dev - > evbit ) )
input_seq_print_bitmap ( seq , " KEY " , dev - > keybit , KEY_MAX ) ;
if ( test_bit ( EV_REL , dev - > evbit ) )
input_seq_print_bitmap ( seq , " REL " , dev - > relbit , REL_MAX ) ;
if ( test_bit ( EV_ABS , dev - > evbit ) )
input_seq_print_bitmap ( seq , " ABS " , dev - > absbit , ABS_MAX ) ;
if ( test_bit ( EV_MSC , dev - > evbit ) )
input_seq_print_bitmap ( seq , " MSC " , dev - > mscbit , MSC_MAX ) ;
if ( test_bit ( EV_LED , dev - > evbit ) )
input_seq_print_bitmap ( seq , " LED " , dev - > ledbit , LED_MAX ) ;
if ( test_bit ( EV_SND , dev - > evbit ) )
input_seq_print_bitmap ( seq , " SND " , dev - > sndbit , SND_MAX ) ;
if ( test_bit ( EV_FF , dev - > evbit ) )
input_seq_print_bitmap ( seq , " FF " , dev - > ffbit , FF_MAX ) ;
if ( test_bit ( EV_SW , dev - > evbit ) )
input_seq_print_bitmap ( seq , " SW " , dev - > swbit , SW_MAX ) ;
seq_putc ( seq , ' \n ' ) ;
kfree ( path ) ;
return 0 ;
2005-06-30 09:50:29 +04:00
}
2006-04-02 09:09:34 +04:00
static struct seq_operations input_devices_seq_ops = {
. start = input_devices_seq_start ,
. next = input_devices_seq_next ,
. stop = input_devices_seq_stop ,
. show = input_devices_seq_show ,
} ;
static int input_proc_devices_open ( struct inode * inode , struct file * file )
2005-06-30 09:50:29 +04:00
{
2006-04-02 09:09:34 +04:00
return seq_open ( file , & input_devices_seq_ops ) ;
}
static struct file_operations input_devices_fileops = {
. owner = THIS_MODULE ,
. open = input_proc_devices_open ,
. poll = input_proc_devices_poll ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static void * input_handlers_seq_start ( struct seq_file * seq , loff_t * pos )
{
/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
seq - > private = ( void * ) ( unsigned long ) * pos ;
return list_get_nth_element ( & input_handler_list , pos ) ;
}
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
static void * input_handlers_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
seq - > private = ( void * ) ( unsigned long ) ( * pos + 1 ) ;
return list_get_next_element ( & input_handler_list , v , pos ) ;
2005-06-30 09:50:29 +04:00
}
2006-04-02 09:09:34 +04:00
static void input_handlers_seq_stop ( struct seq_file * seq , void * v )
{
/* release lock here */
}
static int input_handlers_seq_show ( struct seq_file * seq , void * v )
{
struct input_handler * handler = container_of ( v , struct input_handler , node ) ;
seq_printf ( seq , " N: Number=%ld Name=%s " ,
( unsigned long ) seq - > private , handler - > name ) ;
if ( handler - > fops )
seq_printf ( seq , " Minor=%d " , handler - > minor ) ;
seq_putc ( seq , ' \n ' ) ;
return 0 ;
}
static struct seq_operations input_handlers_seq_ops = {
. start = input_handlers_seq_start ,
. next = input_handlers_seq_next ,
. stop = input_handlers_seq_stop ,
. show = input_handlers_seq_show ,
} ;
static int input_proc_handlers_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & input_handlers_seq_ops ) ;
}
static struct file_operations input_handlers_fileops = {
. owner = THIS_MODULE ,
. open = input_proc_handlers_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
2005-06-30 09:50:29 +04:00
static int __init input_proc_init ( void )
{
struct proc_dir_entry * entry ;
proc_bus_input_dir = proc_mkdir ( " input " , proc_bus ) ;
if ( ! proc_bus_input_dir )
return - ENOMEM ;
proc_bus_input_dir - > owner = THIS_MODULE ;
2006-04-02 09:09:34 +04:00
entry = create_proc_entry ( " devices " , 0 , proc_bus_input_dir ) ;
2005-06-30 09:50:29 +04:00
if ( ! entry )
goto fail1 ;
entry - > owner = THIS_MODULE ;
2006-04-02 09:09:34 +04:00
entry - > proc_fops = & input_devices_fileops ;
2005-06-30 09:50:29 +04:00
2006-04-02 09:09:34 +04:00
entry = create_proc_entry ( " handlers " , 0 , proc_bus_input_dir ) ;
2005-06-30 09:50:29 +04:00
if ( ! entry )
goto fail2 ;
entry - > owner = THIS_MODULE ;
2006-04-02 09:09:34 +04:00
entry - > proc_fops = & input_handlers_fileops ;
2005-06-30 09:50:29 +04:00
return 0 ;
fail2 : remove_proc_entry ( " devices " , proc_bus_input_dir ) ;
fail1 : remove_proc_entry ( " input " , proc_bus ) ;
return - ENOMEM ;
}
2005-07-02 08:54:30 +04:00
static void input_proc_exit ( void )
2005-06-30 09:50:29 +04:00
{
remove_proc_entry ( " devices " , proc_bus_input_dir ) ;
remove_proc_entry ( " handlers " , proc_bus_input_dir ) ;
remove_proc_entry ( " input " , proc_bus ) ;
}
# else /* !CONFIG_PROC_FS */
static inline void input_wakeup_procfs_readers ( void ) { }
static inline int input_proc_init ( void ) { return 0 ; }
static inline void input_proc_exit ( void ) { }
# endif
2005-09-15 11:01:55 +04:00
# define INPUT_DEV_STRING_ATTR_SHOW(name) \
static ssize_t input_dev_show_ # # name ( struct class_device * dev , char * buf ) \
{ \
struct input_dev * input_dev = to_input_dev ( dev ) ; \
int retval ; \
\
2006-02-19 08:21:46 +03:00
retval = mutex_lock_interruptible ( & input_dev - > mutex ) ; \
2005-09-15 11:01:55 +04:00
if ( retval ) \
return retval ; \
\
2006-04-02 09:09:26 +04:00
retval = scnprintf ( buf , PAGE_SIZE , \
" %s \n " , input_dev - > name ? input_dev - > name : " " ) ; \
2005-09-15 11:01:55 +04:00
\
2006-02-19 08:21:46 +03:00
mutex_unlock ( & input_dev - > mutex ) ; \
2005-09-15 11:01:55 +04:00
\
return retval ; \
2005-10-28 09:25:43 +04:00
} \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , input_dev_show_ # # name , NULL ) ;
2005-09-15 11:01:55 +04:00
INPUT_DEV_STRING_ATTR_SHOW ( name ) ;
INPUT_DEV_STRING_ATTR_SHOW ( phys ) ;
INPUT_DEV_STRING_ATTR_SHOW ( uniq ) ;
2006-04-02 09:09:51 +04:00
static int input_print_modalias_bits ( char * buf , int size ,
char name , unsigned long * bm ,
unsigned int min_bit , unsigned int max_bit )
2005-12-07 23:40:34 +03:00
{
2006-04-02 09:09:51 +04:00
int len = 0 , i ;
2005-12-07 23:40:34 +03:00
2006-04-02 09:09:51 +04:00
len + = snprintf ( buf , max ( size , 0 ) , " %c " , name ) ;
for ( i = min_bit ; i < max_bit ; i + + )
if ( bm [ LONG ( i ) ] & BIT ( i ) )
len + = snprintf ( buf + len , max ( size - len , 0 ) , " %X, " , i ) ;
2005-12-07 23:40:34 +03:00
return len ;
}
2006-04-02 09:09:26 +04:00
static int input_print_modalias ( char * buf , int size , struct input_dev * id ,
int add_cr )
2005-12-07 23:40:34 +03:00
{
2006-01-05 15:19:55 +03:00
int len ;
2005-12-07 23:40:34 +03:00
2006-04-02 09:09:51 +04:00
len = snprintf ( buf , max ( size , 0 ) ,
" input:b%04Xv%04Xp%04Xe%04X- " ,
id - > id . bustype , id - > id . vendor ,
id - > id . product , id - > id . version ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' e ' , id - > evbit , 0 , EV_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' k ' , id - > keybit , KEY_MIN_INTERESTING , KEY_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' r ' , id - > relbit , 0 , REL_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' a ' , id - > absbit , 0 , ABS_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' m ' , id - > mscbit , 0 , MSC_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' l ' , id - > ledbit , 0 , LED_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' s ' , id - > sndbit , 0 , SND_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' f ' , id - > ffbit , 0 , FF_MAX ) ;
len + = input_print_modalias_bits ( buf + len , size - len ,
' w ' , id - > swbit , 0 , SW_MAX ) ;
2006-04-02 09:09:26 +04:00
if ( add_cr )
2006-04-02 09:09:51 +04:00
len + = snprintf ( buf + len , max ( size - len , 0 ) , " \n " ) ;
2006-04-02 09:09:26 +04:00
2006-01-05 15:19:55 +03:00
return len ;
}
static ssize_t input_dev_show_modalias ( struct class_device * dev , char * buf )
{
struct input_dev * id = to_input_dev ( dev ) ;
ssize_t len ;
2006-04-02 09:09:26 +04:00
len = input_print_modalias ( buf , PAGE_SIZE , id , 1 ) ;
2006-06-26 09:48:21 +04:00
return min_t ( int , len , PAGE_SIZE ) ;
2005-12-07 23:40:34 +03:00
}
static CLASS_DEVICE_ATTR ( modalias , S_IRUGO , input_dev_show_modalias , NULL ) ;
2005-10-28 09:25:43 +04:00
static struct attribute * input_dev_attrs [ ] = {
& class_device_attr_name . attr ,
& class_device_attr_phys . attr ,
& class_device_attr_uniq . attr ,
2005-12-07 23:40:34 +03:00
& class_device_attr_modalias . attr ,
2005-10-28 09:25:43 +04:00
NULL
} ;
2005-11-20 08:56:31 +03:00
static struct attribute_group input_dev_attr_group = {
2005-10-28 09:25:43 +04:00
. attrs = input_dev_attrs ,
2005-09-15 11:01:55 +04:00
} ;
# define INPUT_DEV_ID_ATTR(name) \
static ssize_t input_dev_show_id_ # # name ( struct class_device * dev , char * buf ) \
{ \
struct input_dev * input_dev = to_input_dev ( dev ) ; \
2006-04-02 09:09:26 +04:00
return scnprintf ( buf , PAGE_SIZE , " %04x \n " , input_dev - > id . name ) ; \
2005-09-15 11:01:55 +04:00
} \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , input_dev_show_id_ # # name , NULL ) ;
INPUT_DEV_ID_ATTR ( bustype ) ;
INPUT_DEV_ID_ATTR ( vendor ) ;
INPUT_DEV_ID_ATTR ( product ) ;
INPUT_DEV_ID_ATTR ( version ) ;
static struct attribute * input_dev_id_attrs [ ] = {
& class_device_attr_bustype . attr ,
& class_device_attr_vendor . attr ,
& class_device_attr_product . attr ,
& class_device_attr_version . attr ,
NULL
} ;
static struct attribute_group input_dev_id_attr_group = {
. name = " id " ,
. attrs = input_dev_id_attrs ,
} ;
2006-04-02 09:09:34 +04:00
static int input_print_bitmap ( char * buf , int buf_size , unsigned long * bitmap ,
int max , int add_cr )
{
int i ;
int len = 0 ;
for ( i = NBITS ( max ) - 1 ; i > 0 ; i - - )
if ( bitmap [ i ] )
break ;
for ( ; i > = 0 ; i - - )
len + = snprintf ( buf + len , max ( buf_size - len , 0 ) ,
" %lx%s " , bitmap [ i ] , i > 0 ? " " : " " ) ;
if ( add_cr )
len + = snprintf ( buf + len , max ( buf_size - len , 0 ) , " \n " ) ;
return len ;
}
2005-09-15 11:01:55 +04:00
# define INPUT_DEV_CAP_ATTR(ev, bm) \
static ssize_t input_dev_show_cap_ # # bm ( struct class_device * dev , char * buf ) \
{ \
struct input_dev * input_dev = to_input_dev ( dev ) ; \
2006-04-02 09:09:26 +04:00
int len = input_print_bitmap ( buf , PAGE_SIZE , \
input_dev - > bm # # bit , ev # # _MAX , 1 ) ; \
return min_t ( int , len , PAGE_SIZE ) ; \
2005-09-15 11:01:55 +04:00
} \
static CLASS_DEVICE_ATTR ( bm , S_IRUGO , input_dev_show_cap_ # # bm , NULL ) ;
INPUT_DEV_CAP_ATTR ( EV , ev ) ;
INPUT_DEV_CAP_ATTR ( KEY , key ) ;
INPUT_DEV_CAP_ATTR ( REL , rel ) ;
INPUT_DEV_CAP_ATTR ( ABS , abs ) ;
INPUT_DEV_CAP_ATTR ( MSC , msc ) ;
INPUT_DEV_CAP_ATTR ( LED , led ) ;
INPUT_DEV_CAP_ATTR ( SND , snd ) ;
INPUT_DEV_CAP_ATTR ( FF , ff ) ;
INPUT_DEV_CAP_ATTR ( SW , sw ) ;
static struct attribute * input_dev_caps_attrs [ ] = {
& class_device_attr_ev . attr ,
& class_device_attr_key . attr ,
& class_device_attr_rel . attr ,
& class_device_attr_abs . attr ,
& class_device_attr_msc . attr ,
& class_device_attr_led . attr ,
& class_device_attr_snd . attr ,
& class_device_attr_ff . attr ,
& class_device_attr_sw . attr ,
NULL
} ;
static struct attribute_group input_dev_caps_attr_group = {
. name = " capabilities " ,
. attrs = input_dev_caps_attrs ,
} ;
2005-09-15 11:01:39 +04:00
static void input_dev_release ( struct class_device * class_dev )
{
struct input_dev * dev = to_input_dev ( class_dev ) ;
2006-07-19 09:40:22 +04:00
input_ff_destroy ( dev ) ;
2005-09-15 11:01:39 +04:00
kfree ( dev ) ;
2006-07-19 09:40:22 +04:00
2005-09-15 11:01:39 +04:00
module_put ( THIS_MODULE ) ;
}
2005-09-15 11:01:57 +04:00
/*
2005-11-16 11:00:00 +03:00
* Input uevent interface - loading event handlers based on
2005-09-15 11:01:57 +04:00
* device bitfields .
*/
2005-11-16 11:00:00 +03:00
static int input_add_uevent_bm_var ( char * * envp , int num_envp , int * cur_index ,
2006-04-02 09:09:51 +04:00
char * buffer , int buffer_size , int * cur_len ,
const char * name , unsigned long * bitmap , int max )
2005-09-15 11:01:57 +04:00
{
if ( * cur_index > = num_envp - 1 )
return - ENOMEM ;
envp [ * cur_index ] = buffer + * cur_len ;
* cur_len + = snprintf ( buffer + * cur_len , max ( buffer_size - * cur_len , 0 ) , name ) ;
2006-04-02 09:09:51 +04:00
if ( * cur_len > = buffer_size )
2005-09-15 11:01:57 +04:00
return - ENOMEM ;
* cur_len + = input_print_bitmap ( buffer + * cur_len ,
max ( buffer_size - * cur_len , 0 ) ,
2006-04-02 09:09:26 +04:00
bitmap , max , 0 ) + 1 ;
2005-09-15 11:01:57 +04:00
if ( * cur_len > buffer_size )
return - ENOMEM ;
( * cur_index ) + + ;
return 0 ;
}
2006-04-02 09:09:51 +04:00
static int input_add_uevent_modalias_var ( char * * envp , int num_envp , int * cur_index ,
char * buffer , int buffer_size , int * cur_len ,
struct input_dev * dev )
{
if ( * cur_index > = num_envp - 1 )
return - ENOMEM ;
envp [ * cur_index ] = buffer + * cur_len ;
* cur_len + = snprintf ( buffer + * cur_len , max ( buffer_size - * cur_len , 0 ) ,
" MODALIAS= " ) ;
if ( * cur_len > = buffer_size )
return - ENOMEM ;
* cur_len + = input_print_modalias ( buffer + * cur_len ,
max ( buffer_size - * cur_len , 0 ) ,
dev , 0 ) + 1 ;
if ( * cur_len > buffer_size )
return - ENOMEM ;
( * cur_index ) + + ;
return 0 ;
}
2005-09-15 11:01:57 +04:00
# define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \
do { \
2006-04-02 09:09:51 +04:00
int err = add_uevent_var ( envp , num_envp , & i , \
2005-09-15 11:01:57 +04:00
buffer , buffer_size , & len , \
fmt , val ) ; \
if ( err ) \
return err ; \
} while ( 0 )
# define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max) \
do { \
2005-11-16 11:00:00 +03:00
int err = input_add_uevent_bm_var ( envp , num_envp , & i , \
2005-09-15 11:01:57 +04:00
buffer , buffer_size , & len , \
name , bm , max ) ; \
if ( err ) \
return err ; \
} while ( 0 )
2006-04-02 09:09:51 +04:00
# define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev) \
do { \
int err = input_add_uevent_modalias_var ( envp , \
num_envp , & i , \
buffer , buffer_size , & len , \
dev ) ; \
if ( err ) \
return err ; \
} while ( 0 )
2005-11-16 11:00:00 +03:00
static int input_dev_uevent ( struct class_device * cdev , char * * envp ,
int num_envp , char * buffer , int buffer_size )
2005-09-15 11:01:57 +04:00
{
struct input_dev * dev = to_input_dev ( cdev ) ;
int i = 0 ;
int len = 0 ;
INPUT_ADD_HOTPLUG_VAR ( " PRODUCT=%x/%x/%x/%x " ,
dev - > id . bustype , dev - > id . vendor ,
dev - > id . product , dev - > id . version ) ;
if ( dev - > name )
INPUT_ADD_HOTPLUG_VAR ( " NAME= \" %s \" " , dev - > name ) ;
if ( dev - > phys )
INPUT_ADD_HOTPLUG_VAR ( " PHYS= \" %s \" " , dev - > phys ) ;
2005-11-09 08:34:29 +03:00
if ( dev - > uniq )
2005-09-15 11:01:57 +04:00
INPUT_ADD_HOTPLUG_VAR ( " UNIQ= \" %s \" " , dev - > uniq ) ;
INPUT_ADD_HOTPLUG_BM_VAR ( " EV= " , dev - > evbit , EV_MAX ) ;
if ( test_bit ( EV_KEY , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " KEY= " , dev - > keybit , KEY_MAX ) ;
if ( test_bit ( EV_REL , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " REL= " , dev - > relbit , REL_MAX ) ;
if ( test_bit ( EV_ABS , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " ABS= " , dev - > absbit , ABS_MAX ) ;
if ( test_bit ( EV_MSC , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " MSC= " , dev - > mscbit , MSC_MAX ) ;
if ( test_bit ( EV_LED , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " LED= " , dev - > ledbit , LED_MAX ) ;
if ( test_bit ( EV_SND , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " SND= " , dev - > sndbit , SND_MAX ) ;
if ( test_bit ( EV_FF , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " FF= " , dev - > ffbit , FF_MAX ) ;
if ( test_bit ( EV_SW , dev - > evbit ) )
INPUT_ADD_HOTPLUG_BM_VAR ( " SW= " , dev - > swbit , SW_MAX ) ;
2006-04-02 09:09:51 +04:00
INPUT_ADD_HOTPLUG_MODALIAS_VAR ( dev ) ;
2005-09-15 11:01:57 +04:00
2006-01-05 15:19:55 +03:00
envp [ i ] = NULL ;
2005-09-15 11:01:57 +04:00
return 0 ;
}
2005-10-28 09:25:43 +04:00
struct class input_class = {
. name = " input " ,
2005-09-15 11:01:39 +04:00
. release = input_dev_release ,
2005-11-16 11:00:00 +03:00
. uevent = input_dev_uevent ,
2005-09-15 11:01:39 +04:00
} ;
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL_GPL ( input_class ) ;
2005-09-15 11:01:39 +04:00
2006-11-03 07:26:55 +03:00
/**
* input_allocate_device - allocate memory for new input device
*
* Returns prepared struct input_dev or NULL .
*
* NOTE : Use input_free_device ( ) to free devices that have not been
* registered ; input_unregister_device ( ) should be used for already
* registered devices .
*/
2005-09-15 11:01:39 +04:00
struct input_dev * input_allocate_device ( void )
{
struct input_dev * dev ;
dev = kzalloc ( sizeof ( struct input_dev ) , GFP_KERNEL ) ;
if ( dev ) {
2005-10-28 09:25:43 +04:00
dev - > cdev . class = & input_class ;
2005-09-15 11:01:39 +04:00
class_device_initialize ( & dev - > cdev ) ;
2006-06-26 09:48:36 +04:00
mutex_init ( & dev - > mutex ) ;
2005-09-15 11:01:39 +04:00
INIT_LIST_HEAD ( & dev - > h_list ) ;
INIT_LIST_HEAD ( & dev - > node ) ;
2006-09-14 09:32:14 +04:00
__module_get ( THIS_MODULE ) ;
2005-09-15 11:01:39 +04:00
}
return dev ;
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_allocate_device ) ;
2005-09-15 11:01:39 +04:00
2006-11-03 07:26:55 +03:00
/**
* input_free_device - free memory occupied by input_dev structure
* @ dev : input device to free
*
* This function should only be used if input_register_device ( )
* was not called yet or if it failed . Once device was registered
* use input_unregister_device ( ) and memory will be freed once last
* refrence to the device is dropped .
*
* Device should be allocated by input_allocate_device ( ) .
*
* NOTE : If there are references to the input device then memory
* will not be freed until last reference is dropped .
*/
2006-06-26 09:48:36 +04:00
void input_free_device ( struct input_dev * dev )
{
if ( dev ) {
mutex_lock ( & dev - > mutex ) ;
dev - > name = dev - > phys = dev - > uniq = NULL ;
mutex_unlock ( & dev - > mutex ) ;
input_put_device ( dev ) ;
}
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_free_device ) ;
2006-06-26 09:48:36 +04:00
2005-11-03 06:51:46 +03:00
int input_register_device ( struct input_dev * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-20 08:56:31 +03:00
static atomic_t input_no = ATOMIC_INIT ( 0 ) ;
2005-04-17 02:20:36 +04:00
struct input_handle * handle ;
struct input_handler * handler ;
2006-09-14 09:31:59 +04:00
const struct input_device_id * id ;
2005-11-20 08:56:31 +03:00
const char * path ;
int error ;
2005-04-17 02:20:36 +04:00
2005-11-03 06:51:46 +03:00
set_bit ( EV_SYN , dev - > evbit ) ;
2005-05-29 11:29:25 +04:00
2005-04-17 02:20:36 +04:00
/*
* If delay and period are pre - set by the driver , then autorepeating
* is handled by the driver itself and we don ' t do it in input . c .
*/
init_timer ( & dev - > timer ) ;
if ( ! dev - > rep [ REP_DELAY ] & & ! dev - > rep [ REP_PERIOD ] ) {
dev - > timer . data = ( long ) dev ;
dev - > timer . function = input_repeat_key ;
dev - > rep [ REP_DELAY ] = 250 ;
dev - > rep [ REP_PERIOD ] = 33 ;
}
list_add_tail ( & dev - > node , & input_dev_list ) ;
2005-11-20 08:56:31 +03:00
snprintf ( dev - > cdev . class_id , sizeof ( dev - > cdev . class_id ) ,
" input%ld " , ( unsigned long ) atomic_inc_return ( & input_no ) - 1 ) ;
error = class_device_add ( & dev - > cdev ) ;
if ( error )
return error ;
error = sysfs_create_group ( & dev - > cdev . kobj , & input_dev_attr_group ) ;
if ( error )
goto fail1 ;
error = sysfs_create_group ( & dev - > cdev . kobj , & input_dev_id_attr_group ) ;
if ( error )
goto fail2 ;
error = sysfs_create_group ( & dev - > cdev . kobj , & input_dev_caps_attr_group ) ;
if ( error )
goto fail3 ;
path = kobject_get_path ( & dev - > cdev . kobj , GFP_KERNEL ) ;
printk ( KERN_INFO " input: %s as %s \n " ,
dev - > name ? dev - > name : " Unspecified device " , path ? path : " N/A " ) ;
kfree ( path ) ;
2005-10-28 09:25:43 +04:00
2005-04-17 02:20:36 +04:00
list_for_each_entry ( handler , & input_handler_list , node )
if ( ! handler - > blacklist | | ! input_match_device ( handler - > blacklist , dev ) )
if ( ( id = input_match_device ( handler - > id_table , dev ) ) )
2006-07-06 08:21:03 +04:00
if ( ( handle = handler - > connect ( handler , dev , id ) ) ) {
2005-04-17 02:20:36 +04:00
input_link_handle ( handle ) ;
2006-07-06 08:21:03 +04:00
if ( handler - > start )
handler - > start ( handle ) ;
}
2005-04-17 02:20:36 +04:00
2005-06-30 09:50:29 +04:00
input_wakeup_procfs_readers ( ) ;
2005-11-03 06:51:46 +03:00
return 0 ;
2005-11-20 08:56:31 +03:00
fail3 : sysfs_remove_group ( & dev - > cdev . kobj , & input_dev_id_attr_group ) ;
fail2 : sysfs_remove_group ( & dev - > cdev . kobj , & input_dev_attr_group ) ;
fail1 : class_device_del ( & dev - > cdev ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_register_device ) ;
2005-04-17 02:20:36 +04:00
void input_unregister_device ( struct input_dev * dev )
{
2006-06-26 09:48:47 +04:00
struct list_head * node , * next ;
2006-09-11 05:56:06 +04:00
int code ;
2005-04-17 02:20:36 +04:00
2006-09-11 05:56:06 +04:00
for ( code = 0 ; code < = KEY_MAX ; code + + )
if ( test_bit ( code , dev - > key ) )
input_report_key ( dev , code , 0 ) ;
input_sync ( dev ) ;
2005-04-17 02:20:36 +04:00
del_timer_sync ( & dev - > timer ) ;
list_for_each_safe ( node , next , & dev - > h_list ) {
struct input_handle * handle = to_handle ( node ) ;
list_del_init ( & handle - > d_node ) ;
list_del_init ( & handle - > h_node ) ;
handle - > handler - > disconnect ( handle ) ;
}
list_del_init ( & dev - > node ) ;
2005-11-03 06:51:46 +03:00
sysfs_remove_group ( & dev - > cdev . kobj , & input_dev_caps_attr_group ) ;
sysfs_remove_group ( & dev - > cdev . kobj , & input_dev_id_attr_group ) ;
2005-11-20 08:56:31 +03:00
sysfs_remove_group ( & dev - > cdev . kobj , & input_dev_attr_group ) ;
2005-09-15 11:01:39 +04:00
2006-06-26 09:48:36 +04:00
mutex_lock ( & dev - > mutex ) ;
dev - > name = dev - > phys = dev - > uniq = NULL ;
mutex_unlock ( & dev - > mutex ) ;
2006-06-27 16:30:31 +04:00
class_device_unregister ( & dev - > cdev ) ;
2005-06-30 09:50:29 +04:00
input_wakeup_procfs_readers ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_unregister_device ) ;
2005-04-17 02:20:36 +04:00
2006-09-14 09:32:39 +04:00
int input_register_handler ( struct input_handler * handler )
2005-04-17 02:20:36 +04:00
{
struct input_dev * dev ;
struct input_handle * handle ;
2006-09-14 09:31:59 +04:00
const struct input_device_id * id ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & handler - > h_list ) ;
2006-09-14 09:32:39 +04:00
if ( handler - > fops ! = NULL ) {
if ( input_table [ handler - > minor > > 5 ] )
return - EBUSY ;
2005-04-17 02:20:36 +04:00
input_table [ handler - > minor > > 5 ] = handler ;
2006-09-14 09:32:39 +04:00
}
2005-04-17 02:20:36 +04:00
list_add_tail ( & handler - > node , & input_handler_list ) ;
list_for_each_entry ( dev , & input_dev_list , node )
if ( ! handler - > blacklist | | ! input_match_device ( handler - > blacklist , dev ) )
if ( ( id = input_match_device ( handler - > id_table , dev ) ) )
2006-07-19 09:08:51 +04:00
if ( ( handle = handler - > connect ( handler , dev , id ) ) ) {
2005-04-17 02:20:36 +04:00
input_link_handle ( handle ) ;
2006-07-19 09:08:51 +04:00
if ( handler - > start )
handler - > start ( handle ) ;
}
2005-04-17 02:20:36 +04:00
2005-06-30 09:50:29 +04:00
input_wakeup_procfs_readers ( ) ;
2006-09-14 09:32:39 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_register_handler ) ;
2005-04-17 02:20:36 +04:00
void input_unregister_handler ( struct input_handler * handler )
{
2006-06-26 09:48:47 +04:00
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
list_for_each_safe ( node , next , & handler - > h_list ) {
struct input_handle * handle = to_handle_h ( node ) ;
list_del_init ( & handle - > h_node ) ;
list_del_init ( & handle - > d_node ) ;
handler - > disconnect ( handle ) ;
}
list_del_init ( & handler - > node ) ;
if ( handler - > fops ! = NULL )
input_table [ handler - > minor > > 5 ] = NULL ;
2005-06-30 09:50:29 +04:00
input_wakeup_procfs_readers ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 09:49:21 +04:00
EXPORT_SYMBOL ( input_unregister_handler ) ;
2005-04-17 02:20:36 +04:00
static int input_open_file ( struct inode * inode , struct file * file )
{
struct input_handler * handler = input_table [ iminor ( inode ) > > 5 ] ;
2006-03-28 13:56:41 +04:00
const struct file_operations * old_fops , * new_fops = NULL ;
2005-04-17 02:20:36 +04:00
int err ;
/* No load-on-demand here? */
if ( ! handler | | ! ( new_fops = fops_get ( handler - > fops ) ) )
return - ENODEV ;
/*
* That ' s _really_ odd . Usually NULL - > open means " nothing special " ,
* not " no device " . Oh , well . . .
*/
if ( ! new_fops - > open ) {
fops_put ( new_fops ) ;
return - ENODEV ;
}
old_fops = file - > f_op ;
file - > f_op = new_fops ;
err = new_fops - > open ( inode , file ) ;
if ( err ) {
fops_put ( file - > f_op ) ;
file - > f_op = fops_get ( old_fops ) ;
}
fops_put ( old_fops ) ;
return err ;
}
static struct file_operations input_fops = {
. owner = THIS_MODULE ,
. open = input_open_file ,
} ;
2005-06-30 09:50:29 +04:00
static int __init input_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-06-30 09:50:29 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2005-10-28 09:25:43 +04:00
err = class_register ( & input_class ) ;
2005-09-15 11:01:39 +04:00
if ( err ) {
printk ( KERN_ERR " input: unable to register input_dev class \n " ) ;
return err ;
}
2005-06-30 09:50:29 +04:00
err = input_proc_init ( ) ;
if ( err )
2005-10-28 09:25:43 +04:00
goto fail1 ;
2005-04-17 02:20:36 +04:00
2005-06-30 09:50:29 +04:00
err = register_chrdev ( INPUT_MAJOR , " input " , & input_fops ) ;
if ( err ) {
printk ( KERN_ERR " input: unable to register char major %d " , INPUT_MAJOR ) ;
2005-10-28 09:25:43 +04:00
goto fail2 ;
2005-04-17 02:20:36 +04:00
}
2005-06-01 11:39:28 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
2005-10-28 09:25:43 +04:00
fail2 : input_proc_exit ( ) ;
2005-10-28 09:25:43 +04:00
fail1 : class_unregister ( & input_class ) ;
2005-06-30 09:50:29 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit input_exit ( void )
{
2005-06-30 09:50:29 +04:00
input_proc_exit ( ) ;
2005-04-17 02:20:36 +04:00
unregister_chrdev ( INPUT_MAJOR , " input " ) ;
2005-10-28 09:25:43 +04:00
class_unregister ( & input_class ) ;
2005-04-17 02:20:36 +04:00
}
subsys_initcall ( input_init ) ;
module_exit ( input_exit ) ;