Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: lenovo: Add ThinkPad 10 Ultrabook Keyboard fn_lock support

Add support for setting the Fn lock value of the ThinkPad 10 Ultrabook
Keyboard through sysfs, re-using the fn_lock sysfs attribute read/write
helpers from the existing ThinkPad Compact Keyboard with TrackPoint
support.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Hans de Goede and committed by
Jiri Kosina
c87de33e bc04b37e

+71 -1
+71 -1
drivers/hid/hid-lenovo.c
··· 29 29 #include <linux/hid.h> 30 30 #include <linux/input.h> 31 31 #include <linux/leds.h> 32 + #include <linux/workqueue.h> 32 33 33 34 #include "hid-ids.h" 34 35 ··· 39 38 struct mutex led_report_mutex; 40 39 struct led_classdev led_mute; 41 40 struct led_classdev led_micmute; 41 + struct work_struct fn_lock_sync_work; 42 + struct hid_device *hdev; 42 43 int press_to_select; 43 44 int dragging; 44 45 int release_to_select; ··· 79 76 hid_err(hdev, "Set LED output report error: %d\n", ret); 80 77 81 78 mutex_unlock(&data->led_report_mutex); 79 + } 80 + 81 + static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work) 82 + { 83 + struct lenovo_drvdata *data = 84 + container_of(work, struct lenovo_drvdata, fn_lock_sync_work); 85 + 86 + lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED, 87 + data->fn_lock); 82 88 } 83 89 84 90 static const __u8 lenovo_pro_dock_need_fixup_collection[] = { ··· 356 344 case USB_DEVICE_ID_LENOVO_CBTKBD: 357 345 lenovo_features_set_cptkbd(hdev); 358 346 break; 347 + case USB_DEVICE_ID_LENOVO_TP10UBKBD: 348 + lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); 349 + break; 359 350 } 360 351 361 352 return count; ··· 435 420 return 0; 436 421 } 437 422 423 + static int lenovo_event_tp10ubkbd(struct hid_device *hdev, 424 + struct hid_field *field, struct hid_usage *usage, __s32 value) 425 + { 426 + struct lenovo_drvdata *data = hid_get_drvdata(hdev); 427 + 428 + if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) { 429 + /* 430 + * The user has toggled the Fn-lock state. Toggle our own 431 + * cached value of it and sync our value to the keyboard to 432 + * ensure things are in sync (the sycning should be a no-op). 433 + */ 434 + data->fn_lock = !data->fn_lock; 435 + schedule_work(&data->fn_lock_sync_work); 436 + } 437 + 438 + return 0; 439 + } 440 + 438 441 static int lenovo_event_cptkbd(struct hid_device *hdev, 439 442 struct hid_field *field, struct hid_usage *usage, __s32 value) 440 443 { ··· 495 462 case USB_DEVICE_ID_LENOVO_CUSBKBD: 496 463 case USB_DEVICE_ID_LENOVO_CBTKBD: 497 464 return lenovo_event_cptkbd(hdev, field, usage, value); 465 + case USB_DEVICE_ID_LENOVO_TP10UBKBD: 466 + return lenovo_event_tp10ubkbd(hdev, field, usage, value); 498 467 default: 499 468 return 0; 500 469 } ··· 934 899 return 0; 935 900 } 936 901 902 + static struct attribute *lenovo_attributes_tp10ubkbd[] = { 903 + &dev_attr_fn_lock.attr, 904 + NULL 905 + }; 906 + 907 + static const struct attribute_group lenovo_attr_group_tp10ubkbd = { 908 + .attrs = lenovo_attributes_tp10ubkbd, 909 + }; 910 + 937 911 static int lenovo_probe_tp10ubkbd(struct hid_device *hdev) 938 912 { 939 913 struct lenovo_drvdata *data; 914 + int ret; 940 915 941 916 /* All the custom action happens on the USBMOUSE device for USB */ 942 917 if (hdev->type != HID_TYPE_USBMOUSE) ··· 957 912 return -ENOMEM; 958 913 959 914 mutex_init(&data->led_report_mutex); 915 + INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock); 916 + data->hdev = hdev; 960 917 961 918 hid_set_drvdata(hdev, data); 962 919 963 - return lenovo_register_leds(hdev); 920 + /* 921 + * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on. 922 + * We cannot read the state, only set it, so we force it to on here 923 + * (which should be a no-op) to make sure that our state matches the 924 + * keyboard's FN-lock state. This is the same as what Windows does. 925 + */ 926 + data->fn_lock = true; 927 + lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock); 928 + 929 + ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); 930 + if (ret) 931 + return ret; 932 + 933 + ret = lenovo_register_leds(hdev); 934 + if (ret) 935 + goto err; 936 + 937 + return 0; 938 + err: 939 + sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); 940 + return ret; 964 941 } 965 942 966 943 static int lenovo_probe(struct hid_device *hdev, ··· 1060 993 1061 994 led_classdev_unregister(&data->led_micmute); 1062 995 led_classdev_unregister(&data->led_mute); 996 + 997 + sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); 998 + cancel_work_sync(&data->fn_lock_sync_work); 1063 999 } 1064 1000 1065 1001 static void lenovo_remove(struct hid_device *hdev)