2005-04-16 15:20:36 -07:00
/*
* $ Id : tsdev . c , v 1.15 2002 / 04 / 10 16 : 50 : 19 jsimmons Exp $
*
* Copyright ( c ) 2001 " Crazy " james Simmons
*
* Compaq touchscreen protocol driver . The protocol emulated by this driver
* is obsolete ; for new programs use the tslib library which can read directly
* from evdev and perform dejittering , variance filtering and calibration -
* all in user space , not at kernel level . The meaning of this driver is
* to allow usage of newer input drivers with old applications that use the
* old / dev / h3600_ts and / dev / h3600_tsraw devices .
*
* 09 - Apr - 2004 : Andrew Zabolotny < zap @ homelink . ru >
* Fixed to actually work , not just output random numbers .
* Added support for both h3600_ts and h3600_tsraw protocol
* emulation .
*/
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Should you need to contact me , the author , you can do so either by
* e - mail - mail your message to < jsimmons @ infradead . org > .
*/
2006-06-26 01:48:47 -04:00
# define TSDEV_MINOR_BASE 128
2005-04-16 15:20:36 -07:00
# define TSDEV_MINORS 32
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
# define TSDEV_MINOR_MASK 15
# define TSDEV_BUFFER_SIZE 64
# 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/major.h>
# include <linux/random.h>
# include <linux/time.h>
# include <linux/device.h>
# ifndef CONFIG_INPUT_TSDEV_SCREEN_X
# define CONFIG_INPUT_TSDEV_SCREEN_X 240
# endif
# ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
# define CONFIG_INPUT_TSDEV_SCREEN_Y 320
# endif
/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw
* devices . The first one must output X / Y data in ' cooked ' format , e . g .
* filtered , dejittered and calibrated . Second device just outputs raw
* data received from the hardware .
*
* This driver doesn ' t support filtering and dejittering ; it supports only
* calibration . Filtering and dejittering must be done in the low - level
* driver , if needed , because it may gain additional benefits from knowing
* the low - level details , the nature of noise and so on .
*
* The driver precomputes a calibration matrix given the initial xres and
* yres values ( quite innacurate for most touchscreens ) that will result
* in a more or less expected range of output values . The driver supports
* the TS_SET_CAL ioctl , which will replace the calibration matrix with a
* new one , supposedly generated from the values taken from the raw device .
*/
MODULE_AUTHOR ( " James Simmons <jsimmons@transvirtual.com> " ) ;
MODULE_DESCRIPTION ( " Input driver to touchscreen converter " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int xres = CONFIG_INPUT_TSDEV_SCREEN_X ;
module_param ( xres , uint , 0 ) ;
MODULE_PARM_DESC ( xres , " Horizontal screen resolution (can be negative for X-mirror) " ) ;
static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y ;
module_param ( yres , uint , 0 ) ;
MODULE_PARM_DESC ( yres , " Vertical screen resolution (can be negative for Y-mirror) " ) ;
/* From Compaq's Touch Screen Specification version 0.2 (draft) */
struct ts_event {
short pressure ;
short x ;
short y ;
short millisecs ;
} ;
struct ts_calibration {
int xscale ;
int xtrans ;
int yscale ;
int ytrans ;
int xyswap ;
} ;
struct tsdev {
int exist ;
int open ;
int minor ;
char name [ 8 ] ;
2007-06-14 23:32:24 -04:00
struct input_handle handle ;
2005-04-16 15:20:36 -07:00
wait_queue_head_t wait ;
2007-04-12 01:30:00 -04:00
struct list_head client_list ;
2007-06-14 23:32:24 -04:00
struct device dev ;
2005-04-16 15:20:36 -07:00
int x , y , pressure ;
struct ts_calibration cal ;
} ;
2007-04-12 01:30:00 -04:00
struct tsdev_client {
2005-04-16 15:20:36 -07:00
struct fasync_struct * fasync ;
struct list_head node ;
struct tsdev * tsdev ;
int head , tail ;
struct ts_event event [ TSDEV_BUFFER_SIZE ] ;
int raw ;
} ;
/* The following ioctl codes are defined ONLY for backward compatibility.
* Don ' t use tsdev for new developement ; use the tslib library instead .
* Touchscreen calibration is a fully userspace task .
*/
/* Use 'f' as magic number */
# define IOC_H3600_TS_MAGIC 'f'
# define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
# define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
static struct tsdev * tsdev_table [ TSDEV_MINORS / 2 ] ;
static int tsdev_fasync ( int fd , struct file * file , int on )
{
2007-04-12 01:30:00 -04:00
struct tsdev_client * client = file - > private_data ;
2005-04-16 15:20:36 -07:00
int retval ;
2007-04-12 01:30:00 -04:00
retval = fasync_helper ( fd , file , on , & client - > fasync ) ;
2005-04-16 15:20:36 -07:00
return retval < 0 ? retval : 0 ;
}
static int tsdev_open ( struct inode * inode , struct file * file )
{
int i = iminor ( inode ) - TSDEV_MINOR_BASE ;
2007-04-12 01:30:00 -04:00
struct tsdev_client * client ;
struct tsdev * tsdev ;
2007-04-12 01:30:15 -04:00
int error ;
2007-02-10 01:29:11 -05:00
printk ( KERN_WARNING " tsdev (compaq touchscreen emulation) is scheduled "
" for removal. \n See Documentation/feature-removal-schedule.txt "
" for details. \n " ) ;
2005-04-16 15:20:36 -07:00
2007-04-12 01:29:46 -04:00
if ( i > = TSDEV_MINORS )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2007-04-12 01:30:00 -04:00
tsdev = tsdev_table [ i & TSDEV_MINOR_MASK ] ;
if ( ! tsdev | | ! tsdev - > exist )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2007-06-14 23:32:24 -04:00
get_device ( & tsdev - > dev ) ;
2007-04-12 01:30:00 -04:00
client = kzalloc ( sizeof ( struct tsdev_client ) , GFP_KERNEL ) ;
2007-06-14 23:32:24 -04:00
if ( ! client ) {
error = - ENOMEM ;
goto err_put_tsdev ;
}
2005-04-16 15:20:36 -07:00
2007-04-12 01:30:00 -04:00
client - > tsdev = tsdev ;
client - > raw = ( i > = TSDEV_MINORS / 2 ) ? 1 : 0 ;
list_add_tail ( & client - > node , & tsdev - > client_list ) ;
2005-04-16 15:20:36 -07:00
2007-04-12 01:30:15 -04:00
if ( ! tsdev - > open + + & & tsdev - > exist ) {
error = input_open_device ( & tsdev - > handle ) ;
2007-06-14 23:32:24 -04:00
if ( error )
goto err_free_client ;
2007-04-12 01:30:15 -04:00
}
2005-04-16 15:20:36 -07:00
2007-04-12 01:30:00 -04:00
file - > private_data = client ;
2005-04-16 15:20:36 -07:00
return 0 ;
2007-06-14 23:32:24 -04:00
err_free_client :
list_del ( & client - > node ) ;
kfree ( client ) ;
err_put_tsdev :
put_device ( & tsdev - > dev ) ;
return error ;
2005-04-16 15:20:36 -07:00
}
2007-06-14 23:32:24 -04:00
static void tsdev_free ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
2007-06-14 23:32:24 -04:00
struct tsdev * tsdev = container_of ( dev , struct tsdev , dev ) ;
2005-04-16 15:20:36 -07:00
tsdev_table [ tsdev - > minor ] = NULL ;
kfree ( tsdev ) ;
}
static int tsdev_release ( struct inode * inode , struct file * file )
{
2007-04-12 01:30:00 -04:00
struct tsdev_client * client = file - > private_data ;
struct tsdev * tsdev = client - > tsdev ;
2005-04-16 15:20:36 -07:00
tsdev_fasync ( - 1 , file , 0 ) ;
2007-04-12 01:30:00 -04:00
list_del ( & client - > node ) ;
kfree ( client ) ;
2007-06-14 23:32:24 -04:00
if ( ! - - tsdev - > open & & tsdev - > exist )
input_close_device ( & tsdev - > handle ) ;
put_device ( & tsdev - > dev ) ;
2007-04-12 01:30:00 -04:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t tsdev_read ( struct file * file , char __user * buffer , size_t count ,
2007-04-12 01:30:00 -04:00
loff_t * ppos )
2005-04-16 15:20:36 -07:00
{
2007-04-12 01:30:00 -04:00
struct tsdev_client * client = file - > private_data ;
struct tsdev * tsdev = client - > tsdev ;
2005-04-16 15:20:36 -07:00
int retval = 0 ;
2007-04-12 01:30:00 -04:00
if ( client - > head = = client - > tail & & tsdev - > exist & & ( file - > f_flags & O_NONBLOCK ) )
2005-04-16 15:20:36 -07:00
return - EAGAIN ;
2007-04-12 01:30:00 -04:00
retval = wait_event_interruptible ( tsdev - > wait ,
client - > head ! = client - > tail | | ! tsdev - > exist ) ;
2005-04-16 15:20:36 -07:00
if ( retval )
return retval ;
2007-04-12 01:30:00 -04:00
if ( ! tsdev - > exist )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2007-04-12 01:30:00 -04:00
while ( client - > head ! = client - > tail & &
2005-04-16 15:20:36 -07:00
retval + sizeof ( struct ts_event ) < = count ) {
2007-04-12 01:30:00 -04:00
if ( copy_to_user ( buffer + retval , client - > event + client - > tail ,
2005-04-16 15:20:36 -07:00
sizeof ( struct ts_event ) ) )
return - EFAULT ;
2007-04-12 01:30:00 -04:00
client - > tail = ( client - > tail + 1 ) & ( TSDEV_BUFFER_SIZE - 1 ) ;
2005-04-16 15:20:36 -07:00
retval + = sizeof ( struct ts_event ) ;
}
return retval ;
}
/* No kernel lock - fine */
2007-04-12 01:30:00 -04:00
static unsigned int tsdev_poll ( struct file * file , poll_table * wait )
2005-04-16 15:20:36 -07:00
{
2007-04-12 01:30:00 -04:00
struct tsdev_client * client = file - > private_data ;
struct tsdev * tsdev = client - > tsdev ;
2006-06-26 01:48:47 -04:00
2007-04-12 01:30:00 -04:00
poll_wait ( file , & tsdev - > wait , wait ) ;
return ( ( client - > head = = client - > tail ) ? 0 : ( POLLIN | POLLRDNORM ) ) |
( tsdev - > exist ? 0 : ( POLLHUP | POLLERR ) ) ;
2005-04-16 15:20:36 -07:00
}
static int tsdev_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
2007-04-12 01:30:00 -04:00
struct tsdev_client * client = file - > private_data ;
struct tsdev * tsdev = client - > tsdev ;
2005-04-16 15:20:36 -07:00
int retval = 0 ;
switch ( cmd ) {
case TS_GET_CAL :
2007-04-12 01:29:46 -04:00
if ( copy_to_user ( ( void __user * ) arg , & tsdev - > cal ,
sizeof ( struct ts_calibration ) ) )
2005-04-16 15:20:36 -07:00
retval = - EFAULT ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
case TS_SET_CAL :
2007-04-12 01:29:46 -04:00
if ( copy_from_user ( & tsdev - > cal , ( void __user * ) arg ,
sizeof ( struct ts_calibration ) ) )
2005-04-16 15:20:36 -07:00
retval = - EFAULT ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
default :
retval = - EINVAL ;
break ;
}
return retval ;
}
2006-09-14 01:31:59 -04:00
static const struct file_operations tsdev_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. open = tsdev_open ,
. release = tsdev_release ,
. read = tsdev_read ,
. poll = tsdev_poll ,
. fasync = tsdev_fasync ,
. ioctl = tsdev_ioctl ,
} ;
static void tsdev_event ( struct input_handle * handle , unsigned int type ,
unsigned int code , int value )
{
struct tsdev * tsdev = handle - > private ;
2007-04-12 01:30:00 -04:00
struct tsdev_client * client ;
2005-04-16 15:20:36 -07:00
struct timeval time ;
switch ( type ) {
case EV_ABS :
switch ( code ) {
case ABS_X :
tsdev - > x = value ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
case ABS_Y :
tsdev - > y = value ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
case ABS_PRESSURE :
if ( value > handle - > dev - > absmax [ ABS_PRESSURE ] )
value = handle - > dev - > absmax [ ABS_PRESSURE ] ;
value - = handle - > dev - > absmin [ ABS_PRESSURE ] ;
if ( value < 0 )
value = 0 ;
tsdev - > pressure = value ;
break ;
}
break ;
case EV_REL :
switch ( code ) {
case REL_X :
tsdev - > x + = value ;
if ( tsdev - > x < 0 )
tsdev - > x = 0 ;
else if ( tsdev - > x > xres )
tsdev - > x = xres ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
case REL_Y :
tsdev - > y + = value ;
if ( tsdev - > y < 0 )
tsdev - > y = 0 ;
else if ( tsdev - > y > yres )
tsdev - > y = yres ;
break ;
}
break ;
case EV_KEY :
if ( code = = BTN_TOUCH | | code = = BTN_MOUSE ) {
switch ( value ) {
case 0 :
tsdev - > pressure = 0 ;
break ;
2006-06-26 01:48:47 -04:00
2005-04-16 15:20:36 -07:00
case 1 :
if ( ! tsdev - > pressure )
tsdev - > pressure = 1 ;
break ;
}
}
break ;
}
if ( type ! = EV_SYN | | code ! = SYN_REPORT )
return ;
2007-04-12 01:30:00 -04:00
list_for_each_entry ( client , & tsdev - > client_list , node ) {
2005-04-16 15:20:36 -07:00
int x , y , tmp ;
do_gettimeofday ( & time ) ;
2007-07-10 00:43:06 -04:00
client - > event [ client - > head ] . millisecs = time . tv_usec / 1000 ;
2007-04-12 01:30:00 -04:00
client - > event [ client - > head ] . pressure = tsdev - > pressure ;
2005-04-16 15:20:36 -07:00
x = tsdev - > x ;
y = tsdev - > y ;
/* Calibration */
2007-04-12 01:30:00 -04:00
if ( ! client - > raw ) {
2005-04-16 15:20:36 -07:00
x = ( ( x * tsdev - > cal . xscale ) > > 8 ) + tsdev - > cal . xtrans ;
y = ( ( y * tsdev - > cal . yscale ) > > 8 ) + tsdev - > cal . ytrans ;
if ( tsdev - > cal . xyswap ) {
tmp = x ; x = y ; y = tmp ;
}
}
2007-04-12 01:30:00 -04:00
client - > event [ client - > head ] . x = x ;
client - > event [ client - > head ] . y = y ;
client - > head = ( client - > head + 1 ) & ( TSDEV_BUFFER_SIZE - 1 ) ;
kill_fasync ( & client - > fasync , SIGIO , POLL_IN ) ;
2005-04-16 15:20:36 -07:00
}
wake_up_interruptible ( & tsdev - > wait ) ;
}
2007-04-12 01:29:46 -04:00
static int tsdev_connect ( struct input_handler * handler , struct input_dev * dev ,
const struct input_device_id * id )
2005-04-16 15:20:36 -07:00
{
struct tsdev * tsdev ;
int minor , delta ;
2007-04-12 01:29:46 -04:00
int error ;
2005-04-16 15:20:36 -07:00
2006-06-26 01:48:47 -04:00
for ( minor = 0 ; minor < TSDEV_MINORS / 2 & & tsdev_table [ minor ] ; minor + + ) ;
if ( minor > = TSDEV_MINORS / 2 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR
" tsdev: You have way too many touchscreens \n " ) ;
2007-04-12 01:29:46 -04:00
return - ENFILE ;
2005-04-16 15:20:36 -07:00
}
2007-04-12 01:29:46 -04:00
tsdev = kzalloc ( sizeof ( struct tsdev ) , GFP_KERNEL ) ;
if ( ! tsdev )
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
2007-04-12 01:30:00 -04:00
INIT_LIST_HEAD ( & tsdev - > client_list ) ;
2005-04-16 15:20:36 -07:00
init_waitqueue_head ( & tsdev - > wait ) ;
tsdev - > exist = 1 ;
tsdev - > minor = minor ;
tsdev - > handle . dev = dev ;
tsdev - > handle . name = tsdev - > name ;
tsdev - > handle . handler = handler ;
tsdev - > handle . private = tsdev ;
2007-06-14 23:32:24 -04:00
snprintf ( tsdev - > name , sizeof ( tsdev - > name ) , " ts%d " , minor ) ;
2005-04-16 15:20:36 -07:00
/* Precompute the rough calibration matrix */
delta = dev - > absmax [ ABS_X ] - dev - > absmin [ ABS_X ] + 1 ;
if ( delta = = 0 )
delta = 1 ;
tsdev - > cal . xscale = ( xres < < 8 ) / delta ;
tsdev - > cal . xtrans = - ( ( dev - > absmin [ ABS_X ] * tsdev - > cal . xscale ) > > 8 ) ;
delta = dev - > absmax [ ABS_Y ] - dev - > absmin [ ABS_Y ] + 1 ;
if ( delta = = 0 )
delta = 1 ;
tsdev - > cal . yscale = ( yres < < 8 ) / delta ;
tsdev - > cal . ytrans = - ( ( dev - > absmin [ ABS_Y ] * tsdev - > cal . yscale ) > > 8 ) ;
2007-06-14 23:32:24 -04:00
snprintf ( tsdev - > dev . bus_id , sizeof ( tsdev - > dev . bus_id ) ,
" ts%d " , minor ) ;
tsdev - > dev . class = & input_class ;
tsdev - > dev . parent = & dev - > dev ;
tsdev - > dev . devt = MKDEV ( INPUT_MAJOR , TSDEV_MINOR_BASE + minor ) ;
tsdev - > dev . release = tsdev_free ;
device_initialize ( & tsdev - > dev ) ;
2007-04-12 01:29:46 -04:00
2007-06-14 23:32:24 -04:00
tsdev_table [ minor ] = tsdev ;
2005-10-27 22:25:43 -07:00
2007-06-14 23:32:24 -04:00
error = device_add ( & tsdev - > dev ) ;
2007-04-12 01:29:46 -04:00
if ( error )
2007-06-14 23:32:24 -04:00
goto err_free_tsdev ;
2005-04-16 15:20:36 -07:00
2007-04-12 01:29:46 -04:00
error = input_register_handle ( & tsdev - > handle ) ;
if ( error )
2007-06-14 23:32:24 -04:00
goto err_delete_tsdev ;
2007-04-12 01:29:46 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
2007-06-14 23:32:24 -04:00
err_delete_tsdev :
device_del ( & tsdev - > dev ) ;
2007-04-12 01:29:46 -04:00
err_free_tsdev :
2007-06-14 23:32:24 -04:00
put_device ( & tsdev - > dev ) ;
2007-04-12 01:29:46 -04:00
return error ;
2005-04-16 15:20:36 -07:00
}
static void tsdev_disconnect ( struct input_handle * handle )
{
struct tsdev * tsdev = handle - > private ;
2007-04-12 01:30:00 -04:00
struct tsdev_client * client ;
2005-04-16 15:20:36 -07:00
2007-04-12 01:29:46 -04:00
input_unregister_handle ( handle ) ;
2007-06-14 23:32:24 -04:00
device_del ( & tsdev - > dev ) ;
2005-04-16 15:20:36 -07:00
tsdev - > exist = 0 ;
if ( tsdev - > open ) {
input_close_device ( handle ) ;
2007-04-12 01:30:00 -04:00
list_for_each_entry ( client , & tsdev - > client_list , node )
kill_fasync ( & client - > fasync , SIGIO , POLL_HUP ) ;
2007-06-03 23:29:36 -04:00
wake_up_interruptible ( & tsdev - > wait ) ;
2007-06-14 23:32:24 -04:00
}
put_device ( & tsdev - > dev ) ;
2005-04-16 15:20:36 -07:00
}
2006-09-14 01:31:59 -04:00
static const struct input_device_id tsdev_ids [ ] = {
2005-04-16 15:20:36 -07: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 ) } ,
2006-06-26 01:48:47 -04:00
} , /* A mouse like device, at least one button, two relative axes */
2005-04-16 15:20:36 -07:00
{
. 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 ) } ,
2006-06-26 01:48:47 -04:00
} , /* A tablet like device, at least touch detection, two absolute axes */
2005-04-16 15:20:36 -07:00
{
. flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT ,
. evbit = { BIT ( EV_ABS ) } ,
. absbit = { BIT ( ABS_X ) | BIT ( ABS_Y ) | BIT ( ABS_PRESSURE ) } ,
2006-06-26 01:48:47 -04:00
} , /* A tablet like device with several gradations of pressure */
2005-04-16 15:20:36 -07:00
2006-06-26 01:48:47 -04:00
{ } /* Terminating entry */
2005-04-16 15:20:36 -07:00
} ;
MODULE_DEVICE_TABLE ( input , tsdev_ids ) ;
static struct input_handler tsdev_handler = {
. event = tsdev_event ,
. connect = tsdev_connect ,
. disconnect = tsdev_disconnect ,
. fops = & tsdev_fops ,
. minor = TSDEV_MINOR_BASE ,
. name = " tsdev " ,
. id_table = tsdev_ids ,
} ;
static int __init tsdev_init ( void )
{
2006-09-14 01:32:39 -04:00
return input_register_handler ( & tsdev_handler ) ;
2005-04-16 15:20:36 -07:00
}
static void __exit tsdev_exit ( void )
{
input_unregister_handler ( & tsdev_handler ) ;
}
module_init ( tsdev_init ) ;
module_exit ( tsdev_exit ) ;