2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-02-15 16:40:43 +04:00
/*
2014-07-24 02:30:46 +04:00
* HID driver for Lenovo :
* - ThinkPad USB Keyboard with TrackPoint ( tpkbd )
2014-07-24 02:30:48 +04:00
* - ThinkPad Compact Bluetooth Keyboard with TrackPoint ( cptkbd )
* - ThinkPad Compact USB Keyboard with TrackPoint ( cptkbd )
2012-02-15 16:40:43 +04:00
*
* Copyright ( c ) 2012 Bernhard Seibold
2014-07-24 02:30:48 +04:00
* Copyright ( c ) 2014 Jamie Lentin < jm @ lentin . co . uk >
2018-04-12 20:36:47 +03:00
*
* Linux IBM / Lenovo Scrollpoint mouse driver :
* - IBM Scrollpoint III
* - IBM Scrollpoint Pro
* - IBM Scrollpoint Optical
* - IBM Scrollpoint Optical 800 dpi
* - IBM Scrollpoint Optical 800 dpi Pro
* - Lenovo Scrollpoint Optical
*
* Copyright ( c ) 2012 Peter De Wachter < pdewacht @ gmail . com >
* Copyright ( c ) 2018 Peter Ganzhorn < peter . ganzhorn @ gmail . com >
2012-02-15 16:40:43 +04:00
*/
/*
*/
# include <linux/module.h>
# include <linux/sysfs.h>
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/input.h>
# include <linux/leds.h>
2020-07-04 16:27:42 +03:00
# include <linux/workqueue.h>
2012-02-15 16:40:43 +04:00
# include "hid-ids.h"
2021-04-04 11:04:29 +03:00
/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
# define LENOVO_KEY_MICMUTE KEY_F20
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata {
2020-07-04 16:27:41 +03:00
u8 led_report [ 3 ] ; /* Must be first for proper alignment */
2012-02-15 16:40:43 +04:00
int led_state ;
2020-07-04 16:27:41 +03:00
struct mutex led_report_mutex ;
2012-02-15 16:40:43 +04:00
struct led_classdev led_mute ;
struct led_classdev led_micmute ;
2020-07-04 16:27:42 +03:00
struct work_struct fn_lock_sync_work ;
struct hid_device * hdev ;
2012-02-15 16:40:43 +04:00
int press_to_select ;
int dragging ;
int release_to_select ;
int select_right ;
int sensitivity ;
int press_speed ;
2015-08-12 00:40:52 +03:00
u8 middlebutton_state ; /* 0:Up, 1:Down (undecided), 2:Scrolling */
2014-07-24 02:30:48 +04:00
bool fn_lock ;
} ;
2012-02-15 16:40:43 +04:00
# define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
2020-07-04 16:27:41 +03:00
# define TP10UBKBD_LED_OUTPUT_REPORT 9
# define TP10UBKBD_FN_LOCK_LED 0x54
# define TP10UBKBD_MUTE_LED 0x64
# define TP10UBKBD_MICMUTE_LED 0x74
# define TP10UBKBD_LED_OFF 1
# define TP10UBKBD_LED_ON 2
2021-04-04 11:04:25 +03:00
static int lenovo_led_set_tp10ubkbd ( struct hid_device * hdev , u8 led_code ,
enum led_brightness value )
2020-07-04 16:27:41 +03:00
{
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
int ret ;
mutex_lock ( & data - > led_report_mutex ) ;
data - > led_report [ 0 ] = TP10UBKBD_LED_OUTPUT_REPORT ;
data - > led_report [ 1 ] = led_code ;
data - > led_report [ 2 ] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF ;
ret = hid_hw_raw_request ( hdev , data - > led_report [ 0 ] , data - > led_report , 3 ,
HID_OUTPUT_REPORT , HID_REQ_SET_REPORT ) ;
2021-04-04 11:04:25 +03:00
if ( ret ! = 3 ) {
if ( ret ! = - ENODEV )
hid_err ( hdev , " Set LED output report error: %d \n " , ret ) ;
ret = ret < 0 ? ret : - EIO ;
} else {
ret = 0 ;
}
2020-07-04 16:27:41 +03:00
mutex_unlock ( & data - > led_report_mutex ) ;
2021-04-04 11:04:25 +03:00
return ret ;
2020-07-04 16:27:41 +03:00
}
2020-07-04 16:27:42 +03:00
static void lenovo_tp10ubkbd_sync_fn_lock ( struct work_struct * work )
{
struct lenovo_drvdata * data =
container_of ( work , struct lenovo_drvdata , fn_lock_sync_work ) ;
lenovo_led_set_tp10ubkbd ( data - > hdev , TP10UBKBD_FN_LOCK_LED ,
data - > fn_lock ) ;
}
2015-05-01 23:22:45 +03:00
static const __u8 lenovo_pro_dock_need_fixup_collection [ ] = {
0x05 , 0x88 , /* Usage Page (Vendor Usage Page 0x88) */
0x09 , 0x01 , /* Usage (Vendor Usage 0x01) */
0xa1 , 0x01 , /* Collection (Application) */
0x85 , 0x04 , /* Report ID (4) */
0x19 , 0x00 , /* Usage Minimum (0) */
0x2a , 0xff , 0xff , /* Usage Maximum (65535) */
} ;
static __u8 * lenovo_report_fixup ( struct hid_device * hdev , __u8 * rdesc ,
unsigned int * rsize )
{
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPPRODOCK :
/* the fixups that need to be done:
* - get a reasonable usage max for the vendor collection
* 0x8801 from the report ID 4
*/
if ( * rsize > = 153 & &
memcmp ( & rdesc [ 140 ] , lenovo_pro_dock_need_fixup_collection ,
sizeof ( lenovo_pro_dock_need_fixup_collection ) ) = = 0 ) {
rdesc [ 151 ] = 0x01 ;
rdesc [ 152 ] = 0x00 ;
}
break ;
}
return rdesc ;
}
2014-07-24 02:30:45 +04:00
static int lenovo_input_mapping_tpkbd ( struct hid_device * hdev ,
2012-02-15 16:40:43 +04:00
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
2013-09-12 00:12:24 +04:00
if ( usage - > hid = = ( HID_UP_BUTTON | 0x0010 ) ) {
2014-07-24 02:30:46 +04:00
/* This sub-device contains trackpoint, mark it */
2013-09-12 00:12:24 +04:00
hid_set_drvdata ( hdev , ( void * ) 1 ) ;
2021-04-04 11:04:29 +03:00
map_key_clear ( LENOVO_KEY_MICMUTE ) ;
2012-02-15 16:40:43 +04:00
return 1 ;
}
return 0 ;
}
2014-07-24 02:30:48 +04:00
static int lenovo_input_mapping_cptkbd ( struct hid_device * hdev ,
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
/* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
if ( ( usage - > hid & HID_USAGE_PAGE ) = = HID_UP_MSVENDOR | |
( usage - > hid & HID_USAGE_PAGE ) = = HID_UP_LNVENDOR ) {
switch ( usage - > hid & HID_USAGE ) {
case 0x00f1 : /* Fn-F4: Mic mute */
2021-04-04 11:04:29 +03:00
map_key_clear ( LENOVO_KEY_MICMUTE ) ;
2014-07-24 02:30:48 +04:00
return 1 ;
case 0x00f2 : /* Fn-F5: Brightness down */
map_key_clear ( KEY_BRIGHTNESSDOWN ) ;
return 1 ;
case 0x00f3 : /* Fn-F6: Brightness up */
map_key_clear ( KEY_BRIGHTNESSUP ) ;
return 1 ;
case 0x00f4 : /* Fn-F7: External display (projector) */
map_key_clear ( KEY_SWITCHVIDEOMODE ) ;
return 1 ;
case 0x00f5 : /* Fn-F8: Wireless */
map_key_clear ( KEY_WLAN ) ;
return 1 ;
case 0x00f6 : /* Fn-F9: Control panel */
map_key_clear ( KEY_CONFIG ) ;
return 1 ;
case 0x00f8 : /* Fn-F11: View open applications (3 boxes) */
map_key_clear ( KEY_SCALE ) ;
return 1 ;
2014-11-09 10:54:29 +03:00
case 0x00f9 : /* Fn-F12: Open My computer (6 boxes) USB-only */
2014-07-24 02:30:48 +04:00
/* NB: This mapping is invented in raw_event below */
map_key_clear ( KEY_FILE ) ;
return 1 ;
2014-11-09 10:54:29 +03:00
case 0x00fa : /* Fn-Esc: Fn-lock toggle */
map_key_clear ( KEY_FN_ESC ) ;
return 1 ;
2014-12-17 00:26:46 +03:00
case 0x00fb : /* Middle mouse button (in native mode) */
map_key_clear ( BTN_MIDDLE ) ;
return 1 ;
}
}
/* Compatibility middle/wheel mappings should be ignored */
if ( usage - > hid = = HID_GD_WHEEL )
return - 1 ;
if ( ( usage - > hid & HID_USAGE_PAGE ) = = HID_UP_BUTTON & &
( usage - > hid & HID_USAGE ) = = 0x003 )
return - 1 ;
if ( ( usage - > hid & HID_USAGE_PAGE ) = = HID_UP_CONSUMER & &
( usage - > hid & HID_USAGE ) = = 0x238 )
return - 1 ;
/* Map wheel emulation reports: 0xffa1 = USB, 0xff10 = BT */
if ( ( usage - > hid & HID_USAGE_PAGE ) = = 0xff100000 | |
( usage - > hid & HID_USAGE_PAGE ) = = 0xffa10000 ) {
field - > flags | = HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE ;
field - > logical_minimum = - 127 ;
field - > logical_maximum = 127 ;
switch ( usage - > hid & HID_USAGE ) {
case 0x0000 :
2015-08-12 00:40:50 +03:00
hid_map_usage ( hi , usage , bit , max , EV_REL , REL_HWHEEL ) ;
2014-12-17 00:26:46 +03:00
return 1 ;
case 0x0001 :
2015-08-12 00:40:50 +03:00
hid_map_usage ( hi , usage , bit , max , EV_REL , REL_WHEEL ) ;
2014-12-17 00:26:46 +03:00
return 1 ;
default :
return - 1 ;
2014-07-24 02:30:48 +04:00
}
}
return 0 ;
}
2018-04-12 20:36:47 +03:00
static int lenovo_input_mapping_scrollpoint ( struct hid_device * hdev ,
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
if ( usage - > hid = = HID_GD_Z ) {
hid_map_usage ( hi , usage , bit , max , EV_REL , REL_HWHEEL ) ;
return 1 ;
}
return 0 ;
}
2020-07-04 16:27:41 +03:00
static int lenovo_input_mapping_tp10_ultrabook_kbd ( struct hid_device * hdev ,
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
/*
* The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for
* a bunch of keys which have no standard consumer page code .
*/
if ( usage - > hid = = 0x000c0001 ) {
switch ( usage - > usage_index ) {
case 8 : /* Fn-Esc: Fn-lock toggle */
map_key_clear ( KEY_FN_ESC ) ;
return 1 ;
case 9 : /* Fn-F4: Mic mute */
2021-04-04 11:04:29 +03:00
map_key_clear ( LENOVO_KEY_MICMUTE ) ;
2020-07-04 16:27:41 +03:00
return 1 ;
case 10 : /* Fn-F7: Control panel */
map_key_clear ( KEY_CONFIG ) ;
return 1 ;
case 11 : /* Fn-F8: Search (magnifier glass) */
map_key_clear ( KEY_SEARCH ) ;
return 1 ;
case 12 : /* Fn-F10: Open My computer (6 boxes) */
map_key_clear ( KEY_FILE ) ;
return 1 ;
}
}
2020-07-04 23:10:59 +03:00
/*
* The Ultrabook Keyboard sends a spurious F23 key - press when resuming
* from suspend and it does not actually have a F23 key , ignore it .
*/
if ( usage - > hid = = 0x00070072 )
return - 1 ;
2020-07-04 16:27:41 +03:00
return 0 ;
}
2021-04-04 11:04:32 +03:00
static int lenovo_input_mapping_x1_tab_kbd ( struct hid_device * hdev ,
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
/*
* The ThinkPad X1 Tablet Thin Keyboard uses 0x000c0001 usage for
* a bunch of keys which have no standard consumer page code .
*/
if ( usage - > hid = = 0x000c0001 ) {
switch ( usage - > usage_index ) {
case 0 : /* Fn-F10: Enable/disable bluetooth */
map_key_clear ( KEY_BLUETOOTH ) ;
return 1 ;
case 1 : /* Fn-F11: Keyboard settings */
map_key_clear ( KEY_KEYBOARD ) ;
return 1 ;
case 2 : /* Fn-F12: User function / Cortana */
map_key_clear ( KEY_MACRO1 ) ;
return 1 ;
case 3 : /* Fn-PrtSc: Snipping tool */
map_key_clear ( KEY_SELECTIVE_SCREENSHOT ) ;
return 1 ;
case 8 : /* Fn-Esc: Fn-lock toggle */
map_key_clear ( KEY_FN_ESC ) ;
return 1 ;
case 9 : /* Fn-F4: Mute/unmute microphone */
map_key_clear ( KEY_MICMUTE ) ;
return 1 ;
case 10 : /* Fn-F9: Settings */
map_key_clear ( KEY_CONFIG ) ;
return 1 ;
case 13 : /* Fn-F7: Manage external displays */
map_key_clear ( KEY_SWITCHVIDEOMODE ) ;
return 1 ;
case 14 : /* Fn-F8: Enable/disable wifi */
map_key_clear ( KEY_WLAN ) ;
return 1 ;
}
}
if ( usage - > hid = = ( HID_UP_KEYBOARD | 0x009a ) ) {
map_key_clear ( KEY_SYSRQ ) ;
return 1 ;
}
return 0 ;
}
2014-07-24 02:30:46 +04:00
static int lenovo_input_mapping ( struct hid_device * hdev ,
struct hid_input * hi , struct hid_field * field ,
struct hid_usage * usage , unsigned long * * bit , int * max )
{
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPKBD :
return lenovo_input_mapping_tpkbd ( hdev , hi , field ,
usage , bit , max ) ;
2014-07-24 02:30:48 +04:00
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
return lenovo_input_mapping_cptkbd ( hdev , hi , field ,
usage , bit , max ) ;
2018-04-12 20:36:47 +03:00
case USB_DEVICE_ID_IBM_SCROLLPOINT_III :
case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO :
case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL :
case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL :
case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO :
case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL :
return lenovo_input_mapping_scrollpoint ( hdev , hi , field ,
usage , bit , max ) ;
2020-07-04 16:27:41 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
return lenovo_input_mapping_tp10_ultrabook_kbd ( hdev , hi , field ,
usage , bit , max ) ;
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
return lenovo_input_mapping_x1_tab_kbd ( hdev , hi , field , usage , bit , max ) ;
2014-07-24 02:30:46 +04:00
default :
return 0 ;
}
}
2012-02-15 16:40:43 +04:00
# undef map_key_clear
2014-07-24 02:30:48 +04:00
/* Send a config command to the keyboard */
static int lenovo_send_cmd_cptkbd ( struct hid_device * hdev ,
unsigned char byte2 , unsigned char byte3 )
{
int ret ;
2016-03-28 16:22:14 +03:00
unsigned char * buf ;
buf = kzalloc ( 3 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
buf [ 0 ] = 0x18 ;
buf [ 1 ] = byte2 ;
buf [ 2 ] = byte3 ;
2014-07-24 02:30:48 +04:00
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_CUSBKBD :
2016-03-28 16:22:14 +03:00
ret = hid_hw_raw_request ( hdev , 0x13 , buf , 3 ,
2014-07-24 02:30:48 +04:00
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
break ;
case USB_DEVICE_ID_LENOVO_CBTKBD :
2016-03-28 16:22:14 +03:00
ret = hid_hw_output_report ( hdev , buf , 3 ) ;
2014-07-24 02:30:48 +04:00
break ;
default :
ret = - EINVAL ;
break ;
}
2016-03-28 16:22:14 +03:00
kfree ( buf ) ;
2014-07-24 02:30:48 +04:00
return ret < 0 ? ret : 0 ; /* BT returns 0, USB returns sizeof(buf) */
}
static void lenovo_features_set_cptkbd ( struct hid_device * hdev )
{
int ret ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * cptkbd_data = hid_get_drvdata ( hdev ) ;
2014-07-24 02:30:48 +04:00
ret = lenovo_send_cmd_cptkbd ( hdev , 0x05 , cptkbd_data - > fn_lock ) ;
if ( ret )
hid_err ( hdev , " Fn-lock setting failed: %d \n " , ret ) ;
2015-08-12 00:40:51 +03:00
ret = lenovo_send_cmd_cptkbd ( hdev , 0x02 , cptkbd_data - > sensitivity ) ;
if ( ret )
hid_err ( hdev , " Sensitivity setting failed: %d \n " , ret ) ;
2014-07-24 02:30:48 +04:00
}
2020-07-04 16:27:40 +03:00
static ssize_t attr_fn_lock_show ( struct device * dev ,
2014-07-24 02:30:48 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:40 +03:00
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
2014-07-24 02:30:48 +04:00
2020-07-04 16:27:40 +03:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , data - > fn_lock ) ;
2014-07-24 02:30:48 +04:00
}
2020-07-04 16:27:40 +03:00
static ssize_t attr_fn_lock_store ( struct device * dev ,
2014-07-24 02:30:48 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:40 +03:00
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
2021-04-04 11:04:25 +03:00
int value , ret ;
2014-07-24 02:30:48 +04:00
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
2020-07-04 16:27:40 +03:00
data - > fn_lock = ! ! value ;
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
lenovo_features_set_cptkbd ( hdev ) ;
break ;
2020-07-04 16:27:42 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
2021-04-04 11:04:25 +03:00
ret = lenovo_led_set_tp10ubkbd ( hdev , TP10UBKBD_FN_LOCK_LED , value ) ;
if ( ret )
return ret ;
2020-07-04 16:27:42 +03:00
break ;
2020-07-04 16:27:40 +03:00
}
2014-07-24 02:30:48 +04:00
return count ;
}
2014-12-17 00:26:45 +03:00
static ssize_t attr_sensitivity_show_cptkbd ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * cptkbd_data = hid_get_drvdata ( hdev ) ;
2014-12-17 00:26:45 +03:00
return snprintf ( buf , PAGE_SIZE , " %u \n " ,
cptkbd_data - > sensitivity ) ;
}
static ssize_t attr_sensitivity_store_cptkbd ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * cptkbd_data = hid_get_drvdata ( hdev ) ;
2014-12-17 00:26:45 +03:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) | | value < 1 | | value > 255 )
return - EINVAL ;
cptkbd_data - > sensitivity = value ;
lenovo_features_set_cptkbd ( hdev ) ;
return count ;
}
2020-07-04 16:27:40 +03:00
static struct device_attribute dev_attr_fn_lock =
2014-07-24 02:30:48 +04:00
__ATTR ( fn_lock , S_IWUSR | S_IRUGO ,
2020-07-04 16:27:40 +03:00
attr_fn_lock_show ,
attr_fn_lock_store ) ;
2014-07-24 02:30:48 +04:00
2014-12-17 00:26:45 +03:00
static struct device_attribute dev_attr_sensitivity_cptkbd =
__ATTR ( sensitivity , S_IWUSR | S_IRUGO ,
attr_sensitivity_show_cptkbd ,
attr_sensitivity_store_cptkbd ) ;
2014-07-24 02:30:48 +04:00
static struct attribute * lenovo_attributes_cptkbd [ ] = {
2020-07-04 16:27:40 +03:00
& dev_attr_fn_lock . attr ,
2014-12-17 00:26:45 +03:00
& dev_attr_sensitivity_cptkbd . attr ,
2014-07-24 02:30:48 +04:00
NULL
} ;
static const struct attribute_group lenovo_attr_group_cptkbd = {
. attrs = lenovo_attributes_cptkbd ,
} ;
static int lenovo_raw_event ( struct hid_device * hdev ,
struct hid_report * report , u8 * data , int size )
{
/*
* Compact USB keyboard ' s Fn - F12 report holds down many other keys , and
* its own key is outside the usage page range . Remove extra
* keypresses and remap to inside usage page .
*/
if ( unlikely ( hdev - > product = = USB_DEVICE_ID_LENOVO_CUSBKBD
& & size = = 3
& & data [ 0 ] = = 0x15
& & data [ 1 ] = = 0x94
& & data [ 2 ] = = 0x01 ) ) {
2014-11-09 10:54:29 +03:00
data [ 1 ] = 0x00 ;
data [ 2 ] = 0x01 ;
2014-07-24 02:30:48 +04:00
}
return 0 ;
}
2020-07-04 16:27:42 +03:00
static int lenovo_event_tp10ubkbd ( struct hid_device * hdev ,
struct hid_field * field , struct hid_usage * usage , __s32 value )
{
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
if ( usage - > type = = EV_KEY & & usage - > code = = KEY_FN_ESC & & value = = 1 ) {
/*
* The user has toggled the Fn - lock state . Toggle our own
* cached value of it and sync our value to the keyboard to
* ensure things are in sync ( the sycning should be a no - op ) .
*/
data - > fn_lock = ! data - > fn_lock ;
schedule_work ( & data - > fn_lock_sync_work ) ;
}
return 0 ;
}
2015-08-12 00:40:52 +03:00
static int lenovo_event_cptkbd ( struct hid_device * hdev ,
struct hid_field * field , struct hid_usage * usage , __s32 value )
{
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * cptkbd_data = hid_get_drvdata ( hdev ) ;
2015-08-12 00:40:52 +03:00
/* "wheel" scroll events */
if ( usage - > type = = EV_REL & & ( usage - > code = = REL_WHEEL | |
usage - > code = = REL_HWHEEL ) ) {
/* Scroll events disable middle-click event */
cptkbd_data - > middlebutton_state = 2 ;
return 0 ;
}
/* Middle click events */
if ( usage - > type = = EV_KEY & & usage - > code = = BTN_MIDDLE ) {
if ( value = = 1 ) {
cptkbd_data - > middlebutton_state = 1 ;
} else if ( value = = 0 ) {
if ( cptkbd_data - > middlebutton_state = = 1 ) {
/* No scrolling inbetween, send middle-click */
input_event ( field - > hidinput - > input ,
EV_KEY , BTN_MIDDLE , 1 ) ;
input_sync ( field - > hidinput - > input ) ;
input_event ( field - > hidinput - > input ,
EV_KEY , BTN_MIDDLE , 0 ) ;
input_sync ( field - > hidinput - > input ) ;
}
cptkbd_data - > middlebutton_state = 0 ;
}
return 1 ;
}
return 0 ;
}
static int lenovo_event ( struct hid_device * hdev , struct hid_field * field ,
struct hid_usage * usage , __s32 value )
{
2021-04-04 11:04:26 +03:00
if ( ! hid_get_drvdata ( hdev ) )
return 0 ;
2015-08-12 00:40:52 +03:00
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
return lenovo_event_cptkbd ( hdev , field , usage , value ) ;
2020-07-04 16:27:42 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
2020-07-04 16:27:42 +03:00
return lenovo_event_tp10ubkbd ( hdev , field , usage , value ) ;
2015-08-12 00:40:52 +03:00
default :
return 0 ;
}
}
2014-07-24 02:30:45 +04:00
static int lenovo_features_set_tpkbd ( struct hid_device * hdev )
2012-02-15 16:40:43 +04:00
{
struct hid_report * report ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
report = hdev - > report_enum [ HID_FEATURE_REPORT ] . report_id_hash [ 4 ] ;
report - > field [ 0 ] - > value [ 0 ] = data_pointer - > press_to_select ? 0x01 : 0x02 ;
report - > field [ 0 ] - > value [ 0 ] | = data_pointer - > dragging ? 0x04 : 0x08 ;
report - > field [ 0 ] - > value [ 0 ] | = data_pointer - > release_to_select ? 0x10 : 0x20 ;
report - > field [ 0 ] - > value [ 0 ] | = data_pointer - > select_right ? 0x80 : 0x40 ;
report - > field [ 1 ] - > value [ 0 ] = 0x03 ; // unknown setting, imitate windows driver
report - > field [ 2 ] - > value [ 0 ] = data_pointer - > sensitivity ;
report - > field [ 3 ] - > value [ 0 ] = data_pointer - > press_speed ;
2013-02-25 14:31:46 +04:00
hid_hw_request ( hdev , report , HID_REQ_SET_REPORT ) ;
2012-02-15 16:40:43 +04:00
return 0 ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_press_to_select_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , data_pointer - > press_to_select ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_press_to_select_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
data_pointer - > press_to_select = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_dragging_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , data_pointer - > dragging ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_dragging_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
data_pointer - > dragging = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_release_to_select_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , data_pointer - > release_to_select ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_release_to_select_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
data_pointer - > release_to_select = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_select_right_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " , data_pointer - > select_right ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_select_right_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
data_pointer - > select_right = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_sensitivity_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " ,
data_pointer - > sensitivity ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_sensitivity_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) | | value < 1 | | value > 255 )
return - EINVAL ;
data_pointer - > sensitivity = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_press_speed_show_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
char * buf )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
return snprintf ( buf , PAGE_SIZE , " %u \n " ,
data_pointer - > press_speed ) ;
}
2014-07-24 02:30:45 +04:00
static ssize_t attr_press_speed_store_tpkbd ( struct device * dev ,
2012-02-15 16:40:43 +04:00
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int value ;
if ( kstrtoint ( buf , 10 , & value ) | | value < 1 | | value > 255 )
return - EINVAL ;
data_pointer - > press_speed = value ;
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return count ;
}
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_press_to_select_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( press_to_select , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_press_to_select_show_tpkbd ,
attr_press_to_select_store_tpkbd ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_dragging_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( dragging , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_dragging_show_tpkbd ,
attr_dragging_store_tpkbd ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_release_to_select_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( release_to_select , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_release_to_select_show_tpkbd ,
attr_release_to_select_store_tpkbd ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_select_right_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( select_right , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_select_right_show_tpkbd ,
attr_select_right_store_tpkbd ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_sensitivity_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( sensitivity , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_sensitivity_show_tpkbd ,
attr_sensitivity_store_tpkbd ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct device_attribute dev_attr_press_speed_tpkbd =
2012-02-15 16:40:43 +04:00
__ATTR ( press_speed , S_IWUSR | S_IRUGO ,
2014-07-24 02:30:45 +04:00
attr_press_speed_show_tpkbd ,
attr_press_speed_store_tpkbd ) ;
static struct attribute * lenovo_attributes_tpkbd [ ] = {
& dev_attr_press_to_select_tpkbd . attr ,
& dev_attr_dragging_tpkbd . attr ,
& dev_attr_release_to_select_tpkbd . attr ,
& dev_attr_select_right_tpkbd . attr ,
& dev_attr_sensitivity_tpkbd . attr ,
& dev_attr_press_speed_tpkbd . attr ,
2012-02-15 16:40:43 +04:00
NULL
} ;
2014-07-24 02:30:45 +04:00
static const struct attribute_group lenovo_attr_group_tpkbd = {
. attrs = lenovo_attributes_tpkbd ,
2012-02-15 16:40:43 +04:00
} ;
2020-07-04 16:27:39 +03:00
static void lenovo_led_set_tpkbd ( struct hid_device * hdev )
{
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
struct hid_report * report ;
report = hdev - > report_enum [ HID_OUTPUT_REPORT ] . report_id_hash [ 3 ] ;
report - > field [ 0 ] - > value [ 0 ] = ( data_pointer - > led_state > > 0 ) & 1 ;
report - > field [ 0 ] - > value [ 1 ] = ( data_pointer - > led_state > > 1 ) & 1 ;
hid_hw_request ( hdev , report , HID_REQ_SET_REPORT ) ;
}
2021-04-04 11:04:24 +03:00
static int lenovo_led_brightness_set ( struct led_classdev * led_cdev ,
2012-02-15 16:40:43 +04:00
enum led_brightness value )
{
2012-09-13 10:12:08 +04:00
struct device * dev = led_cdev - > dev - > parent ;
2015-12-27 12:25:21 +03:00
struct hid_device * hdev = to_hid_device ( dev ) ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2020-07-04 16:27:41 +03:00
u8 tp10ubkbd_led [ ] = { TP10UBKBD_MUTE_LED , TP10UBKBD_MICMUTE_LED } ;
2012-02-15 16:40:43 +04:00
int led_nr = 0 ;
2021-04-04 11:04:25 +03:00
int ret = 0 ;
2012-02-15 16:40:43 +04:00
if ( led_cdev = = & data_pointer - > led_micmute )
led_nr = 1 ;
if ( value = = LED_OFF )
data_pointer - > led_state & = ~ ( 1 < < led_nr ) ;
else
data_pointer - > led_state | = 1 < < led_nr ;
2020-07-04 16:27:39 +03:00
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPKBD :
lenovo_led_set_tpkbd ( hdev ) ;
break ;
2020-07-04 16:27:41 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
2021-04-04 11:04:25 +03:00
ret = lenovo_led_set_tp10ubkbd ( hdev , tp10ubkbd_led [ led_nr ] , value ) ;
2020-07-04 16:27:41 +03:00
break ;
2020-07-04 16:27:39 +03:00
}
2021-04-04 11:04:24 +03:00
2021-04-04 11:04:25 +03:00
return ret ;
2020-07-04 16:27:39 +03:00
}
static int lenovo_register_leds ( struct hid_device * hdev )
{
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
size_t name_sz = strlen ( dev_name ( & hdev - > dev ) ) + 16 ;
char * name_mute , * name_micm ;
int ret ;
name_mute = devm_kzalloc ( & hdev - > dev , name_sz , GFP_KERNEL ) ;
name_micm = devm_kzalloc ( & hdev - > dev , name_sz , GFP_KERNEL ) ;
if ( name_mute = = NULL | | name_micm = = NULL ) {
hid_err ( hdev , " Could not allocate memory for led data \n " ) ;
return - ENOMEM ;
}
snprintf ( name_mute , name_sz , " %s:amber:mute " , dev_name ( & hdev - > dev ) ) ;
snprintf ( name_micm , name_sz , " %s:amber:micmute " , dev_name ( & hdev - > dev ) ) ;
data - > led_mute . name = name_mute ;
2021-04-04 11:04:30 +03:00
data - > led_mute . default_trigger = " audio-mute " ;
2021-04-04 11:04:24 +03:00
data - > led_mute . brightness_set_blocking = lenovo_led_brightness_set ;
2021-04-04 11:04:28 +03:00
data - > led_mute . max_brightness = 1 ;
2021-04-04 11:04:25 +03:00
data - > led_mute . flags = LED_HW_PLUGGABLE ;
2020-07-04 16:27:39 +03:00
data - > led_mute . dev = & hdev - > dev ;
ret = led_classdev_register ( & hdev - > dev , & data - > led_mute ) ;
if ( ret < 0 )
return ret ;
data - > led_micmute . name = name_micm ;
2021-04-04 11:04:30 +03:00
data - > led_micmute . default_trigger = " audio-micmute " ;
2021-04-04 11:04:24 +03:00
data - > led_micmute . brightness_set_blocking = lenovo_led_brightness_set ;
2021-04-04 11:04:28 +03:00
data - > led_micmute . max_brightness = 1 ;
2021-04-04 11:04:25 +03:00
data - > led_micmute . flags = LED_HW_PLUGGABLE ;
2020-07-04 16:27:39 +03:00
data - > led_micmute . dev = & hdev - > dev ;
ret = led_classdev_register ( & hdev - > dev , & data - > led_micmute ) ;
if ( ret < 0 ) {
led_classdev_unregister ( & data - > led_mute ) ;
return ret ;
}
return 0 ;
2012-02-15 16:40:43 +04:00
}
2014-07-24 02:30:45 +04:00
static int lenovo_probe_tpkbd ( struct hid_device * hdev )
2012-02-15 16:40:43 +04:00
{
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer ;
2020-07-04 16:27:39 +03:00
int i , ret ;
2013-09-11 23:56:55 +04:00
2014-07-24 02:30:46 +04:00
/*
* Only register extra settings against subdevice where input_mapping
* set drvdata to 1 , i . e . the trackpoint .
*/
if ( ! hid_get_drvdata ( hdev ) )
return 0 ;
hid_set_drvdata ( hdev , NULL ) ;
2013-09-11 23:56:55 +04:00
/* Validate required reports. */
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! hid_validate_values ( hdev , HID_FEATURE_REPORT , 4 , i , 1 ) )
return - ENODEV ;
}
if ( ! hid_validate_values ( hdev , HID_OUTPUT_REPORT , 3 , 0 , 2 ) )
return - ENODEV ;
2012-02-15 16:40:43 +04:00
2014-07-26 18:42:27 +04:00
ret = sysfs_create_group ( & hdev - > dev . kobj , & lenovo_attr_group_tpkbd ) ;
if ( ret )
hid_warn ( hdev , " Could not create sysfs group: %d \n " , ret ) ;
2012-02-15 16:40:43 +04:00
2013-09-12 00:12:23 +04:00
data_pointer = devm_kzalloc ( & hdev - > dev ,
2020-07-04 16:27:38 +03:00
sizeof ( struct lenovo_drvdata ) ,
2013-09-12 00:12:23 +04:00
GFP_KERNEL ) ;
2012-02-15 16:40:43 +04:00
if ( data_pointer = = NULL ) {
hid_err ( hdev , " Could not allocate memory for driver data \n " ) ;
2015-05-29 03:39:31 +03:00
ret = - ENOMEM ;
goto err ;
2012-02-15 16:40:43 +04:00
}
// set same default values as windows driver
data_pointer - > sensitivity = 0xa0 ;
data_pointer - > press_speed = 0x38 ;
hid_set_drvdata ( hdev , data_pointer ) ;
2020-07-04 16:27:39 +03:00
ret = lenovo_register_leds ( hdev ) ;
if ( ret )
2018-12-25 00:39:14 +03:00
goto err ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
lenovo_features_set_tpkbd ( hdev ) ;
2012-02-15 16:40:43 +04:00
return 0 ;
2015-05-29 03:39:31 +03:00
err :
sysfs_remove_group ( & hdev - > dev . kobj , & lenovo_attr_group_tpkbd ) ;
return ret ;
2012-02-15 16:40:43 +04:00
}
2014-07-24 02:30:48 +04:00
static int lenovo_probe_cptkbd ( struct hid_device * hdev )
{
int ret ;
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * cptkbd_data ;
2014-07-24 02:30:48 +04:00
/* All the custom action happens on the USBMOUSE device for USB */
if ( hdev - > product = = USB_DEVICE_ID_LENOVO_CUSBKBD
& & hdev - > type ! = HID_TYPE_USBMOUSE ) {
hid_dbg ( hdev , " Ignoring keyboard half of device \n " ) ;
return 0 ;
}
cptkbd_data = devm_kzalloc ( & hdev - > dev ,
sizeof ( * cptkbd_data ) ,
GFP_KERNEL ) ;
if ( cptkbd_data = = NULL ) {
hid_err ( hdev , " can't alloc keyboard descriptor \n " ) ;
return - ENOMEM ;
}
hid_set_drvdata ( hdev , cptkbd_data ) ;
/*
* Tell the keyboard a driver understands it , and turn F7 , F9 , F11 into
* regular keys
*/
ret = lenovo_send_cmd_cptkbd ( hdev , 0x01 , 0x03 ) ;
if ( ret )
hid_warn ( hdev , " Failed to switch F7/9/11 mode: %d \n " , ret ) ;
2014-12-17 00:26:46 +03:00
/* Switch middle button to native mode */
ret = lenovo_send_cmd_cptkbd ( hdev , 0x09 , 0x01 ) ;
if ( ret )
hid_warn ( hdev , " Failed to switch middle button: %d \n " , ret ) ;
2014-12-17 00:26:45 +03:00
/* Set keyboard settings to known state */
2015-08-12 00:40:52 +03:00
cptkbd_data - > middlebutton_state = 0 ;
2014-07-24 02:30:48 +04:00
cptkbd_data - > fn_lock = true ;
2014-12-17 00:26:45 +03:00
cptkbd_data - > sensitivity = 0x05 ;
2014-07-24 02:30:48 +04:00
lenovo_features_set_cptkbd ( hdev ) ;
ret = sysfs_create_group ( & hdev - > dev . kobj , & lenovo_attr_group_cptkbd ) ;
if ( ret )
hid_warn ( hdev , " Could not create sysfs group: %d \n " , ret ) ;
return 0 ;
}
2020-07-04 16:27:42 +03:00
static struct attribute * lenovo_attributes_tp10ubkbd [ ] = {
& dev_attr_fn_lock . attr ,
NULL
} ;
static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
. attrs = lenovo_attributes_tp10ubkbd ,
} ;
2020-07-04 16:27:41 +03:00
static int lenovo_probe_tp10ubkbd ( struct hid_device * hdev )
{
2021-04-04 11:04:31 +03:00
struct hid_report_enum * rep_enum ;
2020-07-04 16:27:41 +03:00
struct lenovo_drvdata * data ;
2021-04-04 11:04:31 +03:00
struct hid_report * rep ;
bool found ;
2020-07-04 16:27:42 +03:00
int ret ;
2020-07-04 16:27:41 +03:00
2021-04-04 11:04:31 +03:00
/*
* The LEDs and the Fn - lock functionality use output report 9 ,
* with an application of 0xffa0001 , add the LEDs on the interface
* with this output report .
*/
found = false ;
rep_enum = & hdev - > report_enum [ HID_OUTPUT_REPORT ] ;
list_for_each_entry ( rep , & rep_enum - > report_list , list ) {
if ( rep - > application = = 0xffa00001 )
found = true ;
}
if ( ! found )
2020-07-04 16:27:41 +03:00
return 0 ;
data = devm_kzalloc ( & hdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
mutex_init ( & data - > led_report_mutex ) ;
2020-07-04 16:27:42 +03:00
INIT_WORK ( & data - > fn_lock_sync_work , lenovo_tp10ubkbd_sync_fn_lock ) ;
data - > hdev = hdev ;
2020-07-04 16:27:41 +03:00
hid_set_drvdata ( hdev , data ) ;
2020-07-04 16:27:42 +03:00
/*
* The Thinkpad 10 ultrabook USB kbd dock ' s Fn - lock defaults to on .
* We cannot read the state , only set it , so we force it to on here
* ( which should be a no - op ) to make sure that our state matches the
* keyboard ' s FN - lock state . This is the same as what Windows does .
*/
data - > fn_lock = true ;
lenovo_led_set_tp10ubkbd ( hdev , TP10UBKBD_FN_LOCK_LED , data - > fn_lock ) ;
ret = sysfs_create_group ( & hdev - > dev . kobj , & lenovo_attr_group_tp10ubkbd ) ;
if ( ret )
return ret ;
ret = lenovo_register_leds ( hdev ) ;
if ( ret )
goto err ;
return 0 ;
err :
sysfs_remove_group ( & hdev - > dev . kobj , & lenovo_attr_group_tp10ubkbd ) ;
return ret ;
2020-07-04 16:27:41 +03:00
}
2014-07-24 02:30:45 +04:00
static int lenovo_probe ( struct hid_device * hdev ,
2012-02-15 16:40:43 +04:00
const struct hid_device_id * id )
{
int ret ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " hid_parse failed \n " ) ;
2013-09-11 23:56:59 +04:00
goto err ;
2012-02-15 16:40:43 +04:00
}
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( ret ) {
hid_err ( hdev , " hid_hw_start failed \n " ) ;
2013-09-11 23:56:59 +04:00
goto err ;
2012-02-15 16:40:43 +04:00
}
2014-07-24 02:30:46 +04:00
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPKBD :
2014-07-24 02:30:45 +04:00
ret = lenovo_probe_tpkbd ( hdev ) ;
2014-07-24 02:30:46 +04:00
break ;
2014-07-24 02:30:48 +04:00
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
ret = lenovo_probe_cptkbd ( hdev ) ;
break ;
2020-07-04 16:27:41 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
2020-07-04 16:27:41 +03:00
ret = lenovo_probe_tp10ubkbd ( hdev ) ;
break ;
2014-07-24 02:30:46 +04:00
default :
ret = 0 ;
break ;
2013-09-11 23:56:59 +04:00
}
2014-07-24 02:30:46 +04:00
if ( ret )
goto err_hid ;
2012-02-15 16:40:43 +04:00
return 0 ;
2013-09-11 23:56:59 +04:00
err_hid :
hid_hw_stop ( hdev ) ;
err :
2012-02-15 16:40:43 +04:00
return ret ;
}
2014-07-24 02:30:45 +04:00
static void lenovo_remove_tpkbd ( struct hid_device * hdev )
2012-02-15 16:40:43 +04:00
{
2020-07-04 16:27:38 +03:00
struct lenovo_drvdata * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:46 +04:00
/*
* Only the trackpoint half of the keyboard has drvdata and stuff that
* needs unregistering .
*/
if ( data_pointer = = NULL )
return ;
2012-02-15 16:40:43 +04:00
sysfs_remove_group ( & hdev - > dev . kobj ,
2014-07-24 02:30:45 +04:00
& lenovo_attr_group_tpkbd ) ;
2012-02-15 16:40:43 +04:00
led_classdev_unregister ( & data_pointer - > led_micmute ) ;
led_classdev_unregister ( & data_pointer - > led_mute ) ;
}
2014-07-24 02:30:48 +04:00
static void lenovo_remove_cptkbd ( struct hid_device * hdev )
{
sysfs_remove_group ( & hdev - > dev . kobj ,
& lenovo_attr_group_cptkbd ) ;
}
2020-07-04 16:27:41 +03:00
static void lenovo_remove_tp10ubkbd ( struct hid_device * hdev )
{
struct lenovo_drvdata * data = hid_get_drvdata ( hdev ) ;
if ( data = = NULL )
return ;
led_classdev_unregister ( & data - > led_micmute ) ;
led_classdev_unregister ( & data - > led_mute ) ;
2020-07-04 16:27:42 +03:00
sysfs_remove_group ( & hdev - > dev . kobj , & lenovo_attr_group_tp10ubkbd ) ;
cancel_work_sync ( & data - > fn_lock_sync_work ) ;
2020-07-04 16:27:41 +03:00
}
2014-07-24 02:30:45 +04:00
static void lenovo_remove ( struct hid_device * hdev )
2012-02-15 16:40:43 +04:00
{
2014-07-24 02:30:46 +04:00
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPKBD :
2014-07-24 02:30:45 +04:00
lenovo_remove_tpkbd ( hdev ) ;
2014-07-24 02:30:46 +04:00
break ;
2014-07-24 02:30:48 +04:00
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
lenovo_remove_cptkbd ( hdev ) ;
break ;
2020-07-04 16:27:41 +03:00
case USB_DEVICE_ID_LENOVO_TP10UBKBD :
2021-04-04 11:04:32 +03:00
case USB_DEVICE_ID_LENOVO_X1_TAB :
2020-07-04 16:27:41 +03:00
lenovo_remove_tp10ubkbd ( hdev ) ;
break ;
2014-07-24 02:30:46 +04:00
}
2012-02-15 16:40:43 +04:00
hid_hw_stop ( hdev ) ;
}
2015-09-30 01:52:59 +03:00
static int lenovo_input_configured ( struct hid_device * hdev ,
2015-04-23 11:25:58 +03:00
struct hid_input * hi )
{
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_TPKBD :
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
if ( test_bit ( EV_REL , hi - > input - > evbit ) ) {
/* set only for trackpoint device */
__set_bit ( INPUT_PROP_POINTER , hi - > input - > propbit ) ;
__set_bit ( INPUT_PROP_POINTING_STICK ,
hi - > input - > propbit ) ;
}
break ;
}
2015-09-30 01:52:59 +03:00
return 0 ;
2015-04-23 11:25:58 +03:00
}
2014-07-24 02:30:45 +04:00
static const struct hid_device_id lenovo_devices [ ] = {
2012-02-15 16:40:43 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_TPKBD ) } ,
2014-07-24 02:30:48 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_CUSBKBD ) } ,
{ HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_CBTKBD ) } ,
2015-05-01 23:22:45 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_TPPRODOCK ) } ,
2018-04-12 20:36:47 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_IBM , USB_DEVICE_ID_IBM_SCROLLPOINT_III ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_IBM , USB_DEVICE_ID_IBM_SCROLLPOINT_PRO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_IBM , USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_IBM , USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_IBM , USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL ) } ,
2020-07-04 16:27:41 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_TP10UBKBD ) } ,
2021-04-04 11:04:32 +03:00
/*
* Note bind to the HID_GROUP_GENERIC group , so that we only bind to the keyboard
* part , while letting hid - multitouch . c handle the touchpad and trackpoint .
*/
{ HID_DEVICE ( BUS_USB , HID_GROUP_GENERIC ,
USB_VENDOR_ID_LENOVO , USB_DEVICE_ID_LENOVO_X1_TAB ) } ,
2012-02-15 16:40:43 +04:00
{ }
} ;
2014-07-24 02:30:45 +04:00
MODULE_DEVICE_TABLE ( hid , lenovo_devices ) ;
2012-02-15 16:40:43 +04:00
2014-07-24 02:30:45 +04:00
static struct hid_driver lenovo_driver = {
. name = " lenovo " ,
. id_table = lenovo_devices ,
2015-04-23 11:25:58 +03:00
. input_configured = lenovo_input_configured ,
2014-07-24 02:30:46 +04:00
. input_mapping = lenovo_input_mapping ,
2014-07-24 02:30:45 +04:00
. probe = lenovo_probe ,
. remove = lenovo_remove ,
2014-07-24 02:30:48 +04:00
. raw_event = lenovo_raw_event ,
2015-08-12 00:40:52 +03:00
. event = lenovo_event ,
2015-05-01 23:22:45 +03:00
. report_fixup = lenovo_report_fixup ,
2012-02-15 16:40:43 +04:00
} ;
2014-07-24 02:30:45 +04:00
module_hid_driver ( lenovo_driver ) ;
2012-02-15 16:40:43 +04:00
MODULE_LICENSE ( " GPL " ) ;