2929#include <linux/hid.h>
3030#include <linux/input.h>
3131#include <linux/leds.h>
32+ #include <linux/workqueue.h>
3233
3334#include "hid-ids.h"
3435
@@ -38,6 +39,8 @@ struct lenovo_drvdata {
3839 struct mutex led_report_mutex ;
3940 struct led_classdev led_mute ;
4041 struct led_classdev led_micmute ;
42+ struct work_struct fn_lock_sync_work ;
43+ struct hid_device * hdev ;
4144 int press_to_select ;
4245 int dragging ;
4346 int release_to_select ;
@@ -78,6 +81,15 @@ static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
7881 mutex_unlock (& data -> led_report_mutex );
7982}
8083
84+ static void lenovo_tp10ubkbd_sync_fn_lock (struct work_struct * work )
85+ {
86+ struct lenovo_drvdata * data =
87+ container_of (work , struct lenovo_drvdata , fn_lock_sync_work );
88+
89+ lenovo_led_set_tp10ubkbd (data -> hdev , TP10UBKBD_FN_LOCK_LED ,
90+ data -> fn_lock );
91+ }
92+
8193static const __u8 lenovo_pro_dock_need_fixup_collection [] = {
8294 0x05 , 0x88 , /* Usage Page (Vendor Usage Page 0x88) */
8395 0x09 , 0x01 , /* Usage (Vendor Usage 0x01) */
@@ -344,6 +356,9 @@ static ssize_t attr_fn_lock_store(struct device *dev,
344356 case USB_DEVICE_ID_LENOVO_CBTKBD :
345357 lenovo_features_set_cptkbd (hdev );
346358 break ;
359+ case USB_DEVICE_ID_LENOVO_TP10UBKBD :
360+ lenovo_led_set_tp10ubkbd (hdev , TP10UBKBD_FN_LOCK_LED , value );
361+ break ;
347362 }
348363
349364 return count ;
@@ -420,6 +435,24 @@ static int lenovo_raw_event(struct hid_device *hdev,
420435 return 0 ;
421436}
422437
438+ static int lenovo_event_tp10ubkbd (struct hid_device * hdev ,
439+ struct hid_field * field , struct hid_usage * usage , __s32 value )
440+ {
441+ struct lenovo_drvdata * data = hid_get_drvdata (hdev );
442+
443+ if (usage -> type == EV_KEY && usage -> code == KEY_FN_ESC && value == 1 ) {
444+ /*
445+ * The user has toggled the Fn-lock state. Toggle our own
446+ * cached value of it and sync our value to the keyboard to
447+ * ensure things are in sync (the sycning should be a no-op).
448+ */
449+ data -> fn_lock = !data -> fn_lock ;
450+ schedule_work (& data -> fn_lock_sync_work );
451+ }
452+
453+ return 0 ;
454+ }
455+
423456static int lenovo_event_cptkbd (struct hid_device * hdev ,
424457 struct hid_field * field , struct hid_usage * usage , __s32 value )
425458{
@@ -462,6 +495,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
462495 case USB_DEVICE_ID_LENOVO_CUSBKBD :
463496 case USB_DEVICE_ID_LENOVO_CBTKBD :
464497 return lenovo_event_cptkbd (hdev , field , usage , value );
498+ case USB_DEVICE_ID_LENOVO_TP10UBKBD :
499+ return lenovo_event_tp10ubkbd (hdev , field , usage , value );
465500 default :
466501 return 0 ;
467502 }
@@ -899,9 +934,19 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
899934 return 0 ;
900935}
901936
937+ static struct attribute * lenovo_attributes_tp10ubkbd [] = {
938+ & dev_attr_fn_lock .attr ,
939+ NULL
940+ };
941+
942+ static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
943+ .attrs = lenovo_attributes_tp10ubkbd ,
944+ };
945+
902946static int lenovo_probe_tp10ubkbd (struct hid_device * hdev )
903947{
904948 struct lenovo_drvdata * data ;
949+ int ret ;
905950
906951 /* All the custom action happens on the USBMOUSE device for USB */
907952 if (hdev -> type != HID_TYPE_USBMOUSE )
@@ -912,10 +957,32 @@ static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
912957 return - ENOMEM ;
913958
914959 mutex_init (& data -> led_report_mutex );
960+ INIT_WORK (& data -> fn_lock_sync_work , lenovo_tp10ubkbd_sync_fn_lock );
961+ data -> hdev = hdev ;
915962
916963 hid_set_drvdata (hdev , data );
917964
918- return lenovo_register_leds (hdev );
965+ /*
966+ * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
967+ * We cannot read the state, only set it, so we force it to on here
968+ * (which should be a no-op) to make sure that our state matches the
969+ * keyboard's FN-lock state. This is the same as what Windows does.
970+ */
971+ data -> fn_lock = true;
972+ lenovo_led_set_tp10ubkbd (hdev , TP10UBKBD_FN_LOCK_LED , data -> fn_lock );
973+
974+ ret = sysfs_create_group (& hdev -> dev .kobj , & lenovo_attr_group_tp10ubkbd );
975+ if (ret )
976+ return ret ;
977+
978+ ret = lenovo_register_leds (hdev );
979+ if (ret )
980+ goto err ;
981+
982+ return 0 ;
983+ err :
984+ sysfs_remove_group (& hdev -> dev .kobj , & lenovo_attr_group_tp10ubkbd );
985+ return ret ;
919986}
920987
921988static int lenovo_probe (struct hid_device * hdev ,
@@ -993,6 +1060,9 @@ static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
9931060
9941061 led_classdev_unregister (& data -> led_micmute );
9951062 led_classdev_unregister (& data -> led_mute );
1063+
1064+ sysfs_remove_group (& hdev -> dev .kobj , & lenovo_attr_group_tp10ubkbd );
1065+ cancel_work_sync (& data -> fn_lock_sync_work );
9961066}
9971067
9981068static void lenovo_remove (struct hid_device * hdev )
0 commit comments