blob: b2596ed37880d2c73222f6f16e4bf414fddbad46 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01002/*
Jamie Lentin6a5b4142014-07-23 23:30:46 +01003 * HID driver for Lenovo:
4 * - ThinkPad USB Keyboard with TrackPoint (tpkbd)
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01005 * - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd)
6 * - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01007 *
8 * Copyright (c) 2012 Bernhard Seibold
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01009 * Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk>
pgzha230cd52018-04-12 19:36:47 +020010 *
11 * Linux IBM/Lenovo Scrollpoint mouse driver:
12 * - IBM Scrollpoint III
13 * - IBM Scrollpoint Pro
14 * - IBM Scrollpoint Optical
15 * - IBM Scrollpoint Optical 800dpi
16 * - IBM Scrollpoint Optical 800dpi Pro
17 * - Lenovo Scrollpoint Optical
18 *
19 * Copyright (c) 2012 Peter De Wachter <pdewacht@gmail.com>
20 * Copyright (c) 2018 Peter Ganzhorn <peter.ganzhorn@gmail.com>
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010021 */
22
23/*
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010024 */
25
26#include <linux/module.h>
27#include <linux/sysfs.h>
28#include <linux/device.h>
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010029#include <linux/hid.h>
30#include <linux/input.h>
31#include <linux/leds.h>
Hans de Goedec87de332020-07-04 15:27:42 +020032#include <linux/workqueue.h>
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010033
34#include "hid-ids.h"
35
Hans de Goedeb72cdfa2020-07-04 15:27:38 +020036struct lenovo_drvdata {
Hans de Goedebc04b372020-07-04 15:27:41 +020037 u8 led_report[3]; /* Must be first for proper alignment */
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010038 int led_state;
Hans de Goedebc04b372020-07-04 15:27:41 +020039 struct mutex led_report_mutex;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010040 struct led_classdev led_mute;
41 struct led_classdev led_micmute;
Hans de Goedec87de332020-07-04 15:27:42 +020042 struct work_struct fn_lock_sync_work;
43 struct hid_device *hdev;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010044 int press_to_select;
45 int dragging;
46 int release_to_select;
47 int select_right;
48 int sensitivity;
49 int press_speed;
Jamie Lentin3cb5ff02015-08-11 22:40:52 +010050 u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
Jamie Lentinf3d4ff02014-07-23 23:30:48 +010051 bool fn_lock;
52};
53
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010054#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
55
Hans de Goedebc04b372020-07-04 15:27:41 +020056#define TP10UBKBD_LED_OUTPUT_REPORT 9
57
58#define TP10UBKBD_FN_LOCK_LED 0x54
59#define TP10UBKBD_MUTE_LED 0x64
60#define TP10UBKBD_MICMUTE_LED 0x74
61
62#define TP10UBKBD_LED_OFF 1
63#define TP10UBKBD_LED_ON 2
64
Hans de Goede658d04e2021-04-04 10:04:25 +020065static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
66 enum led_brightness value)
Hans de Goedebc04b372020-07-04 15:27:41 +020067{
68 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
69 int ret;
70
71 mutex_lock(&data->led_report_mutex);
72
73 data->led_report[0] = TP10UBKBD_LED_OUTPUT_REPORT;
74 data->led_report[1] = led_code;
75 data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF;
76 ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3,
77 HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
Hans de Goede658d04e2021-04-04 10:04:25 +020078 if (ret != 3) {
79 if (ret != -ENODEV)
80 hid_err(hdev, "Set LED output report error: %d\n", ret);
81
82 ret = ret < 0 ? ret : -EIO;
83 } else {
84 ret = 0;
85 }
Hans de Goedebc04b372020-07-04 15:27:41 +020086
87 mutex_unlock(&data->led_report_mutex);
Hans de Goede658d04e2021-04-04 10:04:25 +020088
89 return ret;
Hans de Goedebc04b372020-07-04 15:27:41 +020090}
91
Hans de Goedec87de332020-07-04 15:27:42 +020092static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work)
93{
94 struct lenovo_drvdata *data =
95 container_of(work, struct lenovo_drvdata, fn_lock_sync_work);
96
97 lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED,
98 data->fn_lock);
99}
100
Benjamin Tissoires181a8b92015-05-01 16:22:45 -0400101static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
102 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
103 0x09, 0x01, /* Usage (Vendor Usage 0x01) */
104 0xa1, 0x01, /* Collection (Application) */
105 0x85, 0x04, /* Report ID (4) */
106 0x19, 0x00, /* Usage Minimum (0) */
107 0x2a, 0xff, 0xff, /* Usage Maximum (65535) */
108};
109
110static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
111 unsigned int *rsize)
112{
113 switch (hdev->product) {
114 case USB_DEVICE_ID_LENOVO_TPPRODOCK:
115 /* the fixups that need to be done:
116 * - get a reasonable usage max for the vendor collection
117 * 0x8801 from the report ID 4
118 */
119 if (*rsize >= 153 &&
120 memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
121 sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
122 rdesc[151] = 0x01;
123 rdesc[152] = 0x00;
124 }
125 break;
126 }
127 return rdesc;
128}
129
Jamie Lentin94723bf2014-07-23 23:30:45 +0100130static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100131 struct hid_input *hi, struct hid_field *field,
132 struct hid_usage *usage, unsigned long **bit, int *max)
133{
Benjamin Tissoires0c521832013-09-11 22:12:24 +0200134 if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100135 /* This sub-device contains trackpoint, mark it */
Benjamin Tissoires0c521832013-09-11 22:12:24 +0200136 hid_set_drvdata(hdev, (void *)1);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100137 map_key_clear(KEY_MICMUTE);
138 return 1;
139 }
140 return 0;
141}
142
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100143static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
144 struct hid_input *hi, struct hid_field *field,
145 struct hid_usage *usage, unsigned long **bit, int *max)
146{
147 /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
148 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR ||
149 (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100150 switch (usage->hid & HID_USAGE) {
151 case 0x00f1: /* Fn-F4: Mic mute */
152 map_key_clear(KEY_MICMUTE);
153 return 1;
154 case 0x00f2: /* Fn-F5: Brightness down */
155 map_key_clear(KEY_BRIGHTNESSDOWN);
156 return 1;
157 case 0x00f3: /* Fn-F6: Brightness up */
158 map_key_clear(KEY_BRIGHTNESSUP);
159 return 1;
160 case 0x00f4: /* Fn-F7: External display (projector) */
161 map_key_clear(KEY_SWITCHVIDEOMODE);
162 return 1;
163 case 0x00f5: /* Fn-F8: Wireless */
164 map_key_clear(KEY_WLAN);
165 return 1;
166 case 0x00f6: /* Fn-F9: Control panel */
167 map_key_clear(KEY_CONFIG);
168 return 1;
169 case 0x00f8: /* Fn-F11: View open applications (3 boxes) */
170 map_key_clear(KEY_SCALE);
171 return 1;
Jamie Lentin5556eb12014-11-09 07:54:29 +0000172 case 0x00f9: /* Fn-F12: Open My computer (6 boxes) USB-only */
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100173 /* NB: This mapping is invented in raw_event below */
174 map_key_clear(KEY_FILE);
175 return 1;
Jamie Lentin5556eb12014-11-09 07:54:29 +0000176 case 0x00fa: /* Fn-Esc: Fn-lock toggle */
177 map_key_clear(KEY_FN_ESC);
178 return 1;
Jamie Lentin94eefa22014-12-16 21:26:46 +0000179 case 0x00fb: /* Middle mouse button (in native mode) */
180 map_key_clear(BTN_MIDDLE);
181 return 1;
182 }
183 }
184
185 /* Compatibility middle/wheel mappings should be ignored */
186 if (usage->hid == HID_GD_WHEEL)
187 return -1;
188 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON &&
189 (usage->hid & HID_USAGE) == 0x003)
190 return -1;
191 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER &&
192 (usage->hid & HID_USAGE) == 0x238)
193 return -1;
194
195 /* Map wheel emulation reports: 0xffa1 = USB, 0xff10 = BT */
196 if ((usage->hid & HID_USAGE_PAGE) == 0xff100000 ||
197 (usage->hid & HID_USAGE_PAGE) == 0xffa10000) {
198 field->flags |= HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE;
199 field->logical_minimum = -127;
200 field->logical_maximum = 127;
201
202 switch (usage->hid & HID_USAGE) {
203 case 0x0000:
Jamie Lentin7f650682015-08-11 22:40:50 +0100204 hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
Jamie Lentin94eefa22014-12-16 21:26:46 +0000205 return 1;
206 case 0x0001:
Jamie Lentin7f650682015-08-11 22:40:50 +0100207 hid_map_usage(hi, usage, bit, max, EV_REL, REL_WHEEL);
Jamie Lentin94eefa22014-12-16 21:26:46 +0000208 return 1;
209 default:
210 return -1;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100211 }
212 }
213
214 return 0;
215}
216
pgzha230cd52018-04-12 19:36:47 +0200217static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
218 struct hid_input *hi, struct hid_field *field,
219 struct hid_usage *usage, unsigned long **bit, int *max)
220{
221 if (usage->hid == HID_GD_Z) {
222 hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
223 return 1;
224 }
225 return 0;
226}
227
Hans de Goedebc04b372020-07-04 15:27:41 +0200228static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
229 struct hid_input *hi, struct hid_field *field,
230 struct hid_usage *usage, unsigned long **bit, int *max)
231{
232 /*
233 * The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for
234 * a bunch of keys which have no standard consumer page code.
235 */
236 if (usage->hid == 0x000c0001) {
237 switch (usage->usage_index) {
238 case 8: /* Fn-Esc: Fn-lock toggle */
239 map_key_clear(KEY_FN_ESC);
240 return 1;
241 case 9: /* Fn-F4: Mic mute */
242 map_key_clear(KEY_MICMUTE);
243 return 1;
244 case 10: /* Fn-F7: Control panel */
245 map_key_clear(KEY_CONFIG);
246 return 1;
247 case 11: /* Fn-F8: Search (magnifier glass) */
248 map_key_clear(KEY_SEARCH);
249 return 1;
250 case 12: /* Fn-F10: Open My computer (6 boxes) */
251 map_key_clear(KEY_FILE);
252 return 1;
253 }
254 }
255
Hans de Goede49429422020-07-04 22:10:59 +0200256 /*
257 * The Ultrabook Keyboard sends a spurious F23 key-press when resuming
258 * from suspend and it does not actually have a F23 key, ignore it.
259 */
260 if (usage->hid == 0x00070072)
261 return -1;
262
Hans de Goedebc04b372020-07-04 15:27:41 +0200263 return 0;
264}
265
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100266static int lenovo_input_mapping(struct hid_device *hdev,
267 struct hid_input *hi, struct hid_field *field,
268 struct hid_usage *usage, unsigned long **bit, int *max)
269{
270 switch (hdev->product) {
271 case USB_DEVICE_ID_LENOVO_TPKBD:
272 return lenovo_input_mapping_tpkbd(hdev, hi, field,
273 usage, bit, max);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100274 case USB_DEVICE_ID_LENOVO_CUSBKBD:
275 case USB_DEVICE_ID_LENOVO_CBTKBD:
276 return lenovo_input_mapping_cptkbd(hdev, hi, field,
277 usage, bit, max);
pgzha230cd52018-04-12 19:36:47 +0200278 case USB_DEVICE_ID_IBM_SCROLLPOINT_III:
279 case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO:
280 case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL:
281 case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL:
282 case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO:
283 case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
284 return lenovo_input_mapping_scrollpoint(hdev, hi, field,
285 usage, bit, max);
Hans de Goedebc04b372020-07-04 15:27:41 +0200286 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
287 return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
288 usage, bit, max);
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100289 default:
290 return 0;
291 }
292}
293
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100294#undef map_key_clear
295
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100296/* Send a config command to the keyboard */
297static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
298 unsigned char byte2, unsigned char byte3)
299{
300 int ret;
Josh Boyerea36ae02016-03-28 09:22:14 -0400301 unsigned char *buf;
302
303 buf = kzalloc(3, GFP_KERNEL);
304 if (!buf)
305 return -ENOMEM;
306
307 buf[0] = 0x18;
308 buf[1] = byte2;
309 buf[2] = byte3;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100310
311 switch (hdev->product) {
312 case USB_DEVICE_ID_LENOVO_CUSBKBD:
Josh Boyerea36ae02016-03-28 09:22:14 -0400313 ret = hid_hw_raw_request(hdev, 0x13, buf, 3,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100314 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
315 break;
316 case USB_DEVICE_ID_LENOVO_CBTKBD:
Josh Boyerea36ae02016-03-28 09:22:14 -0400317 ret = hid_hw_output_report(hdev, buf, 3);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100318 break;
319 default:
320 ret = -EINVAL;
321 break;
322 }
323
Josh Boyerea36ae02016-03-28 09:22:14 -0400324 kfree(buf);
325
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100326 return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */
327}
328
329static void lenovo_features_set_cptkbd(struct hid_device *hdev)
330{
331 int ret;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200332 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100333
334 ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
335 if (ret)
336 hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
Jamie Lentindbfebb42015-08-11 22:40:51 +0100337
338 ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity);
339 if (ret)
340 hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100341}
342
Hans de Goedeef550c52020-07-04 15:27:40 +0200343static ssize_t attr_fn_lock_show(struct device *dev,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100344 struct device_attribute *attr,
345 char *buf)
346{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800347 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeef550c52020-07-04 15:27:40 +0200348 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100349
Hans de Goedeef550c52020-07-04 15:27:40 +0200350 return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100351}
352
Hans de Goedeef550c52020-07-04 15:27:40 +0200353static ssize_t attr_fn_lock_store(struct device *dev,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100354 struct device_attribute *attr,
355 const char *buf,
356 size_t count)
357{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800358 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeef550c52020-07-04 15:27:40 +0200359 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
Hans de Goede658d04e2021-04-04 10:04:25 +0200360 int value, ret;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100361
362 if (kstrtoint(buf, 10, &value))
363 return -EINVAL;
364 if (value < 0 || value > 1)
365 return -EINVAL;
366
Hans de Goedeef550c52020-07-04 15:27:40 +0200367 data->fn_lock = !!value;
368
369 switch (hdev->product) {
370 case USB_DEVICE_ID_LENOVO_CUSBKBD:
371 case USB_DEVICE_ID_LENOVO_CBTKBD:
372 lenovo_features_set_cptkbd(hdev);
373 break;
Hans de Goedec87de332020-07-04 15:27:42 +0200374 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
Hans de Goede658d04e2021-04-04 10:04:25 +0200375 ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
376 if (ret)
377 return ret;
Hans de Goedec87de332020-07-04 15:27:42 +0200378 break;
Hans de Goedeef550c52020-07-04 15:27:40 +0200379 }
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100380
381 return count;
382}
383
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000384static ssize_t attr_sensitivity_show_cptkbd(struct device *dev,
385 struct device_attribute *attr,
386 char *buf)
387{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800388 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200389 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000390
391 return snprintf(buf, PAGE_SIZE, "%u\n",
392 cptkbd_data->sensitivity);
393}
394
395static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
396 struct device_attribute *attr,
397 const char *buf,
398 size_t count)
399{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800400 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200401 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000402 int value;
403
404 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
405 return -EINVAL;
406
407 cptkbd_data->sensitivity = value;
408 lenovo_features_set_cptkbd(hdev);
409
410 return count;
411}
412
413
Hans de Goedeef550c52020-07-04 15:27:40 +0200414static struct device_attribute dev_attr_fn_lock =
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100415 __ATTR(fn_lock, S_IWUSR | S_IRUGO,
Hans de Goedeef550c52020-07-04 15:27:40 +0200416 attr_fn_lock_show,
417 attr_fn_lock_store);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100418
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000419static struct device_attribute dev_attr_sensitivity_cptkbd =
420 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
421 attr_sensitivity_show_cptkbd,
422 attr_sensitivity_store_cptkbd);
423
424
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100425static struct attribute *lenovo_attributes_cptkbd[] = {
Hans de Goedeef550c52020-07-04 15:27:40 +0200426 &dev_attr_fn_lock.attr,
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000427 &dev_attr_sensitivity_cptkbd.attr,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100428 NULL
429};
430
431static const struct attribute_group lenovo_attr_group_cptkbd = {
432 .attrs = lenovo_attributes_cptkbd,
433};
434
435static int lenovo_raw_event(struct hid_device *hdev,
436 struct hid_report *report, u8 *data, int size)
437{
438 /*
439 * Compact USB keyboard's Fn-F12 report holds down many other keys, and
440 * its own key is outside the usage page range. Remove extra
441 * keypresses and remap to inside usage page.
442 */
443 if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
444 && size == 3
445 && data[0] == 0x15
446 && data[1] == 0x94
447 && data[2] == 0x01)) {
Jamie Lentin5556eb12014-11-09 07:54:29 +0000448 data[1] = 0x00;
449 data[2] = 0x01;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100450 }
451
452 return 0;
453}
454
Hans de Goedec87de332020-07-04 15:27:42 +0200455static int lenovo_event_tp10ubkbd(struct hid_device *hdev,
456 struct hid_field *field, struct hid_usage *usage, __s32 value)
457{
458 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
459
460 if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
461 /*
462 * The user has toggled the Fn-lock state. Toggle our own
463 * cached value of it and sync our value to the keyboard to
464 * ensure things are in sync (the sycning should be a no-op).
465 */
466 data->fn_lock = !data->fn_lock;
467 schedule_work(&data->fn_lock_sync_work);
468 }
469
470 return 0;
471}
472
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100473static int lenovo_event_cptkbd(struct hid_device *hdev,
474 struct hid_field *field, struct hid_usage *usage, __s32 value)
475{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200476 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100477
478 /* "wheel" scroll events */
479 if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
480 usage->code == REL_HWHEEL)) {
481 /* Scroll events disable middle-click event */
482 cptkbd_data->middlebutton_state = 2;
483 return 0;
484 }
485
486 /* Middle click events */
487 if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
488 if (value == 1) {
489 cptkbd_data->middlebutton_state = 1;
490 } else if (value == 0) {
491 if (cptkbd_data->middlebutton_state == 1) {
492 /* No scrolling inbetween, send middle-click */
493 input_event(field->hidinput->input,
494 EV_KEY, BTN_MIDDLE, 1);
495 input_sync(field->hidinput->input);
496 input_event(field->hidinput->input,
497 EV_KEY, BTN_MIDDLE, 0);
498 input_sync(field->hidinput->input);
499 }
500 cptkbd_data->middlebutton_state = 0;
501 }
502 return 1;
503 }
504
505 return 0;
506}
507
508static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
509 struct hid_usage *usage, __s32 value)
510{
Hans de Goede34348a82021-04-04 10:04:26 +0200511 if (!hid_get_drvdata(hdev))
512 return 0;
513
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100514 switch (hdev->product) {
515 case USB_DEVICE_ID_LENOVO_CUSBKBD:
516 case USB_DEVICE_ID_LENOVO_CBTKBD:
517 return lenovo_event_cptkbd(hdev, field, usage, value);
Hans de Goedec87de332020-07-04 15:27:42 +0200518 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
519 return lenovo_event_tp10ubkbd(hdev, field, usage, value);
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100520 default:
521 return 0;
522 }
523}
524
Jamie Lentin94723bf2014-07-23 23:30:45 +0100525static int lenovo_features_set_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100526{
527 struct hid_report *report;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200528 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100529
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100530 report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
531
532 report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
533 report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08;
534 report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
535 report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40;
536 report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
537 report->field[2]->value[0] = data_pointer->sensitivity;
538 report->field[3]->value[0] = data_pointer->press_speed;
539
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100540 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100541 return 0;
542}
543
Jamie Lentin94723bf2014-07-23 23:30:45 +0100544static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100545 struct device_attribute *attr,
546 char *buf)
547{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800548 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200549 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100550
551 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
552}
553
Jamie Lentin94723bf2014-07-23 23:30:45 +0100554static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100555 struct device_attribute *attr,
556 const char *buf,
557 size_t count)
558{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800559 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200560 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100561 int value;
562
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100563 if (kstrtoint(buf, 10, &value))
564 return -EINVAL;
565 if (value < 0 || value > 1)
566 return -EINVAL;
567
568 data_pointer->press_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100569 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100570
571 return count;
572}
573
Jamie Lentin94723bf2014-07-23 23:30:45 +0100574static ssize_t attr_dragging_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100575 struct device_attribute *attr,
576 char *buf)
577{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800578 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200579 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100580
581 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
582}
583
Jamie Lentin94723bf2014-07-23 23:30:45 +0100584static ssize_t attr_dragging_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100585 struct device_attribute *attr,
586 const char *buf,
587 size_t count)
588{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800589 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200590 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100591 int value;
592
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100593 if (kstrtoint(buf, 10, &value))
594 return -EINVAL;
595 if (value < 0 || value > 1)
596 return -EINVAL;
597
598 data_pointer->dragging = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100599 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100600
601 return count;
602}
603
Jamie Lentin94723bf2014-07-23 23:30:45 +0100604static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100605 struct device_attribute *attr,
606 char *buf)
607{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800608 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200609 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100610
611 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
612}
613
Jamie Lentin94723bf2014-07-23 23:30:45 +0100614static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100615 struct device_attribute *attr,
616 const char *buf,
617 size_t count)
618{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800619 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200620 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100621 int value;
622
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100623 if (kstrtoint(buf, 10, &value))
624 return -EINVAL;
625 if (value < 0 || value > 1)
626 return -EINVAL;
627
628 data_pointer->release_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100629 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100630
631 return count;
632}
633
Jamie Lentin94723bf2014-07-23 23:30:45 +0100634static ssize_t attr_select_right_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100635 struct device_attribute *attr,
636 char *buf)
637{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800638 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200639 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100640
641 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
642}
643
Jamie Lentin94723bf2014-07-23 23:30:45 +0100644static ssize_t attr_select_right_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100645 struct device_attribute *attr,
646 const char *buf,
647 size_t count)
648{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800649 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200650 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100651 int value;
652
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100653 if (kstrtoint(buf, 10, &value))
654 return -EINVAL;
655 if (value < 0 || value > 1)
656 return -EINVAL;
657
658 data_pointer->select_right = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100659 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100660
661 return count;
662}
663
Jamie Lentin94723bf2014-07-23 23:30:45 +0100664static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100665 struct device_attribute *attr,
666 char *buf)
667{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800668 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200669 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100670
671 return snprintf(buf, PAGE_SIZE, "%u\n",
672 data_pointer->sensitivity);
673}
674
Jamie Lentin94723bf2014-07-23 23:30:45 +0100675static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100676 struct device_attribute *attr,
677 const char *buf,
678 size_t count)
679{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800680 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200681 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100682 int value;
683
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100684 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
685 return -EINVAL;
686
687 data_pointer->sensitivity = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100688 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100689
690 return count;
691}
692
Jamie Lentin94723bf2014-07-23 23:30:45 +0100693static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100694 struct device_attribute *attr,
695 char *buf)
696{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800697 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200698 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100699
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100700 return snprintf(buf, PAGE_SIZE, "%u\n",
701 data_pointer->press_speed);
702}
703
Jamie Lentin94723bf2014-07-23 23:30:45 +0100704static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100705 struct device_attribute *attr,
706 const char *buf,
707 size_t count)
708{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800709 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200710 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100711 int value;
712
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100713 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
714 return -EINVAL;
715
716 data_pointer->press_speed = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100717 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100718
719 return count;
720}
721
Jamie Lentin94723bf2014-07-23 23:30:45 +0100722static struct device_attribute dev_attr_press_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100723 __ATTR(press_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100724 attr_press_to_select_show_tpkbd,
725 attr_press_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100726
Jamie Lentin94723bf2014-07-23 23:30:45 +0100727static struct device_attribute dev_attr_dragging_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100728 __ATTR(dragging, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100729 attr_dragging_show_tpkbd,
730 attr_dragging_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100731
Jamie Lentin94723bf2014-07-23 23:30:45 +0100732static struct device_attribute dev_attr_release_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100733 __ATTR(release_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100734 attr_release_to_select_show_tpkbd,
735 attr_release_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100736
Jamie Lentin94723bf2014-07-23 23:30:45 +0100737static struct device_attribute dev_attr_select_right_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100738 __ATTR(select_right, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100739 attr_select_right_show_tpkbd,
740 attr_select_right_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100741
Jamie Lentin94723bf2014-07-23 23:30:45 +0100742static struct device_attribute dev_attr_sensitivity_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100743 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100744 attr_sensitivity_show_tpkbd,
745 attr_sensitivity_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100746
Jamie Lentin94723bf2014-07-23 23:30:45 +0100747static struct device_attribute dev_attr_press_speed_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100748 __ATTR(press_speed, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100749 attr_press_speed_show_tpkbd,
750 attr_press_speed_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100751
Jamie Lentin94723bf2014-07-23 23:30:45 +0100752static struct attribute *lenovo_attributes_tpkbd[] = {
753 &dev_attr_press_to_select_tpkbd.attr,
754 &dev_attr_dragging_tpkbd.attr,
755 &dev_attr_release_to_select_tpkbd.attr,
756 &dev_attr_select_right_tpkbd.attr,
757 &dev_attr_sensitivity_tpkbd.attr,
758 &dev_attr_press_speed_tpkbd.attr,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100759 NULL
760};
761
Jamie Lentin94723bf2014-07-23 23:30:45 +0100762static const struct attribute_group lenovo_attr_group_tpkbd = {
763 .attrs = lenovo_attributes_tpkbd,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100764};
765
Hans de Goede484921f2020-07-04 15:27:39 +0200766static void lenovo_led_set_tpkbd(struct hid_device *hdev)
767{
768 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
769 struct hid_report *report;
770
771 report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
772 report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
773 report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
774 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
775}
776
777static enum led_brightness lenovo_led_brightness_get(
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100778 struct led_classdev *led_cdev)
779{
Axel Lin832fbea2012-09-13 14:12:08 +0800780 struct device *dev = led_cdev->dev->parent;
Geliang Tangee79a8f2015-12-27 17:25:21 +0800781 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200782 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100783 int led_nr = 0;
784
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100785 if (led_cdev == &data_pointer->led_micmute)
786 led_nr = 1;
787
788 return data_pointer->led_state & (1 << led_nr)
789 ? LED_FULL
790 : LED_OFF;
791}
792
Hans de Goedebbf62642021-04-04 10:04:24 +0200793static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100794 enum led_brightness value)
795{
Axel Lin832fbea2012-09-13 14:12:08 +0800796 struct device *dev = led_cdev->dev->parent;
Geliang Tangee79a8f2015-12-27 17:25:21 +0800797 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200798 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Hans de Goedebc04b372020-07-04 15:27:41 +0200799 u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100800 int led_nr = 0;
Hans de Goede658d04e2021-04-04 10:04:25 +0200801 int ret = 0;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100802
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100803 if (led_cdev == &data_pointer->led_micmute)
804 led_nr = 1;
805
806 if (value == LED_OFF)
807 data_pointer->led_state &= ~(1 << led_nr);
808 else
809 data_pointer->led_state |= 1 << led_nr;
810
Hans de Goede484921f2020-07-04 15:27:39 +0200811 switch (hdev->product) {
812 case USB_DEVICE_ID_LENOVO_TPKBD:
813 lenovo_led_set_tpkbd(hdev);
814 break;
Hans de Goedebc04b372020-07-04 15:27:41 +0200815 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
Hans de Goede658d04e2021-04-04 10:04:25 +0200816 ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
Hans de Goedebc04b372020-07-04 15:27:41 +0200817 break;
Hans de Goede484921f2020-07-04 15:27:39 +0200818 }
Hans de Goedebbf62642021-04-04 10:04:24 +0200819
Hans de Goede658d04e2021-04-04 10:04:25 +0200820 return ret;
Hans de Goede484921f2020-07-04 15:27:39 +0200821}
822
823static int lenovo_register_leds(struct hid_device *hdev)
824{
825 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
826 size_t name_sz = strlen(dev_name(&hdev->dev)) + 16;
827 char *name_mute, *name_micm;
828 int ret;
829
830 name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
831 name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
832 if (name_mute == NULL || name_micm == NULL) {
833 hid_err(hdev, "Could not allocate memory for led data\n");
834 return -ENOMEM;
835 }
836 snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev));
837 snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
838
839 data->led_mute.name = name_mute;
840 data->led_mute.brightness_get = lenovo_led_brightness_get;
Hans de Goedebbf62642021-04-04 10:04:24 +0200841 data->led_mute.brightness_set_blocking = lenovo_led_brightness_set;
Hans de Goede658d04e2021-04-04 10:04:25 +0200842 data->led_mute.flags = LED_HW_PLUGGABLE;
Hans de Goede484921f2020-07-04 15:27:39 +0200843 data->led_mute.dev = &hdev->dev;
844 ret = led_classdev_register(&hdev->dev, &data->led_mute);
845 if (ret < 0)
846 return ret;
847
848 data->led_micmute.name = name_micm;
849 data->led_micmute.brightness_get = lenovo_led_brightness_get;
Hans de Goedebbf62642021-04-04 10:04:24 +0200850 data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set;
Hans de Goede658d04e2021-04-04 10:04:25 +0200851 data->led_micmute.flags = LED_HW_PLUGGABLE;
Hans de Goede484921f2020-07-04 15:27:39 +0200852 data->led_micmute.dev = &hdev->dev;
853 ret = led_classdev_register(&hdev->dev, &data->led_micmute);
854 if (ret < 0) {
855 led_classdev_unregister(&data->led_mute);
856 return ret;
857 }
858
859 return 0;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100860}
861
Jamie Lentin94723bf2014-07-23 23:30:45 +0100862static int lenovo_probe_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100863{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200864 struct lenovo_drvdata *data_pointer;
Hans de Goede484921f2020-07-04 15:27:39 +0200865 int i, ret;
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200866
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100867 /*
868 * Only register extra settings against subdevice where input_mapping
869 * set drvdata to 1, i.e. the trackpoint.
870 */
871 if (!hid_get_drvdata(hdev))
872 return 0;
873
874 hid_set_drvdata(hdev, NULL);
875
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200876 /* Validate required reports. */
877 for (i = 0; i < 4; i++) {
878 if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
879 return -ENODEV;
880 }
881 if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
882 return -ENODEV;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100883
Jamie Lentine0a6aad2014-07-26 15:42:27 +0100884 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
885 if (ret)
886 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100887
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200888 data_pointer = devm_kzalloc(&hdev->dev,
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200889 sizeof(struct lenovo_drvdata),
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200890 GFP_KERNEL);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100891 if (data_pointer == NULL) {
892 hid_err(hdev, "Could not allocate memory for driver data\n");
Alexey Khoroshilovb7212602015-05-29 03:39:31 +0300893 ret = -ENOMEM;
894 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100895 }
896
897 // set same default values as windows driver
898 data_pointer->sensitivity = 0xa0;
899 data_pointer->press_speed = 0x38;
900
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100901 hid_set_drvdata(hdev, data_pointer);
902
Hans de Goede484921f2020-07-04 15:27:39 +0200903 ret = lenovo_register_leds(hdev);
904 if (ret)
Aditya Pakki6ae16df2018-12-24 15:39:14 -0600905 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100906
Jamie Lentin94723bf2014-07-23 23:30:45 +0100907 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100908
909 return 0;
Alexey Khoroshilovb7212602015-05-29 03:39:31 +0300910err:
911 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
912 return ret;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100913}
914
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100915static int lenovo_probe_cptkbd(struct hid_device *hdev)
916{
917 int ret;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200918 struct lenovo_drvdata *cptkbd_data;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100919
920 /* All the custom action happens on the USBMOUSE device for USB */
921 if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
922 && hdev->type != HID_TYPE_USBMOUSE) {
923 hid_dbg(hdev, "Ignoring keyboard half of device\n");
924 return 0;
925 }
926
927 cptkbd_data = devm_kzalloc(&hdev->dev,
928 sizeof(*cptkbd_data),
929 GFP_KERNEL);
930 if (cptkbd_data == NULL) {
931 hid_err(hdev, "can't alloc keyboard descriptor\n");
932 return -ENOMEM;
933 }
934 hid_set_drvdata(hdev, cptkbd_data);
935
936 /*
937 * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
938 * regular keys
939 */
940 ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
941 if (ret)
942 hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
943
Jamie Lentin94eefa22014-12-16 21:26:46 +0000944 /* Switch middle button to native mode */
945 ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
946 if (ret)
947 hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
948
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000949 /* Set keyboard settings to known state */
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100950 cptkbd_data->middlebutton_state = 0;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100951 cptkbd_data->fn_lock = true;
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000952 cptkbd_data->sensitivity = 0x05;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100953 lenovo_features_set_cptkbd(hdev);
954
955 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd);
956 if (ret)
957 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
958
959 return 0;
960}
961
Hans de Goedec87de332020-07-04 15:27:42 +0200962static struct attribute *lenovo_attributes_tp10ubkbd[] = {
963 &dev_attr_fn_lock.attr,
964 NULL
965};
966
967static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
968 .attrs = lenovo_attributes_tp10ubkbd,
969};
970
Hans de Goedebc04b372020-07-04 15:27:41 +0200971static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
972{
973 struct lenovo_drvdata *data;
Hans de Goedec87de332020-07-04 15:27:42 +0200974 int ret;
Hans de Goedebc04b372020-07-04 15:27:41 +0200975
976 /* All the custom action happens on the USBMOUSE device for USB */
977 if (hdev->type != HID_TYPE_USBMOUSE)
978 return 0;
979
980 data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
981 if (!data)
982 return -ENOMEM;
983
984 mutex_init(&data->led_report_mutex);
Hans de Goedec87de332020-07-04 15:27:42 +0200985 INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock);
986 data->hdev = hdev;
Hans de Goedebc04b372020-07-04 15:27:41 +0200987
988 hid_set_drvdata(hdev, data);
989
Hans de Goedec87de332020-07-04 15:27:42 +0200990 /*
991 * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
992 * We cannot read the state, only set it, so we force it to on here
993 * (which should be a no-op) to make sure that our state matches the
994 * keyboard's FN-lock state. This is the same as what Windows does.
995 */
996 data->fn_lock = true;
997 lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
998
999 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1000 if (ret)
1001 return ret;
1002
1003 ret = lenovo_register_leds(hdev);
1004 if (ret)
1005 goto err;
1006
1007 return 0;
1008err:
1009 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1010 return ret;
Hans de Goedebc04b372020-07-04 15:27:41 +02001011}
1012
Jamie Lentin94723bf2014-07-23 23:30:45 +01001013static int lenovo_probe(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001014 const struct hid_device_id *id)
1015{
1016 int ret;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001017
1018 ret = hid_parse(hdev);
1019 if (ret) {
1020 hid_err(hdev, "hid_parse failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001021 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001022 }
1023
1024 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1025 if (ret) {
1026 hid_err(hdev, "hid_hw_start failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001027 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001028 }
1029
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001030 switch (hdev->product) {
1031 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +01001032 ret = lenovo_probe_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001033 break;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001034 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1035 case USB_DEVICE_ID_LENOVO_CBTKBD:
1036 ret = lenovo_probe_cptkbd(hdev);
1037 break;
Hans de Goedebc04b372020-07-04 15:27:41 +02001038 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1039 ret = lenovo_probe_tp10ubkbd(hdev);
1040 break;
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001041 default:
1042 ret = 0;
1043 break;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001044 }
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001045 if (ret)
1046 goto err_hid;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001047
1048 return 0;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001049err_hid:
1050 hid_hw_stop(hdev);
1051err:
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001052 return ret;
1053}
1054
Jamie Lentin94723bf2014-07-23 23:30:45 +01001055static void lenovo_remove_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001056{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +02001057 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001058
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001059 /*
1060 * Only the trackpoint half of the keyboard has drvdata and stuff that
1061 * needs unregistering.
1062 */
1063 if (data_pointer == NULL)
1064 return;
1065
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001066 sysfs_remove_group(&hdev->dev.kobj,
Jamie Lentin94723bf2014-07-23 23:30:45 +01001067 &lenovo_attr_group_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001068
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001069 led_classdev_unregister(&data_pointer->led_micmute);
1070 led_classdev_unregister(&data_pointer->led_mute);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001071}
1072
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001073static void lenovo_remove_cptkbd(struct hid_device *hdev)
1074{
1075 sysfs_remove_group(&hdev->dev.kobj,
1076 &lenovo_attr_group_cptkbd);
1077}
1078
Hans de Goedebc04b372020-07-04 15:27:41 +02001079static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
1080{
1081 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
1082
1083 if (data == NULL)
1084 return;
1085
1086 led_classdev_unregister(&data->led_micmute);
1087 led_classdev_unregister(&data->led_mute);
Hans de Goedec87de332020-07-04 15:27:42 +02001088
1089 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1090 cancel_work_sync(&data->fn_lock_sync_work);
Hans de Goedebc04b372020-07-04 15:27:41 +02001091}
1092
Jamie Lentin94723bf2014-07-23 23:30:45 +01001093static void lenovo_remove(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001094{
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001095 switch (hdev->product) {
1096 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +01001097 lenovo_remove_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001098 break;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001099 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1100 case USB_DEVICE_ID_LENOVO_CBTKBD:
1101 lenovo_remove_cptkbd(hdev);
1102 break;
Hans de Goedebc04b372020-07-04 15:27:41 +02001103 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1104 lenovo_remove_tp10ubkbd(hdev);
1105 break;
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001106 }
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001107
1108 hid_hw_stop(hdev);
1109}
1110
Dmitry Torokhov91543012015-09-29 15:52:59 -07001111static int lenovo_input_configured(struct hid_device *hdev,
Andreas Fleigd92189e2015-04-23 10:25:58 +02001112 struct hid_input *hi)
1113{
1114 switch (hdev->product) {
1115 case USB_DEVICE_ID_LENOVO_TPKBD:
1116 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1117 case USB_DEVICE_ID_LENOVO_CBTKBD:
1118 if (test_bit(EV_REL, hi->input->evbit)) {
1119 /* set only for trackpoint device */
1120 __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
1121 __set_bit(INPUT_PROP_POINTING_STICK,
1122 hi->input->propbit);
1123 }
1124 break;
1125 }
Dmitry Torokhov91543012015-09-29 15:52:59 -07001126
1127 return 0;
Andreas Fleigd92189e2015-04-23 10:25:58 +02001128}
1129
1130
Jamie Lentin94723bf2014-07-23 23:30:45 +01001131static const struct hid_device_id lenovo_devices[] = {
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001132 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001133 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
1134 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
Benjamin Tissoires181a8b92015-05-01 16:22:45 -04001135 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
pgzha230cd52018-04-12 19:36:47 +02001136 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_III) },
1137 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_PRO) },
1138 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL) },
1139 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
1140 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
1141 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
Hans de Goedebc04b372020-07-04 15:27:41 +02001142 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001143 { }
1144};
1145
Jamie Lentin94723bf2014-07-23 23:30:45 +01001146MODULE_DEVICE_TABLE(hid, lenovo_devices);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001147
Jamie Lentin94723bf2014-07-23 23:30:45 +01001148static struct hid_driver lenovo_driver = {
1149 .name = "lenovo",
1150 .id_table = lenovo_devices,
Andreas Fleigd92189e2015-04-23 10:25:58 +02001151 .input_configured = lenovo_input_configured,
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001152 .input_mapping = lenovo_input_mapping,
Jamie Lentin94723bf2014-07-23 23:30:45 +01001153 .probe = lenovo_probe,
1154 .remove = lenovo_remove,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001155 .raw_event = lenovo_raw_event,
Jamie Lentin3cb5ff02015-08-11 22:40:52 +01001156 .event = lenovo_event,
Benjamin Tissoires181a8b92015-05-01 16:22:45 -04001157 .report_fixup = lenovo_report_fixup,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001158};
Jamie Lentin94723bf2014-07-23 23:30:45 +01001159module_hid_driver(lenovo_driver);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001160
1161MODULE_LICENSE("GPL");