blob: 2287142116b91f0d1b4bed574f5cda93f4d85052 [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 Goede61710322021-04-04 10:04:29 +020036/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
37#define LENOVO_KEY_MICMUTE KEY_F20
38
Hans de Goedeb72cdfa2020-07-04 15:27:38 +020039struct lenovo_drvdata {
Hans de Goedebc04b372020-07-04 15:27:41 +020040 u8 led_report[3]; /* Must be first for proper alignment */
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010041 int led_state;
Hans de Goedebc04b372020-07-04 15:27:41 +020042 struct mutex led_report_mutex;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010043 struct led_classdev led_mute;
44 struct led_classdev led_micmute;
Hans de Goedec87de332020-07-04 15:27:42 +020045 struct work_struct fn_lock_sync_work;
46 struct hid_device *hdev;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010047 int press_to_select;
48 int dragging;
49 int release_to_select;
50 int select_right;
51 int sensitivity;
52 int press_speed;
Jamie Lentin3cb5ff02015-08-11 22:40:52 +010053 u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
Jamie Lentinf3d4ff02014-07-23 23:30:48 +010054 bool fn_lock;
55};
56
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010057#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
58
Hans de Goedebc04b372020-07-04 15:27:41 +020059#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
Hans de Goede658d04e2021-04-04 10:04:25 +020068static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
69 enum led_brightness value)
Hans de Goedebc04b372020-07-04 15:27:41 +020070{
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);
Hans de Goede658d04e2021-04-04 10:04:25 +020081 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 }
Hans de Goedebc04b372020-07-04 15:27:41 +020089
90 mutex_unlock(&data->led_report_mutex);
Hans de Goede658d04e2021-04-04 10:04:25 +020091
92 return ret;
Hans de Goedebc04b372020-07-04 15:27:41 +020093}
94
Hans de Goedec87de332020-07-04 15:27:42 +020095static 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}
103
Benjamin Tissoires181a8b92015-05-01 16:22:45 -0400104static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
105 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
106 0x09, 0x01, /* Usage (Vendor Usage 0x01) */
107 0xa1, 0x01, /* Collection (Application) */
108 0x85, 0x04, /* Report ID (4) */
109 0x19, 0x00, /* Usage Minimum (0) */
110 0x2a, 0xff, 0xff, /* Usage Maximum (65535) */
111};
112
113static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
114 unsigned int *rsize)
115{
116 switch (hdev->product) {
117 case USB_DEVICE_ID_LENOVO_TPPRODOCK:
118 /* the fixups that need to be done:
119 * - get a reasonable usage max for the vendor collection
120 * 0x8801 from the report ID 4
121 */
122 if (*rsize >= 153 &&
123 memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
124 sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
125 rdesc[151] = 0x01;
126 rdesc[152] = 0x00;
127 }
128 break;
129 }
130 return rdesc;
131}
132
Jamie Lentin94723bf2014-07-23 23:30:45 +0100133static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100134 struct hid_input *hi, struct hid_field *field,
135 struct hid_usage *usage, unsigned long **bit, int *max)
136{
Benjamin Tissoires0c521832013-09-11 22:12:24 +0200137 if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100138 /* This sub-device contains trackpoint, mark it */
Benjamin Tissoires0c521832013-09-11 22:12:24 +0200139 hid_set_drvdata(hdev, (void *)1);
Hans de Goede61710322021-04-04 10:04:29 +0200140 map_key_clear(LENOVO_KEY_MICMUTE);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100141 return 1;
142 }
143 return 0;
144}
145
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100146static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
147 struct hid_input *hi, struct hid_field *field,
148 struct hid_usage *usage, unsigned long **bit, int *max)
149{
150 /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
151 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR ||
152 (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100153 switch (usage->hid & HID_USAGE) {
154 case 0x00f1: /* Fn-F4: Mic mute */
Hans de Goede61710322021-04-04 10:04:29 +0200155 map_key_clear(LENOVO_KEY_MICMUTE);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100156 return 1;
157 case 0x00f2: /* Fn-F5: Brightness down */
158 map_key_clear(KEY_BRIGHTNESSDOWN);
159 return 1;
160 case 0x00f3: /* Fn-F6: Brightness up */
161 map_key_clear(KEY_BRIGHTNESSUP);
162 return 1;
163 case 0x00f4: /* Fn-F7: External display (projector) */
164 map_key_clear(KEY_SWITCHVIDEOMODE);
165 return 1;
166 case 0x00f5: /* Fn-F8: Wireless */
167 map_key_clear(KEY_WLAN);
168 return 1;
169 case 0x00f6: /* Fn-F9: Control panel */
170 map_key_clear(KEY_CONFIG);
171 return 1;
172 case 0x00f8: /* Fn-F11: View open applications (3 boxes) */
173 map_key_clear(KEY_SCALE);
174 return 1;
Jamie Lentin5556eb12014-11-09 07:54:29 +0000175 case 0x00f9: /* Fn-F12: Open My computer (6 boxes) USB-only */
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100176 /* NB: This mapping is invented in raw_event below */
177 map_key_clear(KEY_FILE);
178 return 1;
Jamie Lentin5556eb12014-11-09 07:54:29 +0000179 case 0x00fa: /* Fn-Esc: Fn-lock toggle */
180 map_key_clear(KEY_FN_ESC);
181 return 1;
Jamie Lentin94eefa22014-12-16 21:26:46 +0000182 case 0x00fb: /* Middle mouse button (in native mode) */
183 map_key_clear(BTN_MIDDLE);
184 return 1;
185 }
186 }
187
188 /* Compatibility middle/wheel mappings should be ignored */
189 if (usage->hid == HID_GD_WHEEL)
190 return -1;
191 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON &&
192 (usage->hid & HID_USAGE) == 0x003)
193 return -1;
194 if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER &&
195 (usage->hid & HID_USAGE) == 0x238)
196 return -1;
197
198 /* Map wheel emulation reports: 0xffa1 = USB, 0xff10 = BT */
199 if ((usage->hid & HID_USAGE_PAGE) == 0xff100000 ||
200 (usage->hid & HID_USAGE_PAGE) == 0xffa10000) {
201 field->flags |= HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE;
202 field->logical_minimum = -127;
203 field->logical_maximum = 127;
204
205 switch (usage->hid & HID_USAGE) {
206 case 0x0000:
Jamie Lentin7f650682015-08-11 22:40:50 +0100207 hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
Jamie Lentin94eefa22014-12-16 21:26:46 +0000208 return 1;
209 case 0x0001:
Jamie Lentin7f650682015-08-11 22:40:50 +0100210 hid_map_usage(hi, usage, bit, max, EV_REL, REL_WHEEL);
Jamie Lentin94eefa22014-12-16 21:26:46 +0000211 return 1;
212 default:
213 return -1;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100214 }
215 }
216
217 return 0;
218}
219
pgzha230cd52018-04-12 19:36:47 +0200220static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
221 struct hid_input *hi, struct hid_field *field,
222 struct hid_usage *usage, unsigned long **bit, int *max)
223{
224 if (usage->hid == HID_GD_Z) {
225 hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
226 return 1;
227 }
228 return 0;
229}
230
Hans de Goedebc04b372020-07-04 15:27:41 +0200231static 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 */
Hans de Goede61710322021-04-04 10:04:29 +0200245 map_key_clear(LENOVO_KEY_MICMUTE);
Hans de Goedebc04b372020-07-04 15:27:41 +0200246 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
Hans de Goede49429422020-07-04 22:10:59 +0200259 /*
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
Hans de Goedebc04b372020-07-04 15:27:41 +0200266 return 0;
267}
268
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100269static int lenovo_input_mapping(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 switch (hdev->product) {
274 case USB_DEVICE_ID_LENOVO_TPKBD:
275 return lenovo_input_mapping_tpkbd(hdev, hi, field,
276 usage, bit, max);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100277 case USB_DEVICE_ID_LENOVO_CUSBKBD:
278 case USB_DEVICE_ID_LENOVO_CBTKBD:
279 return lenovo_input_mapping_cptkbd(hdev, hi, field,
280 usage, bit, max);
pgzha230cd52018-04-12 19:36:47 +0200281 case USB_DEVICE_ID_IBM_SCROLLPOINT_III:
282 case USB_DEVICE_ID_IBM_SCROLLPOINT_PRO:
283 case USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL:
284 case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL:
285 case USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO:
286 case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
287 return lenovo_input_mapping_scrollpoint(hdev, hi, field,
288 usage, bit, max);
Hans de Goedebc04b372020-07-04 15:27:41 +0200289 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
290 return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
291 usage, bit, max);
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100292 default:
293 return 0;
294 }
295}
296
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100297#undef map_key_clear
298
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100299/* Send a config command to the keyboard */
300static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
301 unsigned char byte2, unsigned char byte3)
302{
303 int ret;
Josh Boyerea36ae02016-03-28 09:22:14 -0400304 unsigned char *buf;
305
306 buf = kzalloc(3, GFP_KERNEL);
307 if (!buf)
308 return -ENOMEM;
309
310 buf[0] = 0x18;
311 buf[1] = byte2;
312 buf[2] = byte3;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100313
314 switch (hdev->product) {
315 case USB_DEVICE_ID_LENOVO_CUSBKBD:
Josh Boyerea36ae02016-03-28 09:22:14 -0400316 ret = hid_hw_raw_request(hdev, 0x13, buf, 3,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100317 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
318 break;
319 case USB_DEVICE_ID_LENOVO_CBTKBD:
Josh Boyerea36ae02016-03-28 09:22:14 -0400320 ret = hid_hw_output_report(hdev, buf, 3);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100321 break;
322 default:
323 ret = -EINVAL;
324 break;
325 }
326
Josh Boyerea36ae02016-03-28 09:22:14 -0400327 kfree(buf);
328
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100329 return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */
330}
331
332static void lenovo_features_set_cptkbd(struct hid_device *hdev)
333{
334 int ret;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200335 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100336
337 ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
338 if (ret)
339 hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
Jamie Lentindbfebb42015-08-11 22:40:51 +0100340
341 ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity);
342 if (ret)
343 hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100344}
345
Hans de Goedeef550c52020-07-04 15:27:40 +0200346static ssize_t attr_fn_lock_show(struct device *dev,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100347 struct device_attribute *attr,
348 char *buf)
349{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800350 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeef550c52020-07-04 15:27:40 +0200351 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100352
Hans de Goedeef550c52020-07-04 15:27:40 +0200353 return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100354}
355
Hans de Goedeef550c52020-07-04 15:27:40 +0200356static ssize_t attr_fn_lock_store(struct device *dev,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100357 struct device_attribute *attr,
358 const char *buf,
359 size_t count)
360{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800361 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeef550c52020-07-04 15:27:40 +0200362 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
Hans de Goede658d04e2021-04-04 10:04:25 +0200363 int value, ret;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100364
365 if (kstrtoint(buf, 10, &value))
366 return -EINVAL;
367 if (value < 0 || value > 1)
368 return -EINVAL;
369
Hans de Goedeef550c52020-07-04 15:27:40 +0200370 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;
Hans de Goedec87de332020-07-04 15:27:42 +0200377 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
Hans de Goede658d04e2021-04-04 10:04:25 +0200378 ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
379 if (ret)
380 return ret;
Hans de Goedec87de332020-07-04 15:27:42 +0200381 break;
Hans de Goedeef550c52020-07-04 15:27:40 +0200382 }
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100383
384 return count;
385}
386
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000387static ssize_t attr_sensitivity_show_cptkbd(struct device *dev,
388 struct device_attribute *attr,
389 char *buf)
390{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800391 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200392 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000393
394 return snprintf(buf, PAGE_SIZE, "%u\n",
395 cptkbd_data->sensitivity);
396}
397
398static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
399 struct device_attribute *attr,
400 const char *buf,
401 size_t count)
402{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800403 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200404 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000405 int value;
406
407 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
408 return -EINVAL;
409
410 cptkbd_data->sensitivity = value;
411 lenovo_features_set_cptkbd(hdev);
412
413 return count;
414}
415
416
Hans de Goedeef550c52020-07-04 15:27:40 +0200417static struct device_attribute dev_attr_fn_lock =
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100418 __ATTR(fn_lock, S_IWUSR | S_IRUGO,
Hans de Goedeef550c52020-07-04 15:27:40 +0200419 attr_fn_lock_show,
420 attr_fn_lock_store);
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100421
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000422static struct device_attribute dev_attr_sensitivity_cptkbd =
423 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
424 attr_sensitivity_show_cptkbd,
425 attr_sensitivity_store_cptkbd);
426
427
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100428static struct attribute *lenovo_attributes_cptkbd[] = {
Hans de Goedeef550c52020-07-04 15:27:40 +0200429 &dev_attr_fn_lock.attr,
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000430 &dev_attr_sensitivity_cptkbd.attr,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100431 NULL
432};
433
434static const struct attribute_group lenovo_attr_group_cptkbd = {
435 .attrs = lenovo_attributes_cptkbd,
436};
437
438static int lenovo_raw_event(struct hid_device *hdev,
439 struct hid_report *report, u8 *data, int size)
440{
441 /*
442 * Compact USB keyboard's Fn-F12 report holds down many other keys, and
443 * its own key is outside the usage page range. Remove extra
444 * keypresses and remap to inside usage page.
445 */
446 if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
447 && size == 3
448 && data[0] == 0x15
449 && data[1] == 0x94
450 && data[2] == 0x01)) {
Jamie Lentin5556eb12014-11-09 07:54:29 +0000451 data[1] = 0x00;
452 data[2] = 0x01;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100453 }
454
455 return 0;
456}
457
Hans de Goedec87de332020-07-04 15:27:42 +0200458static 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
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100476static int lenovo_event_cptkbd(struct hid_device *hdev,
477 struct hid_field *field, struct hid_usage *usage, __s32 value)
478{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200479 struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100480
481 /* "wheel" scroll events */
482 if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
483 usage->code == REL_HWHEEL)) {
484 /* Scroll events disable middle-click event */
485 cptkbd_data->middlebutton_state = 2;
486 return 0;
487 }
488
489 /* Middle click events */
490 if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
491 if (value == 1) {
492 cptkbd_data->middlebutton_state = 1;
493 } else if (value == 0) {
494 if (cptkbd_data->middlebutton_state == 1) {
495 /* No scrolling inbetween, send middle-click */
496 input_event(field->hidinput->input,
497 EV_KEY, BTN_MIDDLE, 1);
498 input_sync(field->hidinput->input);
499 input_event(field->hidinput->input,
500 EV_KEY, BTN_MIDDLE, 0);
501 input_sync(field->hidinput->input);
502 }
503 cptkbd_data->middlebutton_state = 0;
504 }
505 return 1;
506 }
507
508 return 0;
509}
510
511static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
512 struct hid_usage *usage, __s32 value)
513{
Hans de Goede34348a82021-04-04 10:04:26 +0200514 if (!hid_get_drvdata(hdev))
515 return 0;
516
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100517 switch (hdev->product) {
518 case USB_DEVICE_ID_LENOVO_CUSBKBD:
519 case USB_DEVICE_ID_LENOVO_CBTKBD:
520 return lenovo_event_cptkbd(hdev, field, usage, value);
Hans de Goedec87de332020-07-04 15:27:42 +0200521 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
522 return lenovo_event_tp10ubkbd(hdev, field, usage, value);
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100523 default:
524 return 0;
525 }
526}
527
Jamie Lentin94723bf2014-07-23 23:30:45 +0100528static int lenovo_features_set_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100529{
530 struct hid_report *report;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200531 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100532
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100533 report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
534
535 report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
536 report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08;
537 report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
538 report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40;
539 report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
540 report->field[2]->value[0] = data_pointer->sensitivity;
541 report->field[3]->value[0] = data_pointer->press_speed;
542
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100543 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100544 return 0;
545}
546
Jamie Lentin94723bf2014-07-23 23:30:45 +0100547static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100548 struct device_attribute *attr,
549 char *buf)
550{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800551 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200552 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100553
554 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
555}
556
Jamie Lentin94723bf2014-07-23 23:30:45 +0100557static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100558 struct device_attribute *attr,
559 const char *buf,
560 size_t count)
561{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800562 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200563 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100564 int value;
565
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100566 if (kstrtoint(buf, 10, &value))
567 return -EINVAL;
568 if (value < 0 || value > 1)
569 return -EINVAL;
570
571 data_pointer->press_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100572 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100573
574 return count;
575}
576
Jamie Lentin94723bf2014-07-23 23:30:45 +0100577static ssize_t attr_dragging_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100578 struct device_attribute *attr,
579 char *buf)
580{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800581 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200582 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100583
584 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
585}
586
Jamie Lentin94723bf2014-07-23 23:30:45 +0100587static ssize_t attr_dragging_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100588 struct device_attribute *attr,
589 const char *buf,
590 size_t count)
591{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800592 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200593 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100594 int value;
595
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100596 if (kstrtoint(buf, 10, &value))
597 return -EINVAL;
598 if (value < 0 || value > 1)
599 return -EINVAL;
600
601 data_pointer->dragging = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100602 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100603
604 return count;
605}
606
Jamie Lentin94723bf2014-07-23 23:30:45 +0100607static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100608 struct device_attribute *attr,
609 char *buf)
610{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800611 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200612 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100613
614 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
615}
616
Jamie Lentin94723bf2014-07-23 23:30:45 +0100617static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100618 struct device_attribute *attr,
619 const char *buf,
620 size_t count)
621{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800622 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200623 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100624 int value;
625
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100626 if (kstrtoint(buf, 10, &value))
627 return -EINVAL;
628 if (value < 0 || value > 1)
629 return -EINVAL;
630
631 data_pointer->release_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100632 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100633
634 return count;
635}
636
Jamie Lentin94723bf2014-07-23 23:30:45 +0100637static ssize_t attr_select_right_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100638 struct device_attribute *attr,
639 char *buf)
640{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800641 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200642 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100643
644 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
645}
646
Jamie Lentin94723bf2014-07-23 23:30:45 +0100647static ssize_t attr_select_right_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100648 struct device_attribute *attr,
649 const char *buf,
650 size_t count)
651{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800652 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200653 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100654 int value;
655
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100656 if (kstrtoint(buf, 10, &value))
657 return -EINVAL;
658 if (value < 0 || value > 1)
659 return -EINVAL;
660
661 data_pointer->select_right = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100662 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100663
664 return count;
665}
666
Jamie Lentin94723bf2014-07-23 23:30:45 +0100667static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100668 struct device_attribute *attr,
669 char *buf)
670{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800671 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200672 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100673
674 return snprintf(buf, PAGE_SIZE, "%u\n",
675 data_pointer->sensitivity);
676}
677
Jamie Lentin94723bf2014-07-23 23:30:45 +0100678static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100679 struct device_attribute *attr,
680 const char *buf,
681 size_t count)
682{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800683 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200684 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100685 int value;
686
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100687 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
688 return -EINVAL;
689
690 data_pointer->sensitivity = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100691 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100692
693 return count;
694}
695
Jamie Lentin94723bf2014-07-23 23:30:45 +0100696static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100697 struct device_attribute *attr,
698 char *buf)
699{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800700 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200701 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100702
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100703 return snprintf(buf, PAGE_SIZE, "%u\n",
704 data_pointer->press_speed);
705}
706
Jamie Lentin94723bf2014-07-23 23:30:45 +0100707static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100708 struct device_attribute *attr,
709 const char *buf,
710 size_t count)
711{
Geliang Tangee79a8f2015-12-27 17:25:21 +0800712 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200713 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100714 int value;
715
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100716 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
717 return -EINVAL;
718
719 data_pointer->press_speed = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100720 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100721
722 return count;
723}
724
Jamie Lentin94723bf2014-07-23 23:30:45 +0100725static struct device_attribute dev_attr_press_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100726 __ATTR(press_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100727 attr_press_to_select_show_tpkbd,
728 attr_press_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100729
Jamie Lentin94723bf2014-07-23 23:30:45 +0100730static struct device_attribute dev_attr_dragging_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100731 __ATTR(dragging, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100732 attr_dragging_show_tpkbd,
733 attr_dragging_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100734
Jamie Lentin94723bf2014-07-23 23:30:45 +0100735static struct device_attribute dev_attr_release_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100736 __ATTR(release_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100737 attr_release_to_select_show_tpkbd,
738 attr_release_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100739
Jamie Lentin94723bf2014-07-23 23:30:45 +0100740static struct device_attribute dev_attr_select_right_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100741 __ATTR(select_right, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100742 attr_select_right_show_tpkbd,
743 attr_select_right_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100744
Jamie Lentin94723bf2014-07-23 23:30:45 +0100745static struct device_attribute dev_attr_sensitivity_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100746 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100747 attr_sensitivity_show_tpkbd,
748 attr_sensitivity_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100749
Jamie Lentin94723bf2014-07-23 23:30:45 +0100750static struct device_attribute dev_attr_press_speed_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100751 __ATTR(press_speed, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100752 attr_press_speed_show_tpkbd,
753 attr_press_speed_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100754
Jamie Lentin94723bf2014-07-23 23:30:45 +0100755static struct attribute *lenovo_attributes_tpkbd[] = {
756 &dev_attr_press_to_select_tpkbd.attr,
757 &dev_attr_dragging_tpkbd.attr,
758 &dev_attr_release_to_select_tpkbd.attr,
759 &dev_attr_select_right_tpkbd.attr,
760 &dev_attr_sensitivity_tpkbd.attr,
761 &dev_attr_press_speed_tpkbd.attr,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100762 NULL
763};
764
Jamie Lentin94723bf2014-07-23 23:30:45 +0100765static const struct attribute_group lenovo_attr_group_tpkbd = {
766 .attrs = lenovo_attributes_tpkbd,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100767};
768
Hans de Goede484921f2020-07-04 15:27:39 +0200769static 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
Hans de Goedebbf62642021-04-04 10:04:24 +0200780static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100781 enum led_brightness value)
782{
Axel Lin832fbea2012-09-13 14:12:08 +0800783 struct device *dev = led_cdev->dev->parent;
Geliang Tangee79a8f2015-12-27 17:25:21 +0800784 struct hid_device *hdev = to_hid_device(dev);
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200785 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Hans de Goedebc04b372020-07-04 15:27:41 +0200786 u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100787 int led_nr = 0;
Hans de Goede658d04e2021-04-04 10:04:25 +0200788 int ret = 0;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100789
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100790 if (led_cdev == &data_pointer->led_micmute)
791 led_nr = 1;
792
793 if (value == LED_OFF)
794 data_pointer->led_state &= ~(1 << led_nr);
795 else
796 data_pointer->led_state |= 1 << led_nr;
797
Hans de Goede484921f2020-07-04 15:27:39 +0200798 switch (hdev->product) {
799 case USB_DEVICE_ID_LENOVO_TPKBD:
800 lenovo_led_set_tpkbd(hdev);
801 break;
Hans de Goedebc04b372020-07-04 15:27:41 +0200802 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
Hans de Goede658d04e2021-04-04 10:04:25 +0200803 ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
Hans de Goedebc04b372020-07-04 15:27:41 +0200804 break;
Hans de Goede484921f2020-07-04 15:27:39 +0200805 }
Hans de Goedebbf62642021-04-04 10:04:24 +0200806
Hans de Goede658d04e2021-04-04 10:04:25 +0200807 return ret;
Hans de Goede484921f2020-07-04 15:27:39 +0200808}
809
810static int lenovo_register_leds(struct hid_device *hdev)
811{
812 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
813 size_t name_sz = strlen(dev_name(&hdev->dev)) + 16;
814 char *name_mute, *name_micm;
815 int ret;
816
817 name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
818 name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
819 if (name_mute == NULL || name_micm == NULL) {
820 hid_err(hdev, "Could not allocate memory for led data\n");
821 return -ENOMEM;
822 }
823 snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev));
824 snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
825
826 data->led_mute.name = name_mute;
Hans de Goedee2da5ff2021-04-04 10:04:30 +0200827 data->led_mute.default_trigger = "audio-mute";
Hans de Goedebbf62642021-04-04 10:04:24 +0200828 data->led_mute.brightness_set_blocking = lenovo_led_brightness_set;
Hans de Goede8744eee2021-04-04 10:04:28 +0200829 data->led_mute.max_brightness = 1;
Hans de Goede658d04e2021-04-04 10:04:25 +0200830 data->led_mute.flags = LED_HW_PLUGGABLE;
Hans de Goede484921f2020-07-04 15:27:39 +0200831 data->led_mute.dev = &hdev->dev;
832 ret = led_classdev_register(&hdev->dev, &data->led_mute);
833 if (ret < 0)
834 return ret;
835
836 data->led_micmute.name = name_micm;
Hans de Goedee2da5ff2021-04-04 10:04:30 +0200837 data->led_micmute.default_trigger = "audio-micmute";
Hans de Goedebbf62642021-04-04 10:04:24 +0200838 data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set;
Hans de Goede8744eee2021-04-04 10:04:28 +0200839 data->led_micmute.max_brightness = 1;
Hans de Goede658d04e2021-04-04 10:04:25 +0200840 data->led_micmute.flags = LED_HW_PLUGGABLE;
Hans de Goede484921f2020-07-04 15:27:39 +0200841 data->led_micmute.dev = &hdev->dev;
842 ret = led_classdev_register(&hdev->dev, &data->led_micmute);
843 if (ret < 0) {
844 led_classdev_unregister(&data->led_mute);
845 return ret;
846 }
847
848 return 0;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100849}
850
Jamie Lentin94723bf2014-07-23 23:30:45 +0100851static int lenovo_probe_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100852{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200853 struct lenovo_drvdata *data_pointer;
Hans de Goede484921f2020-07-04 15:27:39 +0200854 int i, ret;
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200855
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100856 /*
857 * Only register extra settings against subdevice where input_mapping
858 * set drvdata to 1, i.e. the trackpoint.
859 */
860 if (!hid_get_drvdata(hdev))
861 return 0;
862
863 hid_set_drvdata(hdev, NULL);
864
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200865 /* Validate required reports. */
866 for (i = 0; i < 4; i++) {
867 if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
868 return -ENODEV;
869 }
870 if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
871 return -ENODEV;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100872
Jamie Lentine0a6aad2014-07-26 15:42:27 +0100873 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
874 if (ret)
875 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100876
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200877 data_pointer = devm_kzalloc(&hdev->dev,
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200878 sizeof(struct lenovo_drvdata),
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200879 GFP_KERNEL);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100880 if (data_pointer == NULL) {
881 hid_err(hdev, "Could not allocate memory for driver data\n");
Alexey Khoroshilovb7212602015-05-29 03:39:31 +0300882 ret = -ENOMEM;
883 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100884 }
885
886 // set same default values as windows driver
887 data_pointer->sensitivity = 0xa0;
888 data_pointer->press_speed = 0x38;
889
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100890 hid_set_drvdata(hdev, data_pointer);
891
Hans de Goede484921f2020-07-04 15:27:39 +0200892 ret = lenovo_register_leds(hdev);
893 if (ret)
Aditya Pakki6ae16df2018-12-24 15:39:14 -0600894 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100895
Jamie Lentin94723bf2014-07-23 23:30:45 +0100896 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100897
898 return 0;
Alexey Khoroshilovb7212602015-05-29 03:39:31 +0300899err:
900 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
901 return ret;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100902}
903
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100904static int lenovo_probe_cptkbd(struct hid_device *hdev)
905{
906 int ret;
Hans de Goedeb72cdfa2020-07-04 15:27:38 +0200907 struct lenovo_drvdata *cptkbd_data;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100908
909 /* All the custom action happens on the USBMOUSE device for USB */
910 if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
911 && hdev->type != HID_TYPE_USBMOUSE) {
912 hid_dbg(hdev, "Ignoring keyboard half of device\n");
913 return 0;
914 }
915
916 cptkbd_data = devm_kzalloc(&hdev->dev,
917 sizeof(*cptkbd_data),
918 GFP_KERNEL);
919 if (cptkbd_data == NULL) {
920 hid_err(hdev, "can't alloc keyboard descriptor\n");
921 return -ENOMEM;
922 }
923 hid_set_drvdata(hdev, cptkbd_data);
924
925 /*
926 * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
927 * regular keys
928 */
929 ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
930 if (ret)
931 hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
932
Jamie Lentin94eefa22014-12-16 21:26:46 +0000933 /* Switch middle button to native mode */
934 ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
935 if (ret)
936 hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
937
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000938 /* Set keyboard settings to known state */
Jamie Lentin3cb5ff02015-08-11 22:40:52 +0100939 cptkbd_data->middlebutton_state = 0;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100940 cptkbd_data->fn_lock = true;
Jamie Lentine3cb0ac2014-12-16 21:26:45 +0000941 cptkbd_data->sensitivity = 0x05;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +0100942 lenovo_features_set_cptkbd(hdev);
943
944 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd);
945 if (ret)
946 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
947
948 return 0;
949}
950
Hans de Goedec87de332020-07-04 15:27:42 +0200951static struct attribute *lenovo_attributes_tp10ubkbd[] = {
952 &dev_attr_fn_lock.attr,
953 NULL
954};
955
956static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
957 .attrs = lenovo_attributes_tp10ubkbd,
958};
959
Hans de Goedebc04b372020-07-04 15:27:41 +0200960static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
961{
962 struct lenovo_drvdata *data;
Hans de Goedec87de332020-07-04 15:27:42 +0200963 int ret;
Hans de Goedebc04b372020-07-04 15:27:41 +0200964
965 /* All the custom action happens on the USBMOUSE device for USB */
966 if (hdev->type != HID_TYPE_USBMOUSE)
967 return 0;
968
969 data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
970 if (!data)
971 return -ENOMEM;
972
973 mutex_init(&data->led_report_mutex);
Hans de Goedec87de332020-07-04 15:27:42 +0200974 INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock);
975 data->hdev = hdev;
Hans de Goedebc04b372020-07-04 15:27:41 +0200976
977 hid_set_drvdata(hdev, data);
978
Hans de Goedec87de332020-07-04 15:27:42 +0200979 /*
980 * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
981 * We cannot read the state, only set it, so we force it to on here
982 * (which should be a no-op) to make sure that our state matches the
983 * keyboard's FN-lock state. This is the same as what Windows does.
984 */
985 data->fn_lock = true;
986 lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
987
988 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
989 if (ret)
990 return ret;
991
992 ret = lenovo_register_leds(hdev);
993 if (ret)
994 goto err;
995
996 return 0;
997err:
998 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
999 return ret;
Hans de Goedebc04b372020-07-04 15:27:41 +02001000}
1001
Jamie Lentin94723bf2014-07-23 23:30:45 +01001002static int lenovo_probe(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001003 const struct hid_device_id *id)
1004{
1005 int ret;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001006
1007 ret = hid_parse(hdev);
1008 if (ret) {
1009 hid_err(hdev, "hid_parse failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001010 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001011 }
1012
1013 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1014 if (ret) {
1015 hid_err(hdev, "hid_hw_start failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001016 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001017 }
1018
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001019 switch (hdev->product) {
1020 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +01001021 ret = lenovo_probe_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001022 break;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001023 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1024 case USB_DEVICE_ID_LENOVO_CBTKBD:
1025 ret = lenovo_probe_cptkbd(hdev);
1026 break;
Hans de Goedebc04b372020-07-04 15:27:41 +02001027 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1028 ret = lenovo_probe_tp10ubkbd(hdev);
1029 break;
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001030 default:
1031 ret = 0;
1032 break;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001033 }
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001034 if (ret)
1035 goto err_hid;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001036
1037 return 0;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +02001038err_hid:
1039 hid_hw_stop(hdev);
1040err:
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001041 return ret;
1042}
1043
Jamie Lentin94723bf2014-07-23 23:30:45 +01001044static void lenovo_remove_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001045{
Hans de Goedeb72cdfa2020-07-04 15:27:38 +02001046 struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001047
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001048 /*
1049 * Only the trackpoint half of the keyboard has drvdata and stuff that
1050 * needs unregistering.
1051 */
1052 if (data_pointer == NULL)
1053 return;
1054
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001055 sysfs_remove_group(&hdev->dev.kobj,
Jamie Lentin94723bf2014-07-23 23:30:45 +01001056 &lenovo_attr_group_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001057
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001058 led_classdev_unregister(&data_pointer->led_micmute);
1059 led_classdev_unregister(&data_pointer->led_mute);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001060}
1061
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001062static void lenovo_remove_cptkbd(struct hid_device *hdev)
1063{
1064 sysfs_remove_group(&hdev->dev.kobj,
1065 &lenovo_attr_group_cptkbd);
1066}
1067
Hans de Goedebc04b372020-07-04 15:27:41 +02001068static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
1069{
1070 struct lenovo_drvdata *data = hid_get_drvdata(hdev);
1071
1072 if (data == NULL)
1073 return;
1074
1075 led_classdev_unregister(&data->led_micmute);
1076 led_classdev_unregister(&data->led_mute);
Hans de Goedec87de332020-07-04 15:27:42 +02001077
1078 sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
1079 cancel_work_sync(&data->fn_lock_sync_work);
Hans de Goedebc04b372020-07-04 15:27:41 +02001080}
1081
Jamie Lentin94723bf2014-07-23 23:30:45 +01001082static void lenovo_remove(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001083{
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001084 switch (hdev->product) {
1085 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +01001086 lenovo_remove_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001087 break;
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001088 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1089 case USB_DEVICE_ID_LENOVO_CBTKBD:
1090 lenovo_remove_cptkbd(hdev);
1091 break;
Hans de Goedebc04b372020-07-04 15:27:41 +02001092 case USB_DEVICE_ID_LENOVO_TP10UBKBD:
1093 lenovo_remove_tp10ubkbd(hdev);
1094 break;
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001095 }
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001096
1097 hid_hw_stop(hdev);
1098}
1099
Dmitry Torokhov91543012015-09-29 15:52:59 -07001100static int lenovo_input_configured(struct hid_device *hdev,
Andreas Fleigd92189e2015-04-23 10:25:58 +02001101 struct hid_input *hi)
1102{
1103 switch (hdev->product) {
1104 case USB_DEVICE_ID_LENOVO_TPKBD:
1105 case USB_DEVICE_ID_LENOVO_CUSBKBD:
1106 case USB_DEVICE_ID_LENOVO_CBTKBD:
1107 if (test_bit(EV_REL, hi->input->evbit)) {
1108 /* set only for trackpoint device */
1109 __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
1110 __set_bit(INPUT_PROP_POINTING_STICK,
1111 hi->input->propbit);
1112 }
1113 break;
1114 }
Dmitry Torokhov91543012015-09-29 15:52:59 -07001115
1116 return 0;
Andreas Fleigd92189e2015-04-23 10:25:58 +02001117}
1118
1119
Jamie Lentin94723bf2014-07-23 23:30:45 +01001120static const struct hid_device_id lenovo_devices[] = {
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001121 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001122 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
1123 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
Benjamin Tissoires181a8b92015-05-01 16:22:45 -04001124 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
pgzha230cd52018-04-12 19:36:47 +02001125 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_III) },
1126 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_PRO) },
1127 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_OPTICAL) },
1128 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
1129 { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
1130 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
Hans de Goedebc04b372020-07-04 15:27:41 +02001131 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001132 { }
1133};
1134
Jamie Lentin94723bf2014-07-23 23:30:45 +01001135MODULE_DEVICE_TABLE(hid, lenovo_devices);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001136
Jamie Lentin94723bf2014-07-23 23:30:45 +01001137static struct hid_driver lenovo_driver = {
1138 .name = "lenovo",
1139 .id_table = lenovo_devices,
Andreas Fleigd92189e2015-04-23 10:25:58 +02001140 .input_configured = lenovo_input_configured,
Jamie Lentin6a5b4142014-07-23 23:30:46 +01001141 .input_mapping = lenovo_input_mapping,
Jamie Lentin94723bf2014-07-23 23:30:45 +01001142 .probe = lenovo_probe,
1143 .remove = lenovo_remove,
Jamie Lentinf3d4ff02014-07-23 23:30:48 +01001144 .raw_event = lenovo_raw_event,
Jamie Lentin3cb5ff02015-08-11 22:40:52 +01001145 .event = lenovo_event,
Benjamin Tissoires181a8b92015-05-01 16:22:45 -04001146 .report_fixup = lenovo_report_fixup,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001147};
Jamie Lentin94723bf2014-07-23 23:30:45 +01001148module_hid_driver(lenovo_driver);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001149
1150MODULE_LICENSE("GPL");