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 >
2012-02-15 16:40:43 +04:00
*/
/*
* 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 .
*/
# include <linux/module.h>
# include <linux/sysfs.h>
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/input.h>
# include <linux/leds.h>
# include "hid-ids.h"
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd {
2012-02-15 16:40:43 +04:00
int led_state ;
struct led_classdev led_mute ;
struct led_classdev led_micmute ;
int press_to_select ;
int dragging ;
int release_to_select ;
int select_right ;
int sensitivity ;
int press_speed ;
} ;
2014-07-24 02:30:48 +04:00
struct lenovo_drvdata_cptkbd {
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 ;
2014-12-17 00:26:45 +03:00
int sensitivity ;
2014-07-24 02:30:48 +04:00
} ;
2012-02-15 16:40:43 +04:00
# define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
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 ) ;
2012-02-15 16:40:43 +04:00
map_key_clear ( KEY_MICMUTE ) ;
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 */
map_key_clear ( KEY_MICMUTE ) ;
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 ;
}
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 ) ;
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 ;
unsigned char buf [ ] = { 0x18 , byte2 , byte3 } ;
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_CUSBKBD :
ret = hid_hw_raw_request ( hdev , 0x13 , buf , sizeof ( buf ) ,
HID_FEATURE_REPORT , HID_REQ_SET_REPORT ) ;
break ;
case USB_DEVICE_ID_LENOVO_CBTKBD :
ret = hid_hw_output_report ( hdev , buf , sizeof ( buf ) ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret < 0 ? ret : 0 ; /* BT returns 0, USB returns sizeof(buf) */
}
static void lenovo_features_set_cptkbd ( struct hid_device * hdev )
{
int ret ;
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
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
}
static ssize_t attr_fn_lock_show_cptkbd ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , cptkbd_data - > fn_lock ) ;
}
static ssize_t attr_fn_lock_store_cptkbd ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
int value ;
if ( kstrtoint ( buf , 10 , & value ) )
return - EINVAL ;
if ( value < 0 | | value > 1 )
return - EINVAL ;
cptkbd_data - > fn_lock = ! ! value ;
lenovo_features_set_cptkbd ( hdev ) ;
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 )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
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 )
{
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
int value ;
if ( kstrtoint ( buf , 10 , & value ) | | value < 1 | | value > 255 )
return - EINVAL ;
cptkbd_data - > sensitivity = value ;
lenovo_features_set_cptkbd ( hdev ) ;
return count ;
}
2014-07-24 02:30:48 +04:00
static struct device_attribute dev_attr_fn_lock_cptkbd =
__ATTR ( fn_lock , S_IWUSR | S_IRUGO ,
attr_fn_lock_show_cptkbd ,
attr_fn_lock_store_cptkbd ) ;
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 [ ] = {
& dev_attr_fn_lock_cptkbd . 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 ;
}
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 )
{
struct lenovo_drvdata_cptkbd * cptkbd_data = hid_get_drvdata ( hdev ) ;
/* "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 )
{
switch ( hdev - > product ) {
case USB_DEVICE_ID_LENOVO_CUSBKBD :
case USB_DEVICE_ID_LENOVO_CBTKBD :
return lenovo_event_cptkbd ( hdev , field , usage , value ) ;
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 ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 )
{
2012-09-13 10:12:08 +04:00
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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
} ;
2014-07-24 02:30:45 +04:00
static enum led_brightness lenovo_led_brightness_get_tpkbd (
2012-02-15 16:40:43 +04:00
struct led_classdev * led_cdev )
{
2012-09-13 10:12:08 +04:00
struct device * dev = led_cdev - > dev - > parent ;
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
int led_nr = 0 ;
if ( led_cdev = = & data_pointer - > led_micmute )
led_nr = 1 ;
return data_pointer - > led_state & ( 1 < < led_nr )
? LED_FULL
: LED_OFF ;
}
2014-07-24 02:30:45 +04:00
static void lenovo_led_brightness_set_tpkbd ( 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 ;
struct hid_device * hdev = container_of ( dev , struct hid_device , dev ) ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * data_pointer = hid_get_drvdata ( hdev ) ;
2012-02-15 16:40:43 +04:00
struct hid_report * report ;
int led_nr = 0 ;
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 ;
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 ;
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
}
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
{
struct device * dev = & hdev - > dev ;
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * data_pointer ;
2012-02-15 16:40:43 +04:00
size_t name_sz = strlen ( dev_name ( dev ) ) + 16 ;
char * name_mute , * name_micmute ;
2013-09-12 00:12:23 +04:00
int i ;
2014-07-26 18:42:27 +04:00
int 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 ,
2014-07-24 02:30:45 +04:00
sizeof ( struct lenovo_drvdata_tpkbd ) ,
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 ;
2013-09-12 00:12:23 +04:00
name_mute = devm_kzalloc ( & hdev - > dev , name_sz , GFP_KERNEL ) ;
name_micmute = devm_kzalloc ( & hdev - > dev , name_sz , GFP_KERNEL ) ;
if ( name_mute = = NULL | | name_micmute = = NULL ) {
2012-02-15 16:40:43 +04:00
hid_err ( hdev , " Could not allocate memory for led data \n " ) ;
2015-05-29 03:39:31 +03:00
ret = - ENOMEM ;
goto err ;
2012-02-15 16:40:43 +04:00
}
snprintf ( name_mute , name_sz , " %s:amber:mute " , dev_name ( dev ) ) ;
snprintf ( name_micmute , name_sz , " %s:amber:micmute " , dev_name ( dev ) ) ;
hid_set_drvdata ( hdev , data_pointer ) ;
data_pointer - > led_mute . name = name_mute ;
2014-07-24 02:30:45 +04:00
data_pointer - > led_mute . brightness_get = lenovo_led_brightness_get_tpkbd ;
data_pointer - > led_mute . brightness_set = lenovo_led_brightness_set_tpkbd ;
2012-02-15 16:40:43 +04:00
data_pointer - > led_mute . dev = dev ;
led_classdev_register ( dev , & data_pointer - > led_mute ) ;
data_pointer - > led_micmute . name = name_micmute ;
2014-07-24 02:30:45 +04:00
data_pointer - > led_micmute . brightness_get =
lenovo_led_brightness_get_tpkbd ;
data_pointer - > led_micmute . brightness_set =
lenovo_led_brightness_set_tpkbd ;
2012-02-15 16:40:43 +04:00
data_pointer - > led_micmute . dev = dev ;
led_classdev_register ( dev , & data_pointer - > led_micmute ) ;
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 ;
struct lenovo_drvdata_cptkbd * cptkbd_data ;
/* 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 ;
}
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 ;
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
{
2014-07-24 02:30:45 +04:00
struct lenovo_drvdata_tpkbd * 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 ) ;
hid_set_drvdata ( hdev , NULL ) ;
}
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 ) ;
}
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 ;
2014-07-24 02:30:46 +04:00
}
2012-02-15 16:40:43 +04:00
hid_hw_stop ( hdev ) ;
}
2015-04-23 11:25:58 +03:00
static void lenovo_input_configured ( struct hid_device * hdev ,
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 ;
}
}
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 ) } ,
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 " ) ;