Skip to content

Commit 0b21c35

Browse files
author
Jiri Kosina
committed
Merge branch 'for-5.13/lenovo' into for-linus
- LED fixes and Thinkpad X1 Tablet keyboard support, from Hans de Goede
2 parents cddbefc + c158c2a commit 0b21c35

File tree

1 file changed

+114
-33
lines changed

1 file changed

+114
-33
lines changed

drivers/hid/hid-lenovo.c

Lines changed: 114 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333

3434
#include "hid-ids.h"
3535

36+
/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
37+
#define LENOVO_KEY_MICMUTE KEY_F20
38+
3639
struct lenovo_drvdata {
3740
u8 led_report[3]; /* Must be first for proper alignment */
3841
int led_state;
@@ -62,8 +65,8 @@ struct lenovo_drvdata {
6265
#define TP10UBKBD_LED_OFF 1
6366
#define TP10UBKBD_LED_ON 2
6467

65-
static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
66-
enum led_brightness value)
68+
static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
69+
enum led_brightness value)
6770
{
6871
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
6972
int ret;
@@ -75,10 +78,18 @@ static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
7578
data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF;
7679
ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3,
7780
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
78-
if (ret)
79-
hid_err(hdev, "Set LED output report error: %d\n", ret);
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+
}
8089

8190
mutex_unlock(&data->led_report_mutex);
91+
92+
return ret;
8293
}
8394

8495
static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work)
@@ -126,7 +137,7 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
126137
if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
127138
/* This sub-device contains trackpoint, mark it */
128139
hid_set_drvdata(hdev, (void *)1);
129-
map_key_clear(KEY_MICMUTE);
140+
map_key_clear(LENOVO_KEY_MICMUTE);
130141
return 1;
131142
}
132143
return 0;
@@ -141,7 +152,7 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
141152
(usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
142153
switch (usage->hid & HID_USAGE) {
143154
case 0x00f1: /* Fn-F4: Mic mute */
144-
map_key_clear(KEY_MICMUTE);
155+
map_key_clear(LENOVO_KEY_MICMUTE);
145156
return 1;
146157
case 0x00f2: /* Fn-F5: Brightness down */
147158
map_key_clear(KEY_BRIGHTNESSDOWN);
@@ -231,7 +242,7 @@ static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
231242
map_key_clear(KEY_FN_ESC);
232243
return 1;
233244
case 9: /* Fn-F4: Mic mute */
234-
map_key_clear(KEY_MICMUTE);
245+
map_key_clear(LENOVO_KEY_MICMUTE);
235246
return 1;
236247
case 10: /* Fn-F7: Control panel */
237248
map_key_clear(KEY_CONFIG);
@@ -255,6 +266,54 @@ static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
255266
return 0;
256267
}
257268

269+
static int lenovo_input_mapping_x1_tab_kbd(struct hid_device *hdev,
270+
struct hid_input *hi, struct hid_field *field,
271+
struct hid_usage *usage, unsigned long **bit, int *max)
272+
{
273+
/*
274+
* The ThinkPad X1 Tablet Thin Keyboard uses 0x000c0001 usage for
275+
* a bunch of keys which have no standard consumer page code.
276+
*/
277+
if (usage->hid == 0x000c0001) {
278+
switch (usage->usage_index) {
279+
case 0: /* Fn-F10: Enable/disable bluetooth */
280+
map_key_clear(KEY_BLUETOOTH);
281+
return 1;
282+
case 1: /* Fn-F11: Keyboard settings */
283+
map_key_clear(KEY_KEYBOARD);
284+
return 1;
285+
case 2: /* Fn-F12: User function / Cortana */
286+
map_key_clear(KEY_MACRO1);
287+
return 1;
288+
case 3: /* Fn-PrtSc: Snipping tool */
289+
map_key_clear(KEY_SELECTIVE_SCREENSHOT);
290+
return 1;
291+
case 8: /* Fn-Esc: Fn-lock toggle */
292+
map_key_clear(KEY_FN_ESC);
293+
return 1;
294+
case 9: /* Fn-F4: Mute/unmute microphone */
295+
map_key_clear(KEY_MICMUTE);
296+
return 1;
297+
case 10: /* Fn-F9: Settings */
298+
map_key_clear(KEY_CONFIG);
299+
return 1;
300+
case 13: /* Fn-F7: Manage external displays */
301+
map_key_clear(KEY_SWITCHVIDEOMODE);
302+
return 1;
303+
case 14: /* Fn-F8: Enable/disable wifi */
304+
map_key_clear(KEY_WLAN);
305+
return 1;
306+
}
307+
}
308+
309+
if (usage->hid == (HID_UP_KEYBOARD | 0x009a)) {
310+
map_key_clear(KEY_SYSRQ);
311+
return 1;
312+
}
313+
314+
return 0;
315+
}
316+
258317
static int lenovo_input_mapping(struct hid_device *hdev,
259318
struct hid_input *hi, struct hid_field *field,
260319
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -278,6 +337,8 @@ static int lenovo_input_mapping(struct hid_device *hdev,
278337
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
279338
return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
280339
usage, bit, max);
340+
case USB_DEVICE_ID_LENOVO_X1_TAB:
341+
return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max);
281342
default:
282343
return 0;
283344
}
@@ -349,7 +410,7 @@ static ssize_t attr_fn_lock_store(struct device *dev,
349410
{
350411
struct hid_device *hdev = to_hid_device(dev);
351412
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
352-
int value;
413+
int value, ret;
353414

354415
if (kstrtoint(buf, 10, &value))
355416
return -EINVAL;
@@ -364,7 +425,10 @@ static ssize_t attr_fn_lock_store(struct device *dev,
364425
lenovo_features_set_cptkbd(hdev);
365426
break;
366427
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
367-
lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
428+
case USB_DEVICE_ID_LENOVO_X1_TAB:
429+
ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
430+
if (ret)
431+
return ret;
368432
break;
369433
}
370434

@@ -498,11 +562,15 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
498562
static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
499563
struct hid_usage *usage, __s32 value)
500564
{
565+
if (!hid_get_drvdata(hdev))
566+
return 0;
567+
501568
switch (hdev->product) {
502569
case USB_DEVICE_ID_LENOVO_CUSBKBD:
503570
case USB_DEVICE_ID_LENOVO_CBTKBD:
504571
return lenovo_event_cptkbd(hdev, field, usage, value);
505572
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
573+
case USB_DEVICE_ID_LENOVO_X1_TAB:
506574
return lenovo_event_tp10ubkbd(hdev, field, usage, value);
507575
default:
508576
return 0;
@@ -761,30 +829,15 @@ static void lenovo_led_set_tpkbd(struct hid_device *hdev)
761829
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
762830
}
763831

764-
static enum led_brightness lenovo_led_brightness_get(
765-
struct led_classdev *led_cdev)
766-
{
767-
struct device *dev = led_cdev->dev->parent;
768-
struct hid_device *hdev = to_hid_device(dev);
769-
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
770-
int led_nr = 0;
771-
772-
if (led_cdev == &data_pointer->led_micmute)
773-
led_nr = 1;
774-
775-
return data_pointer->led_state & (1 << led_nr)
776-
? LED_FULL
777-
: LED_OFF;
778-
}
779-
780-
static void lenovo_led_brightness_set(struct led_classdev *led_cdev,
832+
static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
781833
enum led_brightness value)
782834
{
783835
struct device *dev = led_cdev->dev->parent;
784836
struct hid_device *hdev = to_hid_device(dev);
785837
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
786838
u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
787839
int led_nr = 0;
840+
int ret = 0;
788841

789842
if (led_cdev == &data_pointer->led_micmute)
790843
led_nr = 1;
@@ -799,9 +852,12 @@ static void lenovo_led_brightness_set(struct led_classdev *led_cdev,
799852
lenovo_led_set_tpkbd(hdev);
800853
break;
801854
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
802-
lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
855+
case USB_DEVICE_ID_LENOVO_X1_TAB:
856+
ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
803857
break;
804858
}
859+
860+
return ret;
805861
}
806862

807863
static int lenovo_register_leds(struct hid_device *hdev)
@@ -821,16 +877,20 @@ static int lenovo_register_leds(struct hid_device *hdev)
821877
snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
822878

823879
data->led_mute.name = name_mute;
824-
data->led_mute.brightness_get = lenovo_led_brightness_get;
825-
data->led_mute.brightness_set = lenovo_led_brightness_set;
880+
data->led_mute.default_trigger = "audio-mute";
881+
data->led_mute.brightness_set_blocking = lenovo_led_brightness_set;
882+
data->led_mute.max_brightness = 1;
883+
data->led_mute.flags = LED_HW_PLUGGABLE;
826884
data->led_mute.dev = &hdev->dev;
827885
ret = led_classdev_register(&hdev->dev, &data->led_mute);
828886
if (ret < 0)
829887
return ret;
830888

831889
data->led_micmute.name = name_micm;
832-
data->led_micmute.brightness_get = lenovo_led_brightness_get;
833-
data->led_micmute.brightness_set = lenovo_led_brightness_set;
890+
data->led_micmute.default_trigger = "audio-micmute";
891+
data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set;
892+
data->led_micmute.max_brightness = 1;
893+
data->led_micmute.flags = LED_HW_PLUGGABLE;
834894
data->led_micmute.dev = &hdev->dev;
835895
ret = led_classdev_register(&hdev->dev, &data->led_micmute);
836896
if (ret < 0) {
@@ -952,11 +1012,24 @@ static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
9521012

9531013
static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
9541014
{
1015+
struct hid_report_enum *rep_enum;
9551016
struct lenovo_drvdata *data;
1017+
struct hid_report *rep;
1018+
bool found;
9561019
int ret;
9571020

958-
/* All the custom action happens on the USBMOUSE device for USB */
959-
if (hdev->type != HID_TYPE_USBMOUSE)
1021+
/*
1022+
* The LEDs and the Fn-lock functionality use output report 9,
1023+
* with an application of 0xffa0001, add the LEDs on the interface
1024+
* with this output report.
1025+
*/
1026+
found = false;
1027+
rep_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
1028+
list_for_each_entry(rep, &rep_enum->report_list, list) {
1029+
if (rep->application == 0xffa00001)
1030+
found = true;
1031+
}
1032+
if (!found)
9601033
return 0;
9611034

9621035
data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
@@ -1018,6 +1091,7 @@ static int lenovo_probe(struct hid_device *hdev,
10181091
ret = lenovo_probe_cptkbd(hdev);
10191092
break;
10201093
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1094+
case USB_DEVICE_ID_LENOVO_X1_TAB:
10211095
ret = lenovo_probe_tp10ubkbd(hdev);
10221096
break;
10231097
default:
@@ -1083,6 +1157,7 @@ static void lenovo_remove(struct hid_device *hdev)
10831157
lenovo_remove_cptkbd(hdev);
10841158
break;
10851159
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1160+
case USB_DEVICE_ID_LENOVO_X1_TAB:
10861161
lenovo_remove_tp10ubkbd(hdev);
10871162
break;
10881163
}
@@ -1122,6 +1197,12 @@ static const struct hid_device_id lenovo_devices[] = {
11221197
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
11231198
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
11241199
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
1200+
/*
1201+
* Note bind to the HID_GROUP_GENERIC group, so that we only bind to the keyboard
1202+
* part, while letting hid-multitouch.c handle the touchpad and trackpoint.
1203+
*/
1204+
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
1205+
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) },
11251206
{ }
11261207
};
11271208

0 commit comments

Comments
 (0)