2019-05-29 17:12:45 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-09-01 07:57:59 +04:00
/*
2010-10-05 14:08:57 +04:00
* hdaps . c - driver for IBM ' s Hard Drive Active Protection System
2005-09-01 07:57:59 +04:00
*
* Copyright ( C ) 2005 Robert Love < rml @ novell . com >
2012-04-29 04:01:54 +04:00
* Copyright ( C ) 2005 Jesper Juhl < jj @ chaosbits . net >
2005-09-01 07:57:59 +04:00
*
2005-09-23 08:44:00 +04:00
* The HardDisk Active Protection System ( hdaps ) is present in IBM ThinkPads
* starting with the R40 , T41 , and X40 . It provides a basic two - axis
* accelerometer and other data , such as the device ' s temperature .
2005-09-01 07:57:59 +04:00
*
2005-09-17 06:28:07 +04:00
* This driver is based on the document by Mark A . Smith available at
2005-09-01 07:57:59 +04:00
* http : //www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial
* and error .
*/
2011-03-30 02:21:40 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-09-01 07:57:59 +04:00
# include <linux/delay.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2019-10-01 21:59:37 +03:00
# include <linux/input.h>
2005-09-01 07:57:59 +04:00
# include <linux/kernel.h>
2007-05-08 11:32:05 +04:00
# include <linux/mutex.h>
2005-09-01 07:57:59 +04:00
# include <linux/module.h>
# include <linux/timer.h>
# include <linux/dmi.h>
2006-10-18 21:55:46 +04:00
# include <linux/jiffies.h>
2009-09-15 19:18:13 +04:00
# include <linux/io.h>
2005-09-01 07:57:59 +04:00
# define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */
2005-09-17 06:28:07 +04:00
# define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */
2005-09-01 07:57:59 +04:00
# define HDAPS_PORT_STATE 0x1611 /* device state */
# define HDAPS_PORT_YPOS 0x1612 /* y-axis position */
# define HDAPS_PORT_XPOS 0x1614 /* x-axis position */
2006-06-04 22:10:55 +04:00
# define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */
2005-09-01 07:57:59 +04:00
# define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */
# define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */
# define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */
# define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */
# define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */
2005-09-17 06:28:07 +04:00
# define STATE_FRESH 0x50 /* accelerometer data is fresh */
2005-09-01 07:57:59 +04:00
# define KEYBD_MASK 0x20 /* set if keyboard activity */
# define MOUSE_MASK 0x40 /* set if mouse activity */
# define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */
# define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */
# define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */
# define INIT_WAIT_MSECS 200 /* ... in 200ms increments */
2007-10-13 05:30:36 +04:00
# define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/
2005-09-17 06:28:07 +04:00
# define HDAPS_INPUT_FUZZ 4 /* input event threshold */
2005-11-11 06:10:55 +03:00
# define HDAPS_INPUT_FLAT 4
2005-09-17 06:28:07 +04:00
2009-03-30 23:46:41 +04:00
# define HDAPS_X_AXIS (1 << 0)
# define HDAPS_Y_AXIS (1 << 1)
# define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
2005-09-17 06:28:07 +04:00
static struct platform_device * pdev ;
2019-10-01 21:59:37 +03:00
static struct input_dev * hdaps_idev ;
2005-09-01 07:57:59 +04:00
static unsigned int hdaps_invert ;
static u8 km_activity ;
static int rest_x ;
static int rest_y ;
2007-05-08 11:32:05 +04:00
static DEFINE_MUTEX ( hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
/*
2007-05-08 11:32:05 +04:00
* __get_latch - Get the value from a given port . Callers must hold hdaps_mtx .
2005-09-01 07:57:59 +04:00
*/
static inline u8 __get_latch ( u16 port )
{
2005-09-17 06:28:07 +04:00
return inb ( port ) & 0xff ;
2005-09-01 07:57:59 +04:00
}
/*
2005-09-17 06:28:07 +04:00
* __check_latch - Check a port latch for a given value . Returns zero if the
2007-05-08 11:32:05 +04:00
* port contains the given value . Callers must hold hdaps_mtx .
2005-09-01 07:57:59 +04:00
*/
2005-09-17 06:28:07 +04:00
static inline int __check_latch ( u16 port , u8 val )
2005-09-01 07:57:59 +04:00
{
if ( __get_latch ( port ) = = val )
return 0 ;
return - EINVAL ;
}
/*
* __wait_latch - Wait up to 100u s for a port latch to get a certain value ,
2007-05-08 11:32:05 +04:00
* returning zero if the value is obtained . Callers must hold hdaps_mtx .
2005-09-01 07:57:59 +04:00
*/
2005-09-17 06:28:07 +04:00
static int __wait_latch ( u16 port , u8 val )
2005-09-01 07:57:59 +04:00
{
unsigned int i ;
for ( i = 0 ; i < 20 ; i + + ) {
if ( ! __check_latch ( port , val ) )
return 0 ;
udelay ( 5 ) ;
}
2005-09-17 06:28:07 +04:00
return - EIO ;
2005-09-01 07:57:59 +04:00
}
/*
2005-09-17 06:28:07 +04:00
* __device_refresh - request a refresh from the accelerometer . Does not wait
2007-05-08 11:32:05 +04:00
* for refresh to complete . Callers must hold hdaps_mtx .
2005-09-01 07:57:59 +04:00
*/
2005-09-17 06:28:07 +04:00
static void __device_refresh ( void )
2005-09-01 07:57:59 +04:00
{
2005-09-17 06:28:07 +04:00
udelay ( 200 ) ;
if ( inb ( 0x1604 ) ! = STATE_FRESH ) {
outb ( 0x11 , 0x1610 ) ;
outb ( 0x01 , 0x161f ) ;
}
}
2005-09-01 07:57:59 +04:00
2005-09-17 06:28:07 +04:00
/*
* __device_refresh_sync - request a synchronous refresh from the
* accelerometer . We wait for the refresh to complete . Returns zero if
2007-05-08 11:32:05 +04:00
* successful and nonzero on error . Callers must hold hdaps_mtx .
2005-09-17 06:28:07 +04:00
*/
static int __device_refresh_sync ( void )
{
__device_refresh ( ) ;
2005-09-01 07:57:59 +04:00
return __wait_latch ( 0x1604 , STATE_FRESH ) ;
}
/*
2005-09-17 06:28:07 +04:00
* __device_complete - indicate to the accelerometer that we are done reading
2007-05-08 11:32:05 +04:00
* data , and then initiate an async refresh . Callers must hold hdaps_mtx .
2005-09-01 07:57:59 +04:00
*/
static inline void __device_complete ( void )
{
inb ( 0x161f ) ;
inb ( 0x1604 ) ;
2005-09-17 06:28:07 +04:00
__device_refresh ( ) ;
2005-09-01 07:57:59 +04:00
}
/*
* hdaps_readb_one - reads a byte from a single I / O port , placing the value in
* the given pointer . Returns zero on success or a negative error on failure .
* Can sleep .
*/
static int hdaps_readb_one ( unsigned int port , u8 * val )
{
int ret ;
2007-05-08 11:32:05 +04:00
mutex_lock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
2005-09-17 06:28:07 +04:00
/* do a sync refresh -- we need to be sure that we read fresh data */
ret = __device_refresh_sync ( ) ;
if ( ret )
goto out ;
* val = inb ( port ) ;
__device_complete ( ) ;
out :
2007-05-08 11:32:05 +04:00
mutex_unlock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
return ret ;
}
2005-09-17 06:28:07 +04:00
/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */
2005-09-01 07:57:59 +04:00
static int __hdaps_read_pair ( unsigned int port1 , unsigned int port2 ,
int * x , int * y )
{
/* do a sync refresh -- we need to be sure that we read fresh data */
2005-09-17 06:28:07 +04:00
if ( __device_refresh_sync ( ) )
2005-09-01 07:57:59 +04:00
return - EIO ;
* y = inw ( port2 ) ;
* x = inw ( port1 ) ;
km_activity = inb ( HDAPS_PORT_KMACT ) ;
__device_complete ( ) ;
2009-03-30 23:46:41 +04:00
/* hdaps_invert is a bitvector to negate the axes */
if ( hdaps_invert & HDAPS_X_AXIS )
2005-09-01 07:57:59 +04:00
* x = - * x ;
2009-03-30 23:46:41 +04:00
if ( hdaps_invert & HDAPS_Y_AXIS )
2005-09-01 07:57:59 +04:00
* y = - * y ;
return 0 ;
}
/*
* hdaps_read_pair - reads the values from a pair of ports , placing the values
* in the given pointers . Returns zero on success . Can sleep .
*/
static int hdaps_read_pair ( unsigned int port1 , unsigned int port2 ,
int * val1 , int * val2 )
{
int ret ;
2007-05-08 11:32:05 +04:00
mutex_lock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
ret = __hdaps_read_pair ( port1 , port2 , val1 , val2 ) ;
2007-05-08 11:32:05 +04:00
mutex_unlock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
return ret ;
}
2005-09-17 06:28:07 +04:00
/*
* hdaps_device_init - initialize the accelerometer . Returns zero on success
* and negative error code on failure . Can sleep .
*/
2005-09-01 07:57:59 +04:00
static int hdaps_device_init ( void )
{
2005-09-17 06:28:07 +04:00
int total , ret = - ENXIO ;
2005-09-01 07:57:59 +04:00
2007-05-08 11:32:05 +04:00
mutex_lock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
outb ( 0x13 , 0x1610 ) ;
outb ( 0x01 , 0x161f ) ;
if ( __wait_latch ( 0x161f , 0x00 ) )
goto out ;
/*
2005-09-17 06:28:07 +04:00
* Most ThinkPads return 0x01 .
*
* Others - - namely the R50p , T41p , and T42p - - return 0x03 . These laptops
* have " inverted " axises .
2005-09-01 07:57:59 +04:00
*
* The 0x02 value occurs when the chip has been previously initialized .
*/
if ( __check_latch ( 0x1611 , 0x03 ) & &
__check_latch ( 0x1611 , 0x02 ) & &
__check_latch ( 0x1611 , 0x01 ) )
goto out ;
2011-03-30 02:21:40 +04:00
printk ( KERN_DEBUG " hdaps: initial latch check good (0x%02x) \n " ,
2005-09-01 07:57:59 +04:00
__get_latch ( 0x1611 ) ) ;
outb ( 0x17 , 0x1610 ) ;
outb ( 0x81 , 0x1611 ) ;
outb ( 0x01 , 0x161f ) ;
if ( __wait_latch ( 0x161f , 0x00 ) )
goto out ;
if ( __wait_latch ( 0x1611 , 0x00 ) )
goto out ;
if ( __wait_latch ( 0x1612 , 0x60 ) )
goto out ;
if ( __wait_latch ( 0x1613 , 0x00 ) )
goto out ;
outb ( 0x14 , 0x1610 ) ;
outb ( 0x01 , 0x1611 ) ;
outb ( 0x01 , 0x161f ) ;
if ( __wait_latch ( 0x161f , 0x00 ) )
goto out ;
outb ( 0x10 , 0x1610 ) ;
outb ( 0xc8 , 0x1611 ) ;
outb ( 0x00 , 0x1612 ) ;
outb ( 0x02 , 0x1613 ) ;
outb ( 0x01 , 0x161f ) ;
if ( __wait_latch ( 0x161f , 0x00 ) )
goto out ;
2005-09-17 06:28:07 +04:00
if ( __device_refresh_sync ( ) )
2005-09-01 07:57:59 +04:00
goto out ;
if ( __wait_latch ( 0x1611 , 0x00 ) )
goto out ;
/* we have done our dance, now let's wait for the applause */
2005-09-17 06:28:07 +04:00
for ( total = INIT_TIMEOUT_MSECS ; total > 0 ; total - = INIT_WAIT_MSECS ) {
int x , y ;
2005-09-01 07:57:59 +04:00
/* a read of the device helps push it into action */
2005-09-17 06:28:07 +04:00
__hdaps_read_pair ( HDAPS_PORT_XPOS , HDAPS_PORT_YPOS , & x , & y ) ;
2005-09-01 07:57:59 +04:00
if ( ! __wait_latch ( 0x1611 , 0x02 ) ) {
ret = 0 ;
break ;
}
msleep ( INIT_WAIT_MSECS ) ;
}
out :
2007-05-08 11:32:05 +04:00
mutex_unlock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
return ret ;
}
/* Device model stuff */
2005-11-10 01:32:44 +03:00
static int hdaps_probe ( struct platform_device * dev )
2005-09-01 07:57:59 +04:00
{
int ret ;
ret = hdaps_device_init ( ) ;
if ( ret )
return ret ;
2011-03-30 02:21:40 +04:00
pr_info ( " device successfully initialized \n " ) ;
2005-09-01 07:57:59 +04:00
return 0 ;
}
2012-08-10 01:00:13 +04:00
# ifdef CONFIG_PM_SLEEP
2012-07-01 01:50:47 +04:00
static int hdaps_resume ( struct device * dev )
2005-09-01 07:57:59 +04:00
{
2005-10-28 20:52:56 +04:00
return hdaps_device_init ( ) ;
2005-09-01 07:57:59 +04:00
}
2012-08-10 01:00:13 +04:00
# endif
2005-09-01 07:57:59 +04:00
2012-07-01 01:50:47 +04:00
static SIMPLE_DEV_PM_OPS ( hdaps_pm , NULL , hdaps_resume ) ;
2005-11-10 01:32:44 +03:00
static struct platform_driver hdaps_driver = {
2005-09-01 07:57:59 +04:00
. probe = hdaps_probe ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " hdaps " ,
2012-07-01 01:50:47 +04:00
. pm = & hdaps_pm ,
2005-11-10 01:32:44 +03:00
} ,
2005-09-01 07:57:59 +04:00
} ;
2005-09-17 06:28:07 +04:00
/*
2007-05-08 11:32:05 +04:00
* hdaps_calibrate - Set our " resting " values . Callers must hold hdaps_mtx .
2005-09-17 06:28:07 +04:00
*/
static void hdaps_calibrate ( void )
{
__hdaps_read_pair ( HDAPS_PORT_XPOS , HDAPS_PORT_YPOS , & rest_x , & rest_y ) ;
}
2019-10-01 21:59:37 +03:00
static void hdaps_mousedev_poll ( struct input_dev * input_dev )
2005-09-17 06:28:07 +04:00
{
int x , y ;
2007-10-13 05:30:36 +04:00
mutex_lock ( & hdaps_mtx ) ;
2005-09-17 06:28:07 +04:00
if ( __hdaps_read_pair ( HDAPS_PORT_XPOS , HDAPS_PORT_YPOS , & x , & y ) )
goto out ;
2007-10-13 05:30:36 +04:00
input_report_abs ( input_dev , ABS_X , x - rest_x ) ;
input_report_abs ( input_dev , ABS_Y , y - rest_y ) ;
input_sync ( input_dev ) ;
2005-09-17 06:28:07 +04:00
out :
2007-05-08 11:32:05 +04:00
mutex_unlock ( & hdaps_mtx ) ;
2005-09-17 06:28:07 +04:00
}
2005-09-01 07:57:59 +04:00
/* Sysfs Files */
static ssize_t hdaps_position_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int ret , x , y ;
ret = hdaps_read_pair ( HDAPS_PORT_XPOS , HDAPS_PORT_YPOS , & x , & y ) ;
if ( ret )
return ret ;
return sprintf ( buf , " (%d,%d) \ n " , x, y) ;
}
static ssize_t hdaps_variance_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int ret , x , y ;
ret = hdaps_read_pair ( HDAPS_PORT_XVAR , HDAPS_PORT_YVAR , & x , & y ) ;
if ( ret )
return ret ;
return sprintf ( buf , " (%d,%d) \ n " , x, y) ;
}
static ssize_t hdaps_temp1_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2011-11-24 18:01:31 +04:00
u8 uninitialized_var ( temp ) ;
2005-09-01 07:57:59 +04:00
int ret ;
ret = hdaps_readb_one ( HDAPS_PORT_TEMP1 , & temp ) ;
2012-01-31 02:00:11 +04:00
if ( ret )
2005-09-01 07:57:59 +04:00
return ret ;
return sprintf ( buf , " %u \n " , temp ) ;
}
static ssize_t hdaps_temp2_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2011-11-24 18:01:31 +04:00
u8 uninitialized_var ( temp ) ;
2005-09-01 07:57:59 +04:00
int ret ;
ret = hdaps_readb_one ( HDAPS_PORT_TEMP2 , & temp ) ;
2012-01-31 02:00:11 +04:00
if ( ret )
2005-09-01 07:57:59 +04:00
return ret ;
return sprintf ( buf , " %u \n " , temp ) ;
}
static ssize_t hdaps_keyboard_activity_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
return sprintf ( buf , " %u \n " , KEYBD_ISSET ( km_activity ) ) ;
}
static ssize_t hdaps_mouse_activity_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
return sprintf ( buf , " %u \n " , MOUSE_ISSET ( km_activity ) ) ;
}
static ssize_t hdaps_calibrate_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " (%d,%d) \ n " , rest_x, rest_y) ;
}
static ssize_t hdaps_calibrate_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2007-05-08 11:32:05 +04:00
mutex_lock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
hdaps_calibrate ( ) ;
2007-05-08 11:32:05 +04:00
mutex_unlock ( & hdaps_mtx ) ;
2005-09-01 07:57:59 +04:00
return count ;
}
static ssize_t hdaps_invert_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %u \n " , hdaps_invert ) ;
}
static ssize_t hdaps_invert_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
int invert ;
2009-03-30 23:46:41 +04:00
if ( sscanf ( buf , " %d " , & invert ) ! = 1 | |
invert < 0 | | invert > HDAPS_BOTH_AXES )
2005-09-01 07:57:59 +04:00
return - EINVAL ;
hdaps_invert = invert ;
hdaps_calibrate ( ) ;
return count ;
}
static DEVICE_ATTR ( position , 0444 , hdaps_position_show , NULL ) ;
static DEVICE_ATTR ( variance , 0444 , hdaps_variance_show , NULL ) ;
static DEVICE_ATTR ( temp1 , 0444 , hdaps_temp1_show , NULL ) ;
static DEVICE_ATTR ( temp2 , 0444 , hdaps_temp2_show , NULL ) ;
static DEVICE_ATTR ( keyboard_activity , 0444 , hdaps_keyboard_activity_show , NULL ) ;
static DEVICE_ATTR ( mouse_activity , 0444 , hdaps_mouse_activity_show , NULL ) ;
static DEVICE_ATTR ( calibrate , 0644 , hdaps_calibrate_show , hdaps_calibrate_store ) ;
static DEVICE_ATTR ( invert , 0644 , hdaps_invert_show , hdaps_invert_store ) ;
static struct attribute * hdaps_attributes [ ] = {
& dev_attr_position . attr ,
& dev_attr_variance . attr ,
& dev_attr_temp1 . attr ,
& dev_attr_temp2 . attr ,
& dev_attr_keyboard_activity . attr ,
& dev_attr_mouse_activity . attr ,
& dev_attr_calibrate . attr ,
& dev_attr_invert . attr ,
NULL ,
} ;
static struct attribute_group hdaps_attribute_group = {
. attrs = hdaps_attributes ,
} ;
/* Module stuff */
2005-09-23 08:44:00 +04:00
/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */
2007-10-03 23:15:40 +04:00
static int __init hdaps_dmi_match ( const struct dmi_system_id * id )
2005-09-01 07:57:59 +04:00
{
2011-03-30 02:21:40 +04:00
pr_info ( " %s detected \n " , id - > ident ) ;
2005-09-23 08:44:00 +04:00
return 1 ;
2005-09-01 07:57:59 +04:00
}
2005-09-23 08:44:00 +04:00
/* hdaps_dmi_match_invert - found an inverted match. */
2007-10-03 23:15:40 +04:00
static int __init hdaps_dmi_match_invert ( const struct dmi_system_id * id )
2005-09-01 07:57:59 +04:00
{
2009-03-30 23:46:41 +04:00
hdaps_invert = ( unsigned long ) id - > driver_data ;
2011-03-30 02:21:40 +04:00
pr_info ( " inverting axis (%u) readings \n " , hdaps_invert ) ;
2005-09-23 08:44:00 +04:00
return hdaps_dmi_match ( id ) ;
2005-09-01 07:57:59 +04:00
}
2009-03-30 23:46:41 +04:00
# define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
2006-12-12 20:18:28 +03:00
. ident = vendor " " model , \
2005-09-01 07:57:59 +04:00
. callback = hdaps_dmi_match_invert , \
2009-03-30 23:46:41 +04:00
. driver_data = ( void * ) axes , \
2005-09-01 07:57:59 +04:00
. matches = { \
2006-12-12 20:18:28 +03:00
DMI_MATCH ( DMI_BOARD_VENDOR , vendor ) , \
2005-09-01 07:57:59 +04:00
DMI_MATCH ( DMI_PRODUCT_VERSION , model ) \
} \
}
2009-03-30 23:46:41 +04:00
# define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
HDAPS_DMI_MATCH_INVERT ( vendor , model , 0 )
2006-12-12 20:18:28 +03:00
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
2006-12-12 20:18:28 +03:00
" ThinkPad T42p " , so the order of the entries matters .
If your ThinkPad is not recognized , please update to latest
BIOS . This is especially the case for some R52 ThinkPads . */
2017-09-14 12:59:30 +03:00
static const struct dmi_system_id hdaps_whitelist [ ] __initconst = {
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " IBM " , " ThinkPad R50p " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad R50 " ) ,
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad R51 " ) ,
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad R52 " ) ,
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad R61i " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad R61 " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " IBM " , " ThinkPad T41p " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad T41 " ) ,
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " IBM " , " ThinkPad T42p " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad T42 " ) ,
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad T43 " ) ,
2010-08-10 04:21:04 +04:00
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad T400 " , HDAPS_BOTH_AXES ) ,
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad T60 " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad T61p " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad T61 " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad X40 " ) ,
2009-03-30 23:46:42 +04:00
HDAPS_DMI_MATCH_INVERT ( " IBM " , " ThinkPad X41 " , HDAPS_Y_AXIS ) ,
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad X60 " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad X61s " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad X61 " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
HDAPS_DMI_MATCH_NORMAL ( " IBM " , " ThinkPad Z60m " ) ,
2009-03-30 23:46:41 +04:00
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad Z61m " , HDAPS_BOTH_AXES ) ,
HDAPS_DMI_MATCH_INVERT ( " LENOVO " , " ThinkPad Z61p " , HDAPS_BOTH_AXES ) ,
2006-12-12 20:18:28 +03:00
{ . ident = NULL }
} ;
2006-04-11 09:54:11 +04:00
2005-09-01 07:57:59 +04:00
static int __init hdaps_init ( void )
{
int ret ;
if ( ! dmi_check_system ( hdaps_whitelist ) ) {
2011-03-30 02:21:40 +04:00
pr_warn ( " supported laptop not found! \n " ) ;
2006-04-11 09:54:32 +04:00
ret = - ENODEV ;
2005-09-01 07:57:59 +04:00
goto out ;
}
if ( ! request_region ( HDAPS_LOW_PORT , HDAPS_NR_PORTS , " hdaps " ) ) {
ret = - ENXIO ;
goto out ;
}
2005-11-10 01:32:44 +03:00
ret = platform_driver_register ( & hdaps_driver ) ;
2005-09-01 07:57:59 +04:00
if ( ret )
goto out_region ;
pdev = platform_device_register_simple ( " hdaps " , - 1 , NULL , 0 ) ;
if ( IS_ERR ( pdev ) ) {
ret = PTR_ERR ( pdev ) ;
goto out_driver ;
}
ret = sysfs_create_group ( & pdev - > dev . kobj , & hdaps_attribute_group ) ;
if ( ret )
goto out_device ;
2019-10-01 21:59:37 +03:00
hdaps_idev = input_allocate_device ( ) ;
2005-11-11 06:10:55 +03:00
if ( ! hdaps_idev ) {
ret = - ENOMEM ;
goto out_group ;
}
2005-09-17 06:28:07 +04:00
/* initial calibrate for the input device */
hdaps_calibrate ( ) ;
/* initialize the input class */
2019-10-01 21:59:37 +03:00
hdaps_idev - > name = " hdaps " ;
hdaps_idev - > phys = " isa1600/input0 " ;
hdaps_idev - > id . bustype = BUS_ISA ;
hdaps_idev - > dev . parent = & pdev - > dev ;
input_set_abs_params ( hdaps_idev , ABS_X ,
2005-11-11 06:10:55 +03:00
- 256 , 256 , HDAPS_INPUT_FUZZ , HDAPS_INPUT_FLAT ) ;
2019-10-01 21:59:37 +03:00
input_set_abs_params ( hdaps_idev , ABS_Y ,
2005-11-11 06:10:55 +03:00
- 256 , 256 , HDAPS_INPUT_FUZZ , HDAPS_INPUT_FLAT ) ;
2019-10-01 21:59:37 +03:00
ret = input_setup_polling ( hdaps_idev , hdaps_mousedev_poll ) ;
if ( ret )
goto out_idev ;
input_set_poll_interval ( hdaps_idev , HDAPS_POLL_INTERVAL ) ;
ret = input_register_device ( hdaps_idev ) ;
2006-08-28 16:21:42 +04:00
if ( ret )
goto out_idev ;
2005-09-17 06:28:07 +04:00
2011-03-30 02:21:40 +04:00
pr_info ( " driver successfully loaded \n " ) ;
2005-09-01 07:57:59 +04:00
return 0 ;
2006-08-28 16:21:42 +04:00
out_idev :
2019-10-01 21:59:37 +03:00
input_free_device ( hdaps_idev ) ;
2005-11-11 06:10:55 +03:00
out_group :
sysfs_remove_group ( & pdev - > dev . kobj , & hdaps_attribute_group ) ;
2005-09-01 07:57:59 +04:00
out_device :
platform_device_unregister ( pdev ) ;
out_driver :
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & hdaps_driver ) ;
2005-09-01 07:57:59 +04:00
out_region :
release_region ( HDAPS_LOW_PORT , HDAPS_NR_PORTS ) ;
out :
2011-03-30 02:21:40 +04:00
pr_warn ( " driver init failed (ret=%d)! \n " , ret ) ;
2005-09-01 07:57:59 +04:00
return ret ;
}
static void __exit hdaps_exit ( void )
{
2019-10-01 21:59:37 +03:00
input_unregister_device ( hdaps_idev ) ;
2005-09-01 07:57:59 +04:00
sysfs_remove_group ( & pdev - > dev . kobj , & hdaps_attribute_group ) ;
platform_device_unregister ( pdev ) ;
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & hdaps_driver ) ;
2005-09-01 07:57:59 +04:00
release_region ( HDAPS_LOW_PORT , HDAPS_NR_PORTS ) ;
2011-03-30 02:21:40 +04:00
pr_info ( " driver unloaded \n " ) ;
2005-09-01 07:57:59 +04:00
}
module_init ( hdaps_init ) ;
module_exit ( hdaps_exit ) ;
2009-03-30 23:46:41 +04:00
module_param_named ( invert , hdaps_invert , int , 0 ) ;
MODULE_PARM_DESC ( invert , " invert data along each axis. 1 invert x-axis, "
" 2 invert y-axis, 3 invert both axes. " ) ;
2005-09-01 07:57:59 +04:00
MODULE_AUTHOR ( " Robert Love " ) ;
MODULE_DESCRIPTION ( " IBM Hard Drive Active Protection System (HDAPS) driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;