@ -18,6 +18,7 @@
# include <linux/input/mt.h>
# include <linux/serio.h>
# include <linux/libps2.h>
# include <asm/unaligned.h>
# include "psmouse.h"
# include "elantech.h"
@ -403,6 +404,68 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_sync ( dev ) ;
}
static void elantech_report_trackpoint ( struct psmouse * psmouse ,
int packet_type )
{
/*
* byte 0 : 0 0 sx sy 0 M R L
* byte 1 : ~ sx 0 0 0 0 0 0 0
* byte 2 : ~ sy 0 0 0 0 0 0 0
* byte 3 : 0 0 ~ sy ~ sx 0 1 1 0
* byte 4 : x7 x6 x5 x4 x3 x2 x1 x0
* byte 5 : y7 y6 y5 y4 y3 y2 y1 y0
*
* x and y are written in two ' s complement spread
* over 9 bits with sx / sy the relative top bit and
* x7 . . x0 and y7 . . y0 the lower bits .
* The sign of y is opposite to what the input driver
* expects for a relative movement
*/
struct elantech_data * etd = psmouse - > private ;
struct input_dev * tp_dev = etd - > tp_dev ;
unsigned char * packet = psmouse - > packet ;
int x , y ;
u32 t ;
if ( dev_WARN_ONCE ( & psmouse - > ps2dev . serio - > dev ,
! tp_dev ,
psmouse_fmt ( " Unexpected trackpoint message \n " ) ) ) {
if ( etd - > debug = = 1 )
elantech_packet_dump ( psmouse ) ;
return ;
}
t = get_unaligned_le32 ( & packet [ 0 ] ) ;
switch ( t & ~ 7U ) {
case 0x06000030U :
case 0x16008020U :
case 0x26800010U :
case 0x36808000U :
x = packet [ 4 ] - ( int ) ( ( packet [ 1 ] ^ 0x80 ) < < 1 ) ;
y = ( int ) ( ( packet [ 2 ] ^ 0x80 ) < < 1 ) - packet [ 5 ] ;
input_report_key ( tp_dev , BTN_LEFT , packet [ 0 ] & 0x01 ) ;
input_report_key ( tp_dev , BTN_RIGHT , packet [ 0 ] & 0x02 ) ;
input_report_key ( tp_dev , BTN_MIDDLE , packet [ 0 ] & 0x04 ) ;
input_report_rel ( tp_dev , REL_X , x ) ;
input_report_rel ( tp_dev , REL_Y , y ) ;
input_sync ( tp_dev ) ;
break ;
default :
/* Dump unexpected packet sequences if debug=1 (default) */
if ( etd - > debug = = 1 )
elantech_packet_dump ( psmouse ) ;
break ;
}
}
/*
* Interpret complete data packets and report absolute mode input events for
* hardware version 3. ( 12 byte packets for two fingers )
@ -715,6 +778,8 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
if ( ( packet [ 0 ] & 0x0c ) = = 0x0c & & ( packet [ 3 ] & 0xce ) = = 0x0c )
return PACKET_V3_TAIL ;
if ( ( packet [ 3 ] & 0x0f ) = = 0x06 )
return PACKET_TRACKPOINT ;
}
return PACKET_UNKNOWN ;
@ -791,14 +856,23 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
case 3 :
packet_type = elantech_packet_check_v3 ( psmouse ) ;
/* ignore debounce */
if ( packet_type = = PACKET_DEBOUNCE )
return PSMOUSE_FULL_PACKET ;
if ( packet_type = = PACKET_UNKNOWN )
switch ( packet_type ) {
case PACKET_UNKNOWN :
return PSMOUSE_BAD_DATA ;
elantech_report_absolute_v3 ( psmouse , packet_type ) ;
case PACKET_DEBOUNCE :
/* ignore debounce */
break ;
case PACKET_TRACKPOINT :
elantech_report_trackpoint ( psmouse , packet_type ) ;
break ;
default :
elantech_report_absolute_v3 ( psmouse , packet_type ) ;
break ;
}
break ;
case 4 :
@ -1018,8 +1092,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Asus UX31 0x361f00 20 , 15 , 0 e clickpad
* Asus UX32VD 0x361f02 00 , 15 , 0 e clickpad
* Avatar AVIU - 145 A2 0x361f00 ? clickpad
* Fujitsu H730 0x570f00 c0 , 14 , 0 c 3 hw buttons ( * * )
* Gigabyte U2442 0x450f01 58 , 17 , 0 c 2 hw buttons
* Lenovo L430 0x350f02 b9 , 15 , 0 c 2 hw buttons ( * )
* Lenovo L530 0x350f02 b9 , 15 , 0 c 2 hw buttons ( * )
* Samsung NF210 0x150b00 78 , 14 , 0 a 2 hw buttons
* Samsung NP770Z5E 0x575f01 10 , 15 , 0f clickpad
* Samsung NP700Z5B 0x361f06 21 , 15 , 0f clickpad
@ -1029,6 +1105,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* Samsung RF710 0x450f00 ? 2 hw buttons
* System76 Pangolin 0x250f01 ? 2 hw buttons
* ( * ) + 3 trackpoint buttons
* ( * * ) + 0 trackpoint buttons
* Note : Lenovo L430 and Lenovo L430 have the same fw_version / caps
*/
static void elantech_set_buttonpad_prop ( struct psmouse * psmouse )
{
@ -1324,6 +1402,10 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
*/
static void elantech_disconnect ( struct psmouse * psmouse )
{
struct elantech_data * etd = psmouse - > private ;
if ( etd - > tp_dev )
input_unregister_device ( etd - > tp_dev ) ;
sysfs_remove_group ( & psmouse - > ps2dev . serio - > dev . kobj ,
& elantech_attr_group ) ;
kfree ( psmouse - > private ) ;
@ -1438,8 +1520,10 @@ static int elantech_set_properties(struct elantech_data *etd)
int elantech_init ( struct psmouse * psmouse )
{
struct elantech_data * etd ;
int i , error ;
int i ;
int error = - EINVAL ;
unsigned char param [ 3 ] ;
struct input_dev * tp_dev ;
psmouse - > private = etd = kzalloc ( sizeof ( struct elantech_data ) , GFP_KERNEL ) ;
if ( ! etd )
@ -1498,14 +1582,49 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail ;
}
/* The MSB indicates the presence of the trackpoint */
if ( ( etd - > capabilities [ 0 ] & 0x80 ) = = 0x80 ) {
tp_dev = input_allocate_device ( ) ;
if ( ! tp_dev ) {
error = - ENOMEM ;
goto init_fail_tp_alloc ;
}
etd - > tp_dev = tp_dev ;
snprintf ( etd - > tp_phys , sizeof ( etd - > tp_phys ) , " %s/input1 " ,
psmouse - > ps2dev . serio - > phys ) ;
tp_dev - > phys = etd - > tp_phys ;
tp_dev - > name = " Elantech PS/2 TrackPoint " ;
tp_dev - > id . bustype = BUS_I8042 ;
tp_dev - > id . vendor = 0x0002 ;
tp_dev - > id . product = PSMOUSE_ELANTECH ;
tp_dev - > id . version = 0x0000 ;
tp_dev - > dev . parent = & psmouse - > ps2dev . serio - > dev ;
tp_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REL ) ;
tp_dev - > relbit [ BIT_WORD ( REL_X ) ] =
BIT_MASK ( REL_X ) | BIT_MASK ( REL_Y ) ;
tp_dev - > keybit [ BIT_WORD ( BTN_LEFT ) ] =
BIT_MASK ( BTN_LEFT ) | BIT_MASK ( BTN_MIDDLE ) |
BIT_MASK ( BTN_RIGHT ) ;
error = input_register_device ( etd - > tp_dev ) ;
if ( error < 0 )
goto init_fail_tp_reg ;
}
psmouse - > protocol_handler = elantech_process_byte ;
psmouse - > disconnect = elantech_disconnect ;
psmouse - > reconnect = elantech_reconnect ;
psmouse - > pktsize = etd - > hw_version > 1 ? 6 : 4 ;
return 0 ;
init_fail_tp_reg :
input_free_device ( tp_dev ) ;
init_fail_tp_alloc :
sysfs_remove_group ( & psmouse - > ps2dev . serio - > dev . kobj ,
& elantech_attr_group ) ;
init_fail :
psmouse_reset ( psmouse ) ;
kfree ( etd ) ;
return - 1 ;
return error ;
}