2005-04-17 02:20:36 +04:00
/*
* Input driver to ExplorerPS / 2 device driver module .
*
* Copyright ( c ) 1999 - 2002 Vojtech Pavlik
* Copyright ( c ) 2004 Dmitry Torokhov
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*/
2005-09-15 11:01:38 +04:00
# define MOUSEDEV_MINOR_BASE 32
2005-04-17 02:20:36 +04:00
# define MOUSEDEV_MINORS 32
# define MOUSEDEV_MIX 31
# include <linux/slab.h>
# include <linux/poll.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/smp_lock.h>
# include <linux/random.h>
# include <linux/major.h>
# include <linux/device.h>
# ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
# include <linux/miscdevice.h>
# endif
MODULE_AUTHOR ( " Vojtech Pavlik <vojtech@ucw.cz> " ) ;
MODULE_DESCRIPTION ( " Mouse (ExplorerPS/2) device interfaces " ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
# define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
# endif
# ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
# define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
# endif
static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X ;
2005-12-11 20:41:03 +03:00
module_param ( xres , uint , 0644 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( xres , " Horizontal screen resolution " ) ;
static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y ;
2005-12-11 20:41:03 +03:00
module_param ( yres , uint , 0644 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( yres , " Vertical screen resolution " ) ;
static unsigned tap_time = 200 ;
2005-12-11 20:41:03 +03:00
module_param ( tap_time , uint , 0644 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( tap_time , " Tap time for touchpads in absolute mode (msecs) " ) ;
struct mousedev_hw_data {
int dx , dy , dz ;
int x , y ;
int abs_event ;
unsigned long buttons ;
} ;
struct mousedev {
int exist ;
int open ;
int minor ;
char name [ 16 ] ;
wait_queue_head_t wait ;
struct list_head list ;
struct input_handle handle ;
struct mousedev_hw_data packet ;
unsigned int pkt_count ;
int old_x [ 4 ] , old_y [ 4 ] ;
int frac_dx , frac_dy ;
unsigned long touch ;
} ;
enum mousedev_emul {
MOUSEDEV_EMUL_PS2 ,
MOUSEDEV_EMUL_IMPS ,
MOUSEDEV_EMUL_EXPS
} ;
struct mousedev_motion {
int dx , dy , dz ;
unsigned long buttons ;
} ;
# define PACKET_QUEUE_LEN 16
struct mousedev_list {
struct fasync_struct * fasync ;
struct mousedev * mousedev ;
struct list_head node ;
struct mousedev_motion packets [ PACKET_QUEUE_LEN ] ;
unsigned int head , tail ;
spinlock_t packet_lock ;
int pos_x , pos_y ;
signed char ps2 [ 6 ] ;
unsigned char ready , buffer , bufsiz ;
unsigned char imexseq , impsseq ;
enum mousedev_emul mode ;
2005-05-27 23:53:03 +04:00
unsigned long last_buttons ;
2005-04-17 02:20:36 +04:00
} ;
# define MOUSEDEV_SEQ_LEN 6
static unsigned char mousedev_imps_seq [ ] = { 0xf3 , 200 , 0xf3 , 100 , 0xf3 , 80 } ;
static unsigned char mousedev_imex_seq [ ] = { 0xf3 , 200 , 0xf3 , 200 , 0xf3 , 80 } ;
static struct input_handler mousedev_handler ;
static struct mousedev * mousedev_table [ MOUSEDEV_MINORS ] ;
static struct mousedev mousedev_mix ;
# define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
# define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
static void mousedev_touchpad_event ( struct input_dev * dev , struct mousedev * mousedev , unsigned int code , int value )
{
int size , tmp ;
enum { FRACTION_DENOM = 128 } ;
if ( mousedev - > touch ) {
size = dev - > absmax [ ABS_X ] - dev - > absmin [ ABS_X ] ;
2006-06-26 09:48:47 +04:00
if ( size = = 0 )
size = 256 * 2 ;
2005-04-17 02:20:36 +04:00
switch ( code ) {
case ABS_X :
fx ( 0 ) = value ;
if ( mousedev - > pkt_count > = 2 ) {
tmp = ( ( value - fx ( 2 ) ) * ( 256 * FRACTION_DENOM ) ) / size ;
tmp + = mousedev - > frac_dx ;
mousedev - > packet . dx = tmp / FRACTION_DENOM ;
mousedev - > frac_dx = tmp - mousedev - > packet . dx * FRACTION_DENOM ;
}
break ;
case ABS_Y :
fy ( 0 ) = value ;
if ( mousedev - > pkt_count > = 2 ) {
tmp = - ( ( value - fy ( 2 ) ) * ( 256 * FRACTION_DENOM ) ) / size ;
tmp + = mousedev - > frac_dy ;
mousedev - > packet . dy = tmp / FRACTION_DENOM ;
mousedev - > frac_dy = tmp - mousedev - > packet . dy * FRACTION_DENOM ;
}
break ;
}
}
}
static void mousedev_abs_event ( struct input_dev * dev , struct mousedev * mousedev , unsigned int code , int value )
{
int size ;
switch ( code ) {
case ABS_X :
size = dev - > absmax [ ABS_X ] - dev - > absmin [ ABS_X ] ;
2006-06-26 09:48:47 +04:00
if ( size = = 0 )
size = xres ? : 1 ;
if ( value > dev - > absmax [ ABS_X ] )
value = dev - > absmax [ ABS_X ] ;
if ( value < dev - > absmin [ ABS_X ] )
value = dev - > absmin [ ABS_X ] ;
2005-04-17 02:20:36 +04:00
mousedev - > packet . x = ( ( value - dev - > absmin [ ABS_X ] ) * xres ) / size ;
mousedev - > packet . abs_event = 1 ;
break ;
case ABS_Y :
size = dev - > absmax [ ABS_Y ] - dev - > absmin [ ABS_Y ] ;
2006-06-26 09:48:47 +04:00
if ( size = = 0 )
size = yres ? : 1 ;
if ( value > dev - > absmax [ ABS_Y ] )
value = dev - > absmax [ ABS_Y ] ;
if ( value < dev - > absmin [ ABS_Y ] )
value = dev - > absmin [ ABS_Y ] ;
2005-04-17 02:20:36 +04:00
mousedev - > packet . y = yres - ( ( value - dev - > absmin [ ABS_Y ] ) * yres ) / size ;
mousedev - > packet . abs_event = 1 ;
break ;
}
}
static void mousedev_rel_event ( struct mousedev * mousedev , unsigned int code , int value )
{
switch ( code ) {
case REL_X : mousedev - > packet . dx + = value ; break ;
case REL_Y : mousedev - > packet . dy - = value ; break ;
case REL_WHEEL : mousedev - > packet . dz - = value ; break ;
}
}
static void mousedev_key_event ( struct mousedev * mousedev , unsigned int code , int value )
{
int index ;
switch ( code ) {
case BTN_TOUCH :
case BTN_0 :
case BTN_FORWARD :
case BTN_LEFT : index = 0 ; break ;
case BTN_STYLUS :
case BTN_1 :
case BTN_RIGHT : index = 1 ; break ;
case BTN_2 :
case BTN_STYLUS2 :
case BTN_MIDDLE : index = 2 ; break ;
case BTN_3 :
case BTN_BACK :
case BTN_SIDE : index = 3 ; break ;
case BTN_4 :
case BTN_EXTRA : index = 4 ; break ;
2006-06-26 09:48:47 +04:00
default : return ;
2005-04-17 02:20:36 +04:00
}
if ( value ) {
set_bit ( index , & mousedev - > packet . buttons ) ;
set_bit ( index , & mousedev_mix . packet . buttons ) ;
} else {
clear_bit ( index , & mousedev - > packet . buttons ) ;
clear_bit ( index , & mousedev_mix . packet . buttons ) ;
}
}
static void mousedev_notify_readers ( struct mousedev * mousedev , struct mousedev_hw_data * packet )
{
struct mousedev_list * list ;
struct mousedev_motion * p ;
unsigned long flags ;
2005-06-01 11:39:36 +04:00
int wake_readers = 0 ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( list , & mousedev - > list , node ) {
spin_lock_irqsave ( & list - > packet_lock , flags ) ;
p = & list - > packets [ list - > head ] ;
2005-05-27 23:53:03 +04:00
if ( list - > ready & & p - > buttons ! = mousedev - > packet . buttons ) {
2005-04-17 02:20:36 +04:00
unsigned int new_head = ( list - > head + 1 ) % PACKET_QUEUE_LEN ;
if ( new_head ! = list - > tail ) {
p = & list - > packets [ list - > head = new_head ] ;
memset ( p , 0 , sizeof ( struct mousedev_motion ) ) ;
}
}
if ( packet - > abs_event ) {
p - > dx + = packet - > x - list - > pos_x ;
p - > dy + = packet - > y - list - > pos_y ;
list - > pos_x = packet - > x ;
list - > pos_y = packet - > y ;
}
list - > pos_x + = packet - > dx ;
list - > pos_x = list - > pos_x < 0 ? 0 : ( list - > pos_x > = xres ? xres : list - > pos_x ) ;
list - > pos_y + = packet - > dy ;
list - > pos_y = list - > pos_y < 0 ? 0 : ( list - > pos_y > = yres ? yres : list - > pos_y ) ;
p - > dx + = packet - > dx ;
p - > dy + = packet - > dy ;
p - > dz + = packet - > dz ;
p - > buttons = mousedev - > packet . buttons ;
2005-05-27 23:53:03 +04:00
if ( p - > dx | | p - > dy | | p - > dz | | p - > buttons ! = list - > last_buttons )
list - > ready = 1 ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & list - > packet_lock , flags ) ;
2005-05-27 23:53:03 +04:00
2005-06-01 11:39:36 +04:00
if ( list - > ready ) {
2005-05-27 23:53:03 +04:00
kill_fasync ( & list - > fasync , SIGIO , POLL_IN ) ;
2005-06-01 11:39:36 +04:00
wake_readers = 1 ;
}
2005-04-17 02:20:36 +04:00
}
2005-06-01 11:39:36 +04:00
if ( wake_readers )
wake_up_interruptible ( & mousedev - > wait ) ;
2005-04-17 02:20:36 +04:00
}
static void mousedev_touchpad_touch ( struct mousedev * mousedev , int value )
{
if ( ! value ) {
if ( mousedev - > touch & &
time_before ( jiffies , mousedev - > touch + msecs_to_jiffies ( tap_time ) ) ) {
/*
* Toggle left button to emulate tap .
* We rely on the fact that mousedev_mix always has 0
* motion packet so we won ' t mess current position .
*/
set_bit ( 0 , & mousedev - > packet . buttons ) ;
set_bit ( 0 , & mousedev_mix . packet . buttons ) ;
mousedev_notify_readers ( mousedev , & mousedev_mix . packet ) ;
mousedev_notify_readers ( & mousedev_mix , & mousedev_mix . packet ) ;
clear_bit ( 0 , & mousedev - > packet . buttons ) ;
clear_bit ( 0 , & mousedev_mix . packet . buttons ) ;
}
mousedev - > touch = mousedev - > pkt_count = 0 ;
mousedev - > frac_dx = 0 ;
mousedev - > frac_dy = 0 ;
2006-06-26 09:48:47 +04:00
} else if ( ! mousedev - > touch )
mousedev - > touch = jiffies ;
2005-04-17 02:20:36 +04:00
}
static void mousedev_event ( struct input_handle * handle , unsigned int type , unsigned int code , int value )
{
struct mousedev * mousedev = handle - > private ;
switch ( type ) {
case EV_ABS :
/* Ignore joysticks */
if ( test_bit ( BTN_TRIGGER , handle - > dev - > keybit ) )
return ;
if ( test_bit ( BTN_TOOL_FINGER , handle - > dev - > keybit ) )
mousedev_touchpad_event ( handle - > dev , mousedev , code , value ) ;
else
mousedev_abs_event ( handle - > dev , mousedev , code , value ) ;
break ;
case EV_REL :
mousedev_rel_event ( mousedev , code , value ) ;
break ;
case EV_KEY :
if ( value ! = 2 ) {
if ( code = = BTN_TOUCH & & test_bit ( BTN_TOOL_FINGER , handle - > dev - > keybit ) )
mousedev_touchpad_touch ( mousedev , value ) ;
else
mousedev_key_event ( mousedev , code , value ) ;
}
break ;
case EV_SYN :
if ( code = = SYN_REPORT ) {
if ( mousedev - > touch ) {
mousedev - > pkt_count + + ;
/* Input system eats duplicate events, but we need all of them
* to do correct averaging so apply present one forward
2006-06-26 09:48:47 +04:00
*/
2005-04-17 02:20:36 +04:00
fx ( 0 ) = fx ( 1 ) ;
fy ( 0 ) = fy ( 1 ) ;
}
mousedev_notify_readers ( mousedev , & mousedev - > packet ) ;
mousedev_notify_readers ( & mousedev_mix , & mousedev - > packet ) ;
mousedev - > packet . dx = mousedev - > packet . dy = mousedev - > packet . dz = 0 ;
mousedev - > packet . abs_event = 0 ;
}
break ;
}
}
static int mousedev_fasync ( int fd , struct file * file , int on )
{
int retval ;
struct mousedev_list * list = file - > private_data ;
2006-06-26 09:48:47 +04:00
2005-04-17 02:20:36 +04:00
retval = fasync_helper ( fd , file , on , & list - > fasync ) ;
2006-06-26 09:48:47 +04:00
2005-04-17 02:20:36 +04:00
return retval < 0 ? retval : 0 ;
}
static void mousedev_free ( struct mousedev * mousedev )
{
mousedev_table [ mousedev - > minor ] = NULL ;
kfree ( mousedev ) ;
}
2006-01-30 05:50:59 +03:00
static void mixdev_release ( void )
2005-04-17 02:20:36 +04:00
{
struct input_handle * handle ;
list_for_each_entry ( handle , & mousedev_handler . h_list , h_node ) {
struct mousedev * mousedev = handle - > private ;
if ( ! mousedev - > open ) {
if ( mousedev - > exist )
input_close_device ( & mousedev - > handle ) ;
else
mousedev_free ( mousedev ) ;
}
}
}
static int mousedev_release ( struct inode * inode , struct file * file )
{
struct mousedev_list * list = file - > private_data ;
mousedev_fasync ( - 1 , file , 0 ) ;
list_del ( & list - > node ) ;
if ( ! - - list - > mousedev - > open ) {
if ( list - > mousedev - > minor = = MOUSEDEV_MIX )
2006-01-30 05:50:59 +03:00
mixdev_release ( ) ;
else if ( ! mousedev_mix . open ) {
2005-04-17 02:20:36 +04:00
if ( list - > mousedev - > exist )
input_close_device ( & list - > mousedev - > handle ) ;
else
mousedev_free ( list - > mousedev ) ;
}
}
kfree ( list ) ;
return 0 ;
}
static int mousedev_open ( struct inode * inode , struct file * file )
{
struct mousedev_list * list ;
struct input_handle * handle ;
struct mousedev * mousedev ;
int i ;
# ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if ( imajor ( inode ) = = MISC_MAJOR )
i = MOUSEDEV_MIX ;
else
# endif
i = iminor ( inode ) - MOUSEDEV_MINOR_BASE ;
if ( i > = MOUSEDEV_MINORS | | ! mousedev_table [ i ] )
return - ENODEV ;
2006-03-14 08:09:16 +03:00
if ( ! ( list = kzalloc ( sizeof ( struct mousedev_list ) , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
spin_lock_init ( & list - > packet_lock ) ;
list - > pos_x = xres / 2 ;
list - > pos_y = yres / 2 ;
list - > mousedev = mousedev_table [ i ] ;
list_add_tail ( & list - > node , & mousedev_table [ i ] - > list ) ;
file - > private_data = list ;
if ( ! list - > mousedev - > open + + ) {
if ( list - > mousedev - > minor = = MOUSEDEV_MIX ) {
list_for_each_entry ( handle , & mousedev_handler . h_list , h_node ) {
mousedev = handle - > private ;
if ( ! mousedev - > open & & mousedev - > exist )
input_open_device ( handle ) ;
}
} else
if ( ! mousedev_mix . open & & list - > mousedev - > exist )
input_open_device ( & list - > mousedev - > handle ) ;
}
return 0 ;
}
static inline int mousedev_limit_delta ( int delta , int limit )
{
return delta > limit ? limit : ( delta < - limit ? - limit : delta ) ;
}
static void mousedev_packet ( struct mousedev_list * list , signed char * ps2_data )
{
struct mousedev_motion * p ;
unsigned long flags ;
spin_lock_irqsave ( & list - > packet_lock , flags ) ;
p = & list - > packets [ list - > tail ] ;
ps2_data [ 0 ] = 0x08 | ( ( p - > dx < 0 ) < < 4 ) | ( ( p - > dy < 0 ) < < 5 ) | ( p - > buttons & 0x07 ) ;
ps2_data [ 1 ] = mousedev_limit_delta ( p - > dx , 127 ) ;
ps2_data [ 2 ] = mousedev_limit_delta ( p - > dy , 127 ) ;
p - > dx - = ps2_data [ 1 ] ;
p - > dy - = ps2_data [ 2 ] ;
switch ( list - > mode ) {
case MOUSEDEV_EMUL_EXPS :
ps2_data [ 3 ] = mousedev_limit_delta ( p - > dz , 7 ) ;
p - > dz - = ps2_data [ 3 ] ;
ps2_data [ 3 ] = ( ps2_data [ 3 ] & 0x0f ) | ( ( p - > buttons & 0x18 ) < < 1 ) ;
list - > bufsiz = 4 ;
break ;
case MOUSEDEV_EMUL_IMPS :
ps2_data [ 0 ] | = ( ( p - > buttons & 0x10 ) > > 3 ) | ( ( p - > buttons & 0x08 ) > > 1 ) ;
ps2_data [ 3 ] = mousedev_limit_delta ( p - > dz , 127 ) ;
p - > dz - = ps2_data [ 3 ] ;
list - > bufsiz = 4 ;
break ;
case MOUSEDEV_EMUL_PS2 :
default :
ps2_data [ 0 ] | = ( ( p - > buttons & 0x10 ) > > 3 ) | ( ( p - > buttons & 0x08 ) > > 1 ) ;
p - > dz = 0 ;
list - > bufsiz = 3 ;
break ;
}
if ( ! p - > dx & & ! p - > dy & & ! p - > dz ) {
2005-05-27 23:53:03 +04:00
if ( list - > tail = = list - > head ) {
2005-04-17 02:20:36 +04:00
list - > ready = 0 ;
2005-05-27 23:53:03 +04:00
list - > last_buttons = p - > buttons ;
} else
2005-04-17 02:20:36 +04:00
list - > tail = ( list - > tail + 1 ) % PACKET_QUEUE_LEN ;
}
spin_unlock_irqrestore ( & list - > packet_lock , flags ) ;
}
static ssize_t mousedev_write ( struct file * file , const char __user * buffer , size_t count , loff_t * ppos )
{
struct mousedev_list * list = file - > private_data ;
unsigned char c ;
unsigned int i ;
for ( i = 0 ; i < count ; i + + ) {
if ( get_user ( c , buffer + i ) )
return - EFAULT ;
if ( c = = mousedev_imex_seq [ list - > imexseq ] ) {
if ( + + list - > imexseq = = MOUSEDEV_SEQ_LEN ) {
list - > imexseq = 0 ;
list - > mode = MOUSEDEV_EMUL_EXPS ;
}
2006-06-26 09:48:47 +04:00
} else
list - > imexseq = 0 ;
2005-04-17 02:20:36 +04:00
if ( c = = mousedev_imps_seq [ list - > impsseq ] ) {
if ( + + list - > impsseq = = MOUSEDEV_SEQ_LEN ) {
list - > impsseq = 0 ;
list - > mode = MOUSEDEV_EMUL_IMPS ;
}
2006-06-26 09:48:47 +04:00
} else
list - > impsseq = 0 ;
2005-04-17 02:20:36 +04:00
list - > ps2 [ 0 ] = 0xfa ;
switch ( c ) {
case 0xeb : /* Poll */
mousedev_packet ( list , & list - > ps2 [ 1 ] ) ;
list - > bufsiz + + ; /* account for leading ACK */
break ;
case 0xf2 : /* Get ID */
switch ( list - > mode ) {
case MOUSEDEV_EMUL_PS2 : list - > ps2 [ 1 ] = 0 ; break ;
case MOUSEDEV_EMUL_IMPS : list - > ps2 [ 1 ] = 3 ; break ;
case MOUSEDEV_EMUL_EXPS : list - > ps2 [ 1 ] = 4 ; break ;
}
list - > bufsiz = 2 ;
break ;
case 0xe9 : /* Get info */
list - > ps2 [ 1 ] = 0x60 ; list - > ps2 [ 2 ] = 3 ; list - > ps2 [ 3 ] = 200 ;
list - > bufsiz = 4 ;
break ;
case 0xff : /* Reset */
list - > impsseq = list - > imexseq = 0 ;
list - > mode = MOUSEDEV_EMUL_PS2 ;
list - > ps2 [ 1 ] = 0xaa ; list - > ps2 [ 2 ] = 0x00 ;
list - > bufsiz = 3 ;
break ;
default :
list - > bufsiz = 1 ;
break ;
}
list - > buffer = list - > bufsiz ;
}
kill_fasync ( & list - > fasync , SIGIO , POLL_IN ) ;
wake_up_interruptible ( & list - > mousedev - > wait ) ;
return count ;
}
static ssize_t mousedev_read ( struct file * file , char __user * buffer , size_t count , loff_t * ppos )
{
struct mousedev_list * list = file - > private_data ;
int retval = 0 ;
if ( ! list - > ready & & ! list - > buffer & & ( file - > f_flags & O_NONBLOCK ) )
return - EAGAIN ;
retval = wait_event_interruptible ( list - > mousedev - > wait ,
! list - > mousedev - > exist | | list - > ready | | list - > buffer ) ;
if ( retval )
return retval ;
if ( ! list - > mousedev - > exist )
return - ENODEV ;
if ( ! list - > buffer & & list - > ready ) {
mousedev_packet ( list , list - > ps2 ) ;
list - > buffer = list - > bufsiz ;
}
if ( count > list - > buffer )
count = list - > buffer ;
list - > buffer - = count ;
if ( copy_to_user ( buffer , list - > ps2 + list - > bufsiz - list - > buffer - count , count ) )
return - EFAULT ;
return count ;
}
/* No kernel lock - fine */
static unsigned int mousedev_poll ( struct file * file , poll_table * wait )
{
struct mousedev_list * list = file - > private_data ;
2006-06-26 09:48:47 +04:00
2005-04-17 02:20:36 +04:00
poll_wait ( file , & list - > mousedev - > wait , wait ) ;
return ( ( list - > ready | | list - > buffer ) ? ( POLLIN | POLLRDNORM ) : 0 ) |
( list - > mousedev - > exist ? 0 : ( POLLHUP | POLLERR ) ) ;
}
2006-09-14 09:31:59 +04:00
static const struct file_operations mousedev_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. read = mousedev_read ,
. write = mousedev_write ,
. poll = mousedev_poll ,
. open = mousedev_open ,
. release = mousedev_release ,
. fasync = mousedev_fasync ,
} ;
2006-09-14 09:31:59 +04:00
static struct input_handle * mousedev_connect ( struct input_handler * handler , struct input_dev * dev ,
const struct input_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct mousedev * mousedev ;
2005-10-28 09:25:43 +04:00
struct class_device * cdev ;
2005-04-17 02:20:36 +04:00
int minor = 0 ;
for ( minor = 0 ; minor < MOUSEDEV_MINORS & & mousedev_table [ minor ] ; minor + + ) ;
if ( minor = = MOUSEDEV_MINORS ) {
printk ( KERN_ERR " mousedev: no more free mousedev devices \n " ) ;
return NULL ;
}
2006-03-14 08:09:16 +03:00
if ( ! ( mousedev = kzalloc ( sizeof ( struct mousedev ) , GFP_KERNEL ) ) )
2005-04-17 02:20:36 +04:00
return NULL ;
INIT_LIST_HEAD ( & mousedev - > list ) ;
init_waitqueue_head ( & mousedev - > wait ) ;
mousedev - > minor = minor ;
mousedev - > exist = 1 ;
mousedev - > handle . dev = dev ;
mousedev - > handle . name = mousedev - > name ;
mousedev - > handle . handler = handler ;
mousedev - > handle . private = mousedev ;
sprintf ( mousedev - > name , " mouse%d " , minor ) ;
if ( mousedev_mix . open )
input_open_device ( & mousedev - > handle ) ;
mousedev_table [ minor ] = mousedev ;
2005-10-28 09:25:43 +04:00
cdev = class_device_create ( & input_class , & dev - > cdev ,
2005-03-16 01:26:30 +03:00
MKDEV ( INPUT_MAJOR , MOUSEDEV_MINOR_BASE + minor ) ,
2005-10-28 09:25:43 +04:00
dev - > cdev . dev , mousedev - > name ) ;
/* temporary symlink to keep userspace happy */
sysfs_create_link ( & input_class . subsys . kset . kobj , & cdev - > kobj ,
mousedev - > name ) ;
2005-04-17 02:20:36 +04:00
return & mousedev - > handle ;
}
static void mousedev_disconnect ( struct input_handle * handle )
{
struct mousedev * mousedev = handle - > private ;
struct mousedev_list * list ;
2005-10-28 09:25:43 +04:00
sysfs_remove_link ( & input_class . subsys . kset . kobj , mousedev - > name ) ;
2005-10-28 09:25:43 +04:00
class_device_destroy ( & input_class ,
2005-03-16 01:26:30 +03:00
MKDEV ( INPUT_MAJOR , MOUSEDEV_MINOR_BASE + mousedev - > minor ) ) ;
2005-04-17 02:20:36 +04:00
mousedev - > exist = 0 ;
if ( mousedev - > open ) {
input_close_device ( handle ) ;
wake_up_interruptible ( & mousedev - > wait ) ;
list_for_each_entry ( list , & mousedev - > list , node )
kill_fasync ( & list - > fasync , SIGIO , POLL_HUP ) ;
} else {
if ( mousedev_mix . open )
input_close_device ( handle ) ;
mousedev_free ( mousedev ) ;
}
}
2006-09-14 09:31:59 +04:00
static const struct input_device_id mousedev_ids [ ] = {
2005-04-17 02:20:36 +04:00
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT ,
. evbit = { BIT ( EV_KEY ) | BIT ( EV_REL ) } ,
. keybit = { [ LONG ( BTN_LEFT ) ] = BIT ( BTN_LEFT ) } ,
. relbit = { BIT ( REL_X ) | BIT ( REL_Y ) } ,
} , /* A mouse like device, at least one button, two relative axes */
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT ,
. evbit = { BIT ( EV_KEY ) | BIT ( EV_REL ) } ,
. relbit = { BIT ( REL_WHEEL ) } ,
} , /* A separate scrollwheel */
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT ,
. evbit = { BIT ( EV_KEY ) | BIT ( EV_ABS ) } ,
. keybit = { [ LONG ( BTN_TOUCH ) ] = BIT ( BTN_TOUCH ) } ,
. absbit = { BIT ( ABS_X ) | BIT ( ABS_Y ) } ,
} , /* A tablet like device, at least touch detection, two absolute axes */
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT ,
. evbit = { BIT ( EV_KEY ) | BIT ( EV_ABS ) } ,
. keybit = { [ LONG ( BTN_TOOL_FINGER ) ] = BIT ( BTN_TOOL_FINGER ) } ,
. absbit = { BIT ( ABS_X ) | BIT ( ABS_Y ) | BIT ( ABS_PRESSURE ) | BIT ( ABS_TOOL_WIDTH ) } ,
} , /* A touchpad */
{ } , /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( input , mousedev_ids ) ;
static struct input_handler mousedev_handler = {
. event = mousedev_event ,
. connect = mousedev_connect ,
. disconnect = mousedev_disconnect ,
. fops = & mousedev_fops ,
. minor = MOUSEDEV_MINOR_BASE ,
. name = " mousedev " ,
. id_table = mousedev_ids ,
} ;
# ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
static struct miscdevice psaux_mouse = {
PSMOUSE_MINOR , " psaux " , & mousedev_fops
} ;
static int psaux_registered ;
# endif
static int __init mousedev_init ( void )
{
2006-09-14 09:32:39 +04:00
struct class_device * cdev ;
int error ;
error = input_register_handler ( & mousedev_handler ) ;
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
memset ( & mousedev_mix , 0 , sizeof ( struct mousedev ) ) ;
INIT_LIST_HEAD ( & mousedev_mix . list ) ;
init_waitqueue_head ( & mousedev_mix . wait ) ;
mousedev_table [ MOUSEDEV_MIX ] = & mousedev_mix ;
mousedev_mix . exist = 1 ;
mousedev_mix . minor = MOUSEDEV_MIX ;
2006-09-14 09:32:39 +04:00
cdev = class_device_create ( & input_class , NULL ,
2005-03-16 01:26:30 +03:00
MKDEV ( INPUT_MAJOR , MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX ) , NULL , " mice " ) ;
2006-09-14 09:32:39 +04:00
if ( IS_ERR ( cdev ) ) {
input_unregister_handler ( & mousedev_handler ) ;
return PTR_ERR ( cdev ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
2006-09-14 09:32:39 +04:00
error = misc_register ( & psaux_mouse ) ;
if ( error )
printk ( KERN_WARNING " mice: could not register psaux device, "
" error: %d \n " , error ) ;
else
psaux_registered = 1 ;
2005-04-17 02:20:36 +04:00
# endif
printk ( KERN_INFO " mice: PS/2 mouse device common for all mice \n " ) ;
return 0 ;
}
static void __exit mousedev_exit ( void )
{
# ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
if ( psaux_registered )
misc_deregister ( & psaux_mouse ) ;
# endif
2005-10-28 09:25:43 +04:00
class_device_destroy ( & input_class ,
2005-03-16 01:26:30 +03:00
MKDEV ( INPUT_MAJOR , MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX ) ) ;
2005-04-17 02:20:36 +04:00
input_unregister_handler ( & mousedev_handler ) ;
}
module_init ( mousedev_init ) ;
module_exit ( mousedev_exit ) ;