2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2018-01-14 02:33:49 +03:00
/*
* HID Driver for ELAN Touchpad
*
* Currently only supports touchpad found on HP Pavilion X2 10
*
* Copyright ( c ) 2016 Alexandrov Stanislav < neko @ nya . ai >
*/
# include <linux/hid.h>
# include <linux/input/mt.h>
# include <linux/leds.h>
# include <linux/module.h>
# include <linux/usb.h>
# include "hid-ids.h"
2018-07-11 13:38:37 +03:00
# define ELAN_MT_I2C 0x5d
2018-01-14 02:33:49 +03:00
# define ELAN_SINGLE_FINGER 0x81
# define ELAN_MT_FIRST_FINGER 0x82
# define ELAN_MT_SECOND_FINGER 0x83
# define ELAN_INPUT_REPORT_SIZE 8
2018-07-11 13:38:37 +03:00
# define ELAN_I2C_REPORT_SIZE 32
# define ELAN_FINGER_DATA_LEN 5
2018-07-11 13:38:32 +03:00
# define ELAN_MAX_FINGERS 5
2018-07-11 13:38:31 +03:00
# define ELAN_MAX_PRESSURE 255
2018-07-11 13:38:32 +03:00
# define ELAN_TP_USB_INTF 1
2018-01-14 02:33:49 +03:00
2018-07-11 13:38:33 +03:00
# define ELAN_FEATURE_REPORT 0x0d
# define ELAN_FEATURE_SIZE 5
# define ELAN_PARAM_MAX_X 6
# define ELAN_PARAM_MAX_Y 7
# define ELAN_PARAM_RES 8
2018-01-14 02:33:49 +03:00
# define ELAN_MUTE_LED_REPORT 0xBC
# define ELAN_LED_REPORT_SIZE 8
2018-07-11 13:38:35 +03:00
# define ELAN_HAS_LED BIT(0)
2018-01-14 02:33:49 +03:00
struct elan_drvdata {
struct input_dev * input ;
u8 prev_report [ ELAN_INPUT_REPORT_SIZE ] ;
struct led_classdev mute_led ;
u8 mute_led_state ;
2018-07-11 13:38:33 +03:00
u16 max_x ;
u16 max_y ;
2018-07-11 13:38:34 +03:00
u16 res_x ;
u16 res_y ;
2018-01-14 02:33:49 +03:00
} ;
static int is_not_elan_touchpad ( struct hid_device * hdev )
{
2018-07-11 13:38:37 +03:00
if ( hdev - > bus = = BUS_USB ) {
struct usb_interface * intf = to_usb_interface ( hdev - > dev . parent ) ;
2018-01-14 02:33:49 +03:00
2018-07-11 13:38:37 +03:00
return ( intf - > altsetting - > desc . bInterfaceNumber ! =
ELAN_TP_USB_INTF ) ;
}
return 0 ;
2018-01-14 02:33:49 +03:00
}
static int elan_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
if ( is_not_elan_touchpad ( hdev ) )
return 0 ;
if ( field - > report - > id = = ELAN_SINGLE_FINGER | |
field - > report - > id = = ELAN_MT_FIRST_FINGER | |
2018-07-11 13:38:37 +03:00
field - > report - > id = = ELAN_MT_SECOND_FINGER | |
field - > report - > id = = ELAN_MT_I2C )
2018-01-14 02:33:49 +03:00
return - 1 ;
return 0 ;
}
2018-07-11 13:38:33 +03:00
static int elan_get_device_param ( struct hid_device * hdev ,
unsigned char * dmabuf , unsigned char param )
{
int ret ;
dmabuf [ 0 ] = ELAN_FEATURE_REPORT ;
dmabuf [ 1 ] = 0x05 ;
dmabuf [ 2 ] = 0x03 ;
dmabuf [ 3 ] = param ;
dmabuf [ 4 ] = 0x01 ;
ret = hid_hw_raw_request ( hdev , ELAN_FEATURE_REPORT , dmabuf ,
ELAN_FEATURE_SIZE , HID_FEATURE_REPORT ,
HID_REQ_SET_REPORT ) ;
if ( ret ! = ELAN_FEATURE_SIZE ) {
hid_err ( hdev , " Set report error for parm %d: %d \n " , param , ret ) ;
return ret ;
}
ret = hid_hw_raw_request ( hdev , ELAN_FEATURE_REPORT , dmabuf ,
ELAN_FEATURE_SIZE , HID_FEATURE_REPORT ,
HID_REQ_GET_REPORT ) ;
if ( ret ! = ELAN_FEATURE_SIZE ) {
hid_err ( hdev , " Get report error for parm %d: %d \n " , param , ret ) ;
return ret ;
}
return 0 ;
}
2018-07-11 13:38:34 +03:00
static unsigned int elan_convert_res ( char val )
{
/*
* ( value from firmware ) * 10 + 790 = dpi
* dpi * 10 / 254 = dots / mm
*/
return ( val * 10 + 790 ) * 10 / 254 ;
}
2018-07-11 13:38:33 +03:00
static int elan_get_device_params ( struct hid_device * hdev )
{
struct elan_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
unsigned char * dmabuf ;
int ret ;
dmabuf = kmalloc ( ELAN_FEATURE_SIZE , GFP_KERNEL ) ;
if ( ! dmabuf )
return - ENOMEM ;
ret = elan_get_device_param ( hdev , dmabuf , ELAN_PARAM_MAX_X ) ;
if ( ret )
goto err ;
drvdata - > max_x = ( dmabuf [ 4 ] < < 8 ) | dmabuf [ 3 ] ;
ret = elan_get_device_param ( hdev , dmabuf , ELAN_PARAM_MAX_Y ) ;
if ( ret )
goto err ;
drvdata - > max_y = ( dmabuf [ 4 ] < < 8 ) | dmabuf [ 3 ] ;
2018-07-11 13:38:34 +03:00
ret = elan_get_device_param ( hdev , dmabuf , ELAN_PARAM_RES ) ;
if ( ret )
goto err ;
drvdata - > res_x = elan_convert_res ( dmabuf [ 3 ] ) ;
drvdata - > res_y = elan_convert_res ( dmabuf [ 4 ] ) ;
2018-07-11 13:38:33 +03:00
err :
kfree ( dmabuf ) ;
return ret ;
}
2018-01-14 02:33:49 +03:00
static int elan_input_configured ( struct hid_device * hdev , struct hid_input * hi )
{
int ret ;
struct input_dev * input ;
struct elan_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
if ( is_not_elan_touchpad ( hdev ) )
return 0 ;
2018-07-11 13:38:33 +03:00
ret = elan_get_device_params ( hdev ) ;
if ( ret )
return ret ;
2018-01-14 02:33:49 +03:00
input = devm_input_allocate_device ( & hdev - > dev ) ;
if ( ! input )
return - ENOMEM ;
input - > name = " Elan Touchpad " ;
input - > phys = hdev - > phys ;
input - > uniq = hdev - > uniq ;
input - > id . bustype = hdev - > bus ;
input - > id . vendor = hdev - > vendor ;
input - > id . product = hdev - > product ;
input - > id . version = hdev - > version ;
input - > dev . parent = & hdev - > dev ;
2018-07-11 13:38:33 +03:00
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 , drvdata - > max_x ,
0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 , drvdata - > max_y ,
0 , 0 ) ;
2018-07-11 13:38:31 +03:00
input_set_abs_params ( input , ABS_MT_PRESSURE , 0 , ELAN_MAX_PRESSURE ,
0 , 0 ) ;
2018-01-14 02:33:49 +03:00
__set_bit ( BTN_LEFT , input - > keybit ) ;
__set_bit ( INPUT_PROP_BUTTONPAD , input - > propbit ) ;
2018-07-11 13:38:32 +03:00
ret = input_mt_init_slots ( input , ELAN_MAX_FINGERS , INPUT_MT_POINTER ) ;
2018-01-14 02:33:49 +03:00
if ( ret ) {
hid_err ( hdev , " Failed to init elan MT slots: %d \n " , ret ) ;
2020-08-31 12:06:43 +03:00
input_free_device ( input ) ;
2018-01-14 02:33:49 +03:00
return ret ;
}
2018-07-11 13:38:34 +03:00
input_abs_set_res ( input , ABS_X , drvdata - > res_x ) ;
input_abs_set_res ( input , ABS_Y , drvdata - > res_y ) ;
2018-01-14 02:33:49 +03:00
ret = input_register_device ( input ) ;
if ( ret ) {
hid_err ( hdev , " Failed to register elan input device: %d \n " ,
ret ) ;
2020-08-31 12:06:43 +03:00
input_mt_destroy_slots ( input ) ;
2018-01-14 02:33:49 +03:00
input_free_device ( input ) ;
return ret ;
}
drvdata - > input = input ;
return 0 ;
}
static void elan_report_mt_slot ( struct elan_drvdata * drvdata , u8 * data ,
unsigned int slot_num )
{
struct input_dev * input = drvdata - > input ;
2018-07-11 13:38:31 +03:00
int x , y , p ;
2018-01-14 02:33:49 +03:00
bool active = ! ! data ;
input_mt_slot ( input , slot_num ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , active ) ;
if ( active ) {
x = ( ( data [ 0 ] & 0xF0 ) < < 4 ) | data [ 1 ] ;
2018-07-11 13:38:33 +03:00
y = drvdata - > max_y -
2018-01-14 02:33:49 +03:00
( ( ( data [ 0 ] & 0x07 ) < < 8 ) | data [ 2 ] ) ;
2018-07-11 13:38:31 +03:00
p = data [ 4 ] ;
2018-01-14 02:33:49 +03:00
input_report_abs ( input , ABS_MT_POSITION_X , x ) ;
input_report_abs ( input , ABS_MT_POSITION_Y , y ) ;
2018-07-11 13:38:31 +03:00
input_report_abs ( input , ABS_MT_PRESSURE , p ) ;
2018-01-14 02:33:49 +03:00
}
}
2018-07-11 13:38:37 +03:00
static void elan_usb_report_input ( struct elan_drvdata * drvdata , u8 * data )
2018-01-14 02:33:49 +03:00
{
int i ;
struct input_dev * input = drvdata - > input ;
/*
* There is 3 types of reports : for single touch ,
* for multitouch - first finger and for multitouch - second finger
*
* packet structure for ELAN_SINGLE_FINGER and ELAN_MT_FIRST_FINGER :
*
* byte 1 : 1 0 0 0 0 0 0 1 // 0x81 or 0x82
* byte 2 : 0 0 0 0 0 0 0 0 // looks like unused
* byte 3 : f5 f4 f3 f2 f1 0 0 L
* byte 4 : x12 x11 x10 x9 0 ? y11 y10 y9
* byte 5 : x8 x7 x6 x5 x4 x3 x2 x1
* byte 6 : y8 y7 y6 y5 y4 y3 y2 y1
* byte 7 : sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
2018-07-11 13:38:31 +03:00
* byte 8 : p8 p7 p6 p5 p4 p3 p2 p1
2018-01-14 02:33:49 +03:00
*
* packet structure for ELAN_MT_SECOND_FINGER :
*
* byte 1 : 1 0 0 0 0 0 1 1 // 0x83
* byte 2 : x12 x11 x10 x9 0 y11 y10 y9
* byte 3 : x8 x7 x6 x5 x4 x3 x2 x1
* byte 4 : y8 y7 y6 y5 y4 y3 y2 y1
* byte 5 : sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
2018-07-11 13:38:31 +03:00
* byte 6 : p8 p7 p6 p5 p4 p3 p2 p1
2018-01-14 02:33:49 +03:00
* byte 7 : 0 0 0 0 0 0 0 0
* byte 8 : 0 0 0 0 0 0 0 0
*
* f5 - f1 : finger touch bits
* L : clickpad button
2018-07-11 13:38:31 +03:00
* sy / sx : finger width / height expressed in traces , the total number
* of traces can be queried by doing a HID_REQ_SET_REPORT
* { 0x0d , 0x05 , 0x03 , 0x05 , 0x01 } followed by a GET , in the
* returned buf , buf [ 3 ] = no - x - traces , buf [ 4 ] = no - y - traces .
* p : pressure
2018-01-14 02:33:49 +03:00
*/
if ( data [ 0 ] = = ELAN_SINGLE_FINGER ) {
2018-07-11 13:38:32 +03:00
for ( i = 0 ; i < ELAN_MAX_FINGERS ; i + + ) {
2018-01-14 02:33:49 +03:00
if ( data [ 2 ] & BIT ( i + 3 ) )
elan_report_mt_slot ( drvdata , data + 3 , i ) ;
else
elan_report_mt_slot ( drvdata , NULL , i ) ;
}
input_report_key ( input , BTN_LEFT , data [ 2 ] & 0x01 ) ;
}
/*
* When touched with two fingers Elan touchpad will emit two HID reports
* first is ELAN_MT_FIRST_FINGER and second is ELAN_MT_SECOND_FINGER
* we will save ELAN_MT_FIRST_FINGER report and wait for
* ELAN_MT_SECOND_FINGER to finish multitouch
*/
if ( data [ 0 ] = = ELAN_MT_FIRST_FINGER ) {
memcpy ( drvdata - > prev_report , data ,
sizeof ( drvdata - > prev_report ) ) ;
return ;
}
if ( data [ 0 ] = = ELAN_MT_SECOND_FINGER ) {
int first = 0 ;
u8 * prev_report = drvdata - > prev_report ;
if ( prev_report [ 0 ] ! = ELAN_MT_FIRST_FINGER )
return ;
2018-07-11 13:38:32 +03:00
for ( i = 0 ; i < ELAN_MAX_FINGERS ; i + + ) {
2018-01-14 02:33:49 +03:00
if ( prev_report [ 2 ] & BIT ( i + 3 ) ) {
if ( ! first ) {
first = 1 ;
elan_report_mt_slot ( drvdata , prev_report + 3 , i ) ;
} else {
elan_report_mt_slot ( drvdata , data + 1 , i ) ;
}
} else {
elan_report_mt_slot ( drvdata , NULL , i ) ;
}
}
input_report_key ( input , BTN_LEFT , prev_report [ 2 ] & 0x01 ) ;
}
input_mt_sync_frame ( input ) ;
input_sync ( input ) ;
}
2018-07-11 13:38:37 +03:00
static void elan_i2c_report_input ( struct elan_drvdata * drvdata , u8 * data )
{
struct input_dev * input = drvdata - > input ;
u8 * finger_data ;
int i ;
/*
* Elan MT touchpads in i2c mode send finger data in the same format
* as in USB mode , but then with all fingers in a single packet .
*
* packet structure for ELAN_MT_I2C :
*
* byte 1 : 1 0 0 1 1 1 0 1 // 0x5d
* byte 2 : f5 f4 f3 f2 f1 0 0 L
* byte 3 : x12 x11 x10 x9 0 ? y11 y10 y9
* byte 4 : x8 x7 x6 x5 x4 x3 x2 x1
* byte 5 : y8 y7 y6 y5 y4 y3 y2 y1
* byte 6 : sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
* byte 7 : p8 p7 p6 p5 p4 p3 p2 p1
* byte 8 - 12 : Same as byte 3 - 7 for second finger down
* byte 13 - 17 : Same as byte 3 - 7 for third finger down
* byte 18 - 22 : Same as byte 3 - 7 for fourth finger down
* byte 23 - 27 : Same as byte 3 - 7 for fifth finger down
*/
finger_data = data + 2 ;
for ( i = 0 ; i < ELAN_MAX_FINGERS ; i + + ) {
if ( data [ 1 ] & BIT ( i + 3 ) ) {
elan_report_mt_slot ( drvdata , finger_data , i ) ;
finger_data + = ELAN_FINGER_DATA_LEN ;
} else {
elan_report_mt_slot ( drvdata , NULL , i ) ;
}
}
input_report_key ( input , BTN_LEFT , data [ 1 ] & 0x01 ) ;
input_mt_sync_frame ( input ) ;
input_sync ( input ) ;
}
2018-01-14 02:33:49 +03:00
static int elan_raw_event ( struct hid_device * hdev ,
struct hid_report * report , u8 * data , int size )
{
struct elan_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
if ( is_not_elan_touchpad ( hdev ) )
return 0 ;
if ( data [ 0 ] = = ELAN_SINGLE_FINGER | |
data [ 0 ] = = ELAN_MT_FIRST_FINGER | |
data [ 0 ] = = ELAN_MT_SECOND_FINGER ) {
if ( size = = ELAN_INPUT_REPORT_SIZE ) {
2018-07-11 13:38:37 +03:00
elan_usb_report_input ( drvdata , data ) ;
2018-01-14 02:33:49 +03:00
return 1 ;
}
}
2018-07-11 13:38:37 +03:00
if ( data [ 0 ] = = ELAN_MT_I2C & & size = = ELAN_I2C_REPORT_SIZE ) {
elan_i2c_report_input ( drvdata , data ) ;
return 1 ;
}
2018-01-14 02:33:49 +03:00
return 0 ;
}
static int elan_start_multitouch ( struct hid_device * hdev )
{
int ret ;
/*
* This byte sequence will enable multitouch mode and disable
* mouse emulation
*/
2019-01-25 19:05:46 +03:00
static const unsigned char buf [ ] = { 0x0D , 0x00 , 0x03 , 0x21 , 0x00 } ;
2018-01-14 02:33:49 +03:00
unsigned char * dmabuf = kmemdup ( buf , sizeof ( buf ) , GFP_KERNEL ) ;
if ( ! dmabuf )
return - ENOMEM ;
ret = hid_hw_raw_request ( hdev , dmabuf [ 0 ] , dmabuf , sizeof ( buf ) ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
kfree ( dmabuf ) ;
if ( ret ! = sizeof ( buf ) ) {
hid_err ( hdev , " Failed to start multitouch: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int elan_mute_led_set_brigtness ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
int ret ;
u8 led_state ;
struct device * dev = led_cdev - > dev - > parent ;
struct hid_device * hdev = to_hid_device ( dev ) ;
struct elan_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
unsigned char * dmabuf = kzalloc ( ELAN_LED_REPORT_SIZE , GFP_KERNEL ) ;
if ( ! dmabuf )
return - ENOMEM ;
led_state = ! ! value ;
dmabuf [ 0 ] = ELAN_MUTE_LED_REPORT ;
dmabuf [ 1 ] = 0x02 ;
dmabuf [ 2 ] = led_state ;
ret = hid_hw_raw_request ( hdev , dmabuf [ 0 ] , dmabuf , ELAN_LED_REPORT_SIZE ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
kfree ( dmabuf ) ;
if ( ret ! = ELAN_LED_REPORT_SIZE ) {
2021-04-04 10:52:50 +03:00
if ( ret ! = - ENODEV )
hid_err ( hdev , " Failed to set mute led brightness: %d \n " , ret ) ;
return ret < 0 ? ret : - EIO ;
2018-01-14 02:33:49 +03:00
}
drvdata - > mute_led_state = led_state ;
return 0 ;
}
static int elan_init_mute_led ( struct hid_device * hdev )
{
struct elan_drvdata * drvdata = hid_get_drvdata ( hdev ) ;
struct led_classdev * mute_led = & drvdata - > mute_led ;
mute_led - > name = " elan:red:mute " ;
2021-04-04 10:52:51 +03:00
mute_led - > default_trigger = " audio-mute " ;
2018-01-14 02:33:49 +03:00
mute_led - > brightness_set_blocking = elan_mute_led_set_brigtness ;
mute_led - > max_brightness = LED_ON ;
2021-04-04 10:52:50 +03:00
mute_led - > flags = LED_HW_PLUGGABLE ;
2018-01-14 02:33:49 +03:00
mute_led - > dev = & hdev - > dev ;
return devm_led_classdev_register ( & hdev - > dev , mute_led ) ;
}
static int elan_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
int ret ;
struct elan_drvdata * drvdata ;
drvdata = devm_kzalloc ( & hdev - > dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata )
return - ENOMEM ;
hid_set_drvdata ( hdev , drvdata ) ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " Hid Parse failed \n " ) ;
return ret ;
}
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( ret ) {
hid_err ( hdev , " Hid hw start failed \n " ) ;
return ret ;
}
if ( is_not_elan_touchpad ( hdev ) )
return 0 ;
if ( ! drvdata - > input ) {
2018-09-27 17:00:52 +03:00
hid_err ( hdev , " Input device is not registered \n " ) ;
2018-01-14 02:33:49 +03:00
ret = - ENAVAIL ;
goto err ;
}
ret = elan_start_multitouch ( hdev ) ;
if ( ret )
goto err ;
2018-07-11 13:38:35 +03:00
if ( id - > driver_data & ELAN_HAS_LED ) {
ret = elan_init_mute_led ( hdev ) ;
if ( ret )
goto err ;
}
2018-01-14 02:33:49 +03:00
return 0 ;
err :
hid_hw_stop ( hdev ) ;
return ret ;
}
static void elan_remove ( struct hid_device * hdev )
{
hid_hw_stop ( hdev ) ;
}
static const struct hid_device_id elan_devices [ ] = {
2018-07-11 13:38:36 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_ELAN , USB_DEVICE_ID_HP_X2 ) ,
. driver_data = ELAN_HAS_LED } ,
2018-07-11 13:38:35 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_ELAN , USB_DEVICE_ID_HP_X2_10_COVER ) ,
. driver_data = ELAN_HAS_LED } ,
2018-07-11 13:38:37 +03:00
{ HID_I2C_DEVICE ( USB_VENDOR_ID_ELAN , USB_DEVICE_ID_TOSHIBA_CLICK_L9W ) } ,
2018-01-14 02:33:49 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( hid , elan_devices ) ;
static struct hid_driver elan_driver = {
. name = " elan " ,
. id_table = elan_devices ,
. input_mapping = elan_input_mapping ,
. input_configured = elan_input_configured ,
. raw_event = elan_raw_event ,
. probe = elan_probe ,
. remove = elan_remove ,
} ;
module_hid_driver ( elan_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Alexandrov Stanislav " ) ;
MODULE_DESCRIPTION ( " Driver for HID ELAN Touchpads " ) ;