hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/drivers/hid/hid-lenovo.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * HID driver for Lenovo:
34 * - ThinkPad USB Keyboard with TrackPoint (tpkbd)
....@@ -20,10 +21,6 @@
2021 */
2122
2223 /*
23
- * This program is free software; you can redistribute it and/or modify it
24
- * under the terms of the GNU General Public License as published by the Free
25
- * Software Foundation; either version 2 of the License, or (at your option)
26
- * any later version.
2724 */
2825
2926 #include <linux/module.h>
....@@ -32,28 +29,77 @@
3229 #include <linux/hid.h>
3330 #include <linux/input.h>
3431 #include <linux/leds.h>
32
+#include <linux/workqueue.h>
3533
3634 #include "hid-ids.h"
3735
38
-struct lenovo_drvdata_tpkbd {
36
+/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
37
+#define LENOVO_KEY_MICMUTE KEY_F20
38
+
39
+struct lenovo_drvdata {
40
+ u8 led_report[3]; /* Must be first for proper alignment */
3941 int led_state;
42
+ struct mutex led_report_mutex;
4043 struct led_classdev led_mute;
4144 struct led_classdev led_micmute;
45
+ struct work_struct fn_lock_sync_work;
46
+ struct hid_device *hdev;
4247 int press_to_select;
4348 int dragging;
4449 int release_to_select;
4550 int select_right;
4651 int sensitivity;
4752 int press_speed;
48
-};
49
-
50
-struct lenovo_drvdata_cptkbd {
5153 u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
5254 bool fn_lock;
53
- int sensitivity;
5455 };
5556
5657 #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
58
+
59
+#define TP10UBKBD_LED_OUTPUT_REPORT 9
60
+
61
+#define TP10UBKBD_FN_LOCK_LED 0x54
62
+#define TP10UBKBD_MUTE_LED 0x64
63
+#define TP10UBKBD_MICMUTE_LED 0x74
64
+
65
+#define TP10UBKBD_LED_OFF 1
66
+#define TP10UBKBD_LED_ON 2
67
+
68
+static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
69
+ enum led_brightness value)
70
+{
71
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
72
+ int ret;
73
+
74
+ mutex_lock(&data->led_report_mutex);
75
+
76
+ data->led_report[0] = TP10UBKBD_LED_OUTPUT_REPORT;
77
+ data->led_report[1] = led_code;
78
+ data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF;
79
+ ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3,
80
+ HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
81
+ if (ret != 3) {
82
+ if (ret != -ENODEV)
83
+ hid_err(hdev, "Set LED output report error: %d\n", ret);
84
+
85
+ ret = ret < 0 ? ret : -EIO;
86
+ } else {
87
+ ret = 0;
88
+ }
89
+
90
+ mutex_unlock(&data->led_report_mutex);
91
+
92
+ return ret;
93
+}
94
+
95
+static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work)
96
+{
97
+ struct lenovo_drvdata *data =
98
+ container_of(work, struct lenovo_drvdata, fn_lock_sync_work);
99
+
100
+ lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED,
101
+ data->fn_lock);
102
+}
57103
58104 static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
59105 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
....@@ -91,7 +137,7 @@
91137 if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
92138 /* This sub-device contains trackpoint, mark it */
93139 hid_set_drvdata(hdev, (void *)1);
94
- map_key_clear(KEY_MICMUTE);
140
+ map_key_clear(LENOVO_KEY_MICMUTE);
95141 return 1;
96142 }
97143 return 0;
....@@ -106,7 +152,7 @@
106152 (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
107153 switch (usage->hid & HID_USAGE) {
108154 case 0x00f1: /* Fn-F4: Mic mute */
109
- map_key_clear(KEY_MICMUTE);
155
+ map_key_clear(LENOVO_KEY_MICMUTE);
110156 return 1;
111157 case 0x00f2: /* Fn-F5: Brightness down */
112158 map_key_clear(KEY_BRIGHTNESSDOWN);
....@@ -182,6 +228,44 @@
182228 return 0;
183229 }
184230
231
+static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
232
+ struct hid_input *hi, struct hid_field *field,
233
+ struct hid_usage *usage, unsigned long **bit, int *max)
234
+{
235
+ /*
236
+ * The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for
237
+ * a bunch of keys which have no standard consumer page code.
238
+ */
239
+ if (usage->hid == 0x000c0001) {
240
+ switch (usage->usage_index) {
241
+ case 8: /* Fn-Esc: Fn-lock toggle */
242
+ map_key_clear(KEY_FN_ESC);
243
+ return 1;
244
+ case 9: /* Fn-F4: Mic mute */
245
+ map_key_clear(LENOVO_KEY_MICMUTE);
246
+ return 1;
247
+ case 10: /* Fn-F7: Control panel */
248
+ map_key_clear(KEY_CONFIG);
249
+ return 1;
250
+ case 11: /* Fn-F8: Search (magnifier glass) */
251
+ map_key_clear(KEY_SEARCH);
252
+ return 1;
253
+ case 12: /* Fn-F10: Open My computer (6 boxes) */
254
+ map_key_clear(KEY_FILE);
255
+ return 1;
256
+ }
257
+ }
258
+
259
+ /*
260
+ * The Ultrabook Keyboard sends a spurious F23 key-press when resuming
261
+ * from suspend and it does not actually have a F23 key, ignore it.
262
+ */
263
+ if (usage->hid == 0x00070072)
264
+ return -1;
265
+
266
+ return 0;
267
+}
268
+
185269 static int lenovo_input_mapping(struct hid_device *hdev,
186270 struct hid_input *hi, struct hid_field *field,
187271 struct hid_usage *usage, unsigned long **bit, int *max)
....@@ -202,6 +286,9 @@
202286 case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
203287 return lenovo_input_mapping_scrollpoint(hdev, hi, field,
204288 usage, bit, max);
289
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
290
+ return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
291
+ usage, bit, max);
205292 default:
206293 return 0;
207294 }
....@@ -245,7 +332,7 @@
245332 static void lenovo_features_set_cptkbd(struct hid_device *hdev)
246333 {
247334 int ret;
248
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
335
+ struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
249336
250337 ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
251338 if (ret)
....@@ -256,32 +343,43 @@
256343 hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
257344 }
258345
259
-static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
346
+static ssize_t attr_fn_lock_show(struct device *dev,
260347 struct device_attribute *attr,
261348 char *buf)
262349 {
263350 struct hid_device *hdev = to_hid_device(dev);
264
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
351
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
265352
266
- return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock);
353
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock);
267354 }
268355
269
-static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
356
+static ssize_t attr_fn_lock_store(struct device *dev,
270357 struct device_attribute *attr,
271358 const char *buf,
272359 size_t count)
273360 {
274361 struct hid_device *hdev = to_hid_device(dev);
275
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
276
- int value;
362
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
363
+ int value, ret;
277364
278365 if (kstrtoint(buf, 10, &value))
279366 return -EINVAL;
280367 if (value < 0 || value > 1)
281368 return -EINVAL;
282369
283
- cptkbd_data->fn_lock = !!value;
284
- lenovo_features_set_cptkbd(hdev);
370
+ data->fn_lock = !!value;
371
+
372
+ switch (hdev->product) {
373
+ case USB_DEVICE_ID_LENOVO_CUSBKBD:
374
+ case USB_DEVICE_ID_LENOVO_CBTKBD:
375
+ lenovo_features_set_cptkbd(hdev);
376
+ break;
377
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
378
+ ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
379
+ if (ret)
380
+ return ret;
381
+ break;
382
+ }
285383
286384 return count;
287385 }
....@@ -291,7 +389,7 @@
291389 char *buf)
292390 {
293391 struct hid_device *hdev = to_hid_device(dev);
294
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
392
+ struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
295393
296394 return snprintf(buf, PAGE_SIZE, "%u\n",
297395 cptkbd_data->sensitivity);
....@@ -303,7 +401,7 @@
303401 size_t count)
304402 {
305403 struct hid_device *hdev = to_hid_device(dev);
306
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
404
+ struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
307405 int value;
308406
309407 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
....@@ -316,10 +414,10 @@
316414 }
317415
318416
319
-static struct device_attribute dev_attr_fn_lock_cptkbd =
417
+static struct device_attribute dev_attr_fn_lock =
320418 __ATTR(fn_lock, S_IWUSR | S_IRUGO,
321
- attr_fn_lock_show_cptkbd,
322
- attr_fn_lock_store_cptkbd);
419
+ attr_fn_lock_show,
420
+ attr_fn_lock_store);
323421
324422 static struct device_attribute dev_attr_sensitivity_cptkbd =
325423 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
....@@ -328,7 +426,7 @@
328426
329427
330428 static struct attribute *lenovo_attributes_cptkbd[] = {
331
- &dev_attr_fn_lock_cptkbd.attr,
429
+ &dev_attr_fn_lock.attr,
332430 &dev_attr_sensitivity_cptkbd.attr,
333431 NULL
334432 };
....@@ -357,10 +455,28 @@
357455 return 0;
358456 }
359457
458
+static int lenovo_event_tp10ubkbd(struct hid_device *hdev,
459
+ struct hid_field *field, struct hid_usage *usage, __s32 value)
460
+{
461
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
462
+
463
+ if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
464
+ /*
465
+ * The user has toggled the Fn-lock state. Toggle our own
466
+ * cached value of it and sync our value to the keyboard to
467
+ * ensure things are in sync (the sycning should be a no-op).
468
+ */
469
+ data->fn_lock = !data->fn_lock;
470
+ schedule_work(&data->fn_lock_sync_work);
471
+ }
472
+
473
+ return 0;
474
+}
475
+
360476 static int lenovo_event_cptkbd(struct hid_device *hdev,
361477 struct hid_field *field, struct hid_usage *usage, __s32 value)
362478 {
363
- struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
479
+ struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
364480
365481 /* "wheel" scroll events */
366482 if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
....@@ -395,10 +511,15 @@
395511 static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
396512 struct hid_usage *usage, __s32 value)
397513 {
514
+ if (!hid_get_drvdata(hdev))
515
+ return 0;
516
+
398517 switch (hdev->product) {
399518 case USB_DEVICE_ID_LENOVO_CUSBKBD:
400519 case USB_DEVICE_ID_LENOVO_CBTKBD:
401520 return lenovo_event_cptkbd(hdev, field, usage, value);
521
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
522
+ return lenovo_event_tp10ubkbd(hdev, field, usage, value);
402523 default:
403524 return 0;
404525 }
....@@ -407,7 +528,7 @@
407528 static int lenovo_features_set_tpkbd(struct hid_device *hdev)
408529 {
409530 struct hid_report *report;
410
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
531
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
411532
412533 report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
413534
....@@ -428,7 +549,7 @@
428549 char *buf)
429550 {
430551 struct hid_device *hdev = to_hid_device(dev);
431
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
552
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
432553
433554 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
434555 }
....@@ -439,7 +560,7 @@
439560 size_t count)
440561 {
441562 struct hid_device *hdev = to_hid_device(dev);
442
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
563
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
443564 int value;
444565
445566 if (kstrtoint(buf, 10, &value))
....@@ -458,7 +579,7 @@
458579 char *buf)
459580 {
460581 struct hid_device *hdev = to_hid_device(dev);
461
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
582
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
462583
463584 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
464585 }
....@@ -469,7 +590,7 @@
469590 size_t count)
470591 {
471592 struct hid_device *hdev = to_hid_device(dev);
472
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
593
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
473594 int value;
474595
475596 if (kstrtoint(buf, 10, &value))
....@@ -488,7 +609,7 @@
488609 char *buf)
489610 {
490611 struct hid_device *hdev = to_hid_device(dev);
491
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
612
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
492613
493614 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
494615 }
....@@ -499,7 +620,7 @@
499620 size_t count)
500621 {
501622 struct hid_device *hdev = to_hid_device(dev);
502
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
623
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
503624 int value;
504625
505626 if (kstrtoint(buf, 10, &value))
....@@ -518,7 +639,7 @@
518639 char *buf)
519640 {
520641 struct hid_device *hdev = to_hid_device(dev);
521
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
642
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
522643
523644 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
524645 }
....@@ -529,7 +650,7 @@
529650 size_t count)
530651 {
531652 struct hid_device *hdev = to_hid_device(dev);
532
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
653
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
533654 int value;
534655
535656 if (kstrtoint(buf, 10, &value))
....@@ -548,7 +669,7 @@
548669 char *buf)
549670 {
550671 struct hid_device *hdev = to_hid_device(dev);
551
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
672
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
552673
553674 return snprintf(buf, PAGE_SIZE, "%u\n",
554675 data_pointer->sensitivity);
....@@ -560,7 +681,7 @@
560681 size_t count)
561682 {
562683 struct hid_device *hdev = to_hid_device(dev);
563
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
684
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
564685 int value;
565686
566687 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
....@@ -577,7 +698,7 @@
577698 char *buf)
578699 {
579700 struct hid_device *hdev = to_hid_device(dev);
580
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
701
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
581702
582703 return snprintf(buf, PAGE_SIZE, "%u\n",
583704 data_pointer->press_speed);
....@@ -589,7 +710,7 @@
589710 size_t count)
590711 {
591712 struct hid_device *hdev = to_hid_device(dev);
592
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
713
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
593714 int value;
594715
595716 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
....@@ -645,12 +766,23 @@
645766 .attrs = lenovo_attributes_tpkbd,
646767 };
647768
648
-static enum led_brightness lenovo_led_brightness_get_tpkbd(
769
+static void lenovo_led_set_tpkbd(struct hid_device *hdev)
770
+{
771
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
772
+ struct hid_report *report;
773
+
774
+ report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
775
+ report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
776
+ report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
777
+ hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
778
+}
779
+
780
+static enum led_brightness lenovo_led_brightness_get(
649781 struct led_classdev *led_cdev)
650782 {
651783 struct device *dev = led_cdev->dev->parent;
652784 struct hid_device *hdev = to_hid_device(dev);
653
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
785
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
654786 int led_nr = 0;
655787
656788 if (led_cdev == &data_pointer->led_micmute)
....@@ -661,14 +793,15 @@
661793 : LED_OFF;
662794 }
663795
664
-static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
796
+static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
665797 enum led_brightness value)
666798 {
667799 struct device *dev = led_cdev->dev->parent;
668800 struct hid_device *hdev = to_hid_device(dev);
669
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
670
- struct hid_report *report;
801
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
802
+ u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
671803 int led_nr = 0;
804
+ int ret = 0;
672805
673806 if (led_cdev == &data_pointer->led_micmute)
674807 led_nr = 1;
....@@ -678,20 +811,61 @@
678811 else
679812 data_pointer->led_state |= 1 << led_nr;
680813
681
- report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
682
- report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
683
- report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
684
- hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
814
+ switch (hdev->product) {
815
+ case USB_DEVICE_ID_LENOVO_TPKBD:
816
+ lenovo_led_set_tpkbd(hdev);
817
+ break;
818
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
819
+ ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
820
+ break;
821
+ }
822
+
823
+ return ret;
824
+}
825
+
826
+static int lenovo_register_leds(struct hid_device *hdev)
827
+{
828
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
829
+ size_t name_sz = strlen(dev_name(&hdev->dev)) + 16;
830
+ char *name_mute, *name_micm;
831
+ int ret;
832
+
833
+ name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
834
+ name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
835
+ if (name_mute == NULL || name_micm == NULL) {
836
+ hid_err(hdev, "Could not allocate memory for led data\n");
837
+ return -ENOMEM;
838
+ }
839
+ snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev));
840
+ snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
841
+
842
+ data->led_mute.name = name_mute;
843
+ data->led_mute.brightness_get = lenovo_led_brightness_get;
844
+ data->led_mute.brightness_set_blocking = lenovo_led_brightness_set;
845
+ data->led_mute.flags = LED_HW_PLUGGABLE;
846
+ data->led_mute.dev = &hdev->dev;
847
+ ret = led_classdev_register(&hdev->dev, &data->led_mute);
848
+ if (ret < 0)
849
+ return ret;
850
+
851
+ data->led_micmute.name = name_micm;
852
+ data->led_micmute.brightness_get = lenovo_led_brightness_get;
853
+ data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set;
854
+ data->led_micmute.flags = LED_HW_PLUGGABLE;
855
+ data->led_micmute.dev = &hdev->dev;
856
+ ret = led_classdev_register(&hdev->dev, &data->led_micmute);
857
+ if (ret < 0) {
858
+ led_classdev_unregister(&data->led_mute);
859
+ return ret;
860
+ }
861
+
862
+ return 0;
685863 }
686864
687865 static int lenovo_probe_tpkbd(struct hid_device *hdev)
688866 {
689
- struct device *dev = &hdev->dev;
690
- struct lenovo_drvdata_tpkbd *data_pointer;
691
- size_t name_sz = strlen(dev_name(dev)) + 16;
692
- char *name_mute, *name_micmute;
693
- int i;
694
- int ret;
867
+ struct lenovo_drvdata *data_pointer;
868
+ int i, ret;
695869
696870 /*
697871 * Only register extra settings against subdevice where input_mapping
....@@ -715,7 +889,7 @@
715889 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
716890
717891 data_pointer = devm_kzalloc(&hdev->dev,
718
- sizeof(struct lenovo_drvdata_tpkbd),
892
+ sizeof(struct lenovo_drvdata),
719893 GFP_KERNEL);
720894 if (data_pointer == NULL) {
721895 hid_err(hdev, "Could not allocate memory for driver data\n");
....@@ -727,37 +901,11 @@
727901 data_pointer->sensitivity = 0xa0;
728902 data_pointer->press_speed = 0x38;
729903
730
- name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
731
- name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
732
- if (name_mute == NULL || name_micmute == NULL) {
733
- hid_err(hdev, "Could not allocate memory for led data\n");
734
- ret = -ENOMEM;
735
- goto err;
736
- }
737
- snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
738
- snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
739
-
740904 hid_set_drvdata(hdev, data_pointer);
741905
742
- data_pointer->led_mute.name = name_mute;
743
- data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
744
- data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
745
- data_pointer->led_mute.dev = dev;
746
- ret = led_classdev_register(dev, &data_pointer->led_mute);
747
- if (ret < 0)
906
+ ret = lenovo_register_leds(hdev);
907
+ if (ret)
748908 goto err;
749
-
750
- data_pointer->led_micmute.name = name_micmute;
751
- data_pointer->led_micmute.brightness_get =
752
- lenovo_led_brightness_get_tpkbd;
753
- data_pointer->led_micmute.brightness_set =
754
- lenovo_led_brightness_set_tpkbd;
755
- data_pointer->led_micmute.dev = dev;
756
- ret = led_classdev_register(dev, &data_pointer->led_micmute);
757
- if (ret < 0) {
758
- led_classdev_unregister(&data_pointer->led_mute);
759
- goto err;
760
- }
761909
762910 lenovo_features_set_tpkbd(hdev);
763911
....@@ -770,7 +918,7 @@
770918 static int lenovo_probe_cptkbd(struct hid_device *hdev)
771919 {
772920 int ret;
773
- struct lenovo_drvdata_cptkbd *cptkbd_data;
921
+ struct lenovo_drvdata *cptkbd_data;
774922
775923 /* All the custom action happens on the USBMOUSE device for USB */
776924 if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
....@@ -814,6 +962,57 @@
814962 return 0;
815963 }
816964
965
+static struct attribute *lenovo_attributes_tp10ubkbd[] = {
966
+ &dev_attr_fn_lock.attr,
967
+ NULL
968
+};
969
+
970
+static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
971
+ .attrs = lenovo_attributes_tp10ubkbd,
972
+};
973
+
974
+static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
975
+{
976
+ struct lenovo_drvdata *data;
977
+ int ret;
978
+
979
+ /* All the custom action happens on the USBMOUSE device for USB */
980
+ if (hdev->type != HID_TYPE_USBMOUSE)
981
+ return 0;
982
+
983
+ data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
984
+ if (!data)
985
+ return -ENOMEM;
986
+
987
+ mutex_init(&data->led_report_mutex);
988
+ INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock);
989
+ data->hdev = hdev;
990
+
991
+ hid_set_drvdata(hdev, data);
992
+
993
+ /*
994
+ * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
995
+ * We cannot read the state, only set it, so we force it to on here
996
+ * (which should be a no-op) to make sure that our state matches the
997
+ * keyboard's FN-lock state. This is the same as what Windows does.
998
+ */
999
+ data->fn_lock = true;
1000
+ lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
1001
+
1002
+ ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1003
+ if (ret)
1004
+ return ret;
1005
+
1006
+ ret = lenovo_register_leds(hdev);
1007
+ if (ret)
1008
+ goto err;
1009
+
1010
+ return 0;
1011
+err:
1012
+ sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1013
+ return ret;
1014
+}
1015
+
8171016 static int lenovo_probe(struct hid_device *hdev,
8181017 const struct hid_device_id *id)
8191018 {
....@@ -839,6 +1038,9 @@
8391038 case USB_DEVICE_ID_LENOVO_CBTKBD:
8401039 ret = lenovo_probe_cptkbd(hdev);
8411040 break;
1041
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1042
+ ret = lenovo_probe_tp10ubkbd(hdev);
1043
+ break;
8421044 default:
8431045 ret = 0;
8441046 break;
....@@ -855,7 +1057,7 @@
8551057
8561058 static void lenovo_remove_tpkbd(struct hid_device *hdev)
8571059 {
858
- struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
1060
+ struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
8591061
8601062 /*
8611063 * Only the trackpoint half of the keyboard has drvdata and stuff that
....@@ -869,14 +1071,26 @@
8691071
8701072 led_classdev_unregister(&data_pointer->led_micmute);
8711073 led_classdev_unregister(&data_pointer->led_mute);
872
-
873
- hid_set_drvdata(hdev, NULL);
8741074 }
8751075
8761076 static void lenovo_remove_cptkbd(struct hid_device *hdev)
8771077 {
8781078 sysfs_remove_group(&hdev->dev.kobj,
8791079 &lenovo_attr_group_cptkbd);
1080
+}
1081
+
1082
+static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
1083
+{
1084
+ struct lenovo_drvdata *data = hid_get_drvdata(hdev);
1085
+
1086
+ if (data == NULL)
1087
+ return;
1088
+
1089
+ led_classdev_unregister(&data->led_micmute);
1090
+ led_classdev_unregister(&data->led_mute);
1091
+
1092
+ sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1093
+ cancel_work_sync(&data->fn_lock_sync_work);
8801094 }
8811095
8821096 static void lenovo_remove(struct hid_device *hdev)
....@@ -888,6 +1102,9 @@
8881102 case USB_DEVICE_ID_LENOVO_CUSBKBD:
8891103 case USB_DEVICE_ID_LENOVO_CBTKBD:
8901104 lenovo_remove_cptkbd(hdev);
1105
+ break;
1106
+ case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1107
+ lenovo_remove_tp10ubkbd(hdev);
8911108 break;
8921109 }
8931110
....@@ -925,6 +1142,7 @@
9251142 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
9261143 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
9271144 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
1145
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
9281146 { }
9291147 };
9301148