.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * HID driver for Lenovo: |
---|
3 | 4 | * - ThinkPad USB Keyboard with TrackPoint (tpkbd) |
---|
.. | .. |
---|
20 | 21 | */ |
---|
21 | 22 | |
---|
22 | 23 | /* |
---|
23 | | - * This program is free software; you can redistribute it and/or modify it |
---|
24 | | - * under the terms of the GNU General Public License as published by the Free |
---|
25 | | - * Software Foundation; either version 2 of the License, or (at your option) |
---|
26 | | - * any later version. |
---|
27 | 24 | */ |
---|
28 | 25 | |
---|
29 | 26 | #include <linux/module.h> |
---|
.. | .. |
---|
32 | 29 | #include <linux/hid.h> |
---|
33 | 30 | #include <linux/input.h> |
---|
34 | 31 | #include <linux/leds.h> |
---|
| 32 | +#include <linux/workqueue.h> |
---|
35 | 33 | |
---|
36 | 34 | #include "hid-ids.h" |
---|
37 | 35 | |
---|
38 | | -struct lenovo_drvdata_tpkbd { |
---|
| 36 | +/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */ |
---|
| 37 | +#define LENOVO_KEY_MICMUTE KEY_F20 |
---|
| 38 | + |
---|
| 39 | +struct lenovo_drvdata { |
---|
| 40 | + u8 led_report[3]; /* Must be first for proper alignment */ |
---|
39 | 41 | int led_state; |
---|
| 42 | + struct mutex led_report_mutex; |
---|
40 | 43 | struct led_classdev led_mute; |
---|
41 | 44 | struct led_classdev led_micmute; |
---|
| 45 | + struct work_struct fn_lock_sync_work; |
---|
| 46 | + struct hid_device *hdev; |
---|
42 | 47 | int press_to_select; |
---|
43 | 48 | int dragging; |
---|
44 | 49 | int release_to_select; |
---|
45 | 50 | int select_right; |
---|
46 | 51 | int sensitivity; |
---|
47 | 52 | int press_speed; |
---|
48 | | -}; |
---|
49 | | - |
---|
50 | | -struct lenovo_drvdata_cptkbd { |
---|
51 | 53 | u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */ |
---|
52 | 54 | bool fn_lock; |
---|
53 | | - int sensitivity; |
---|
54 | 55 | }; |
---|
55 | 56 | |
---|
56 | 57 | #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) |
---|
| 58 | + |
---|
| 59 | +#define TP10UBKBD_LED_OUTPUT_REPORT 9 |
---|
| 60 | + |
---|
| 61 | +#define TP10UBKBD_FN_LOCK_LED 0x54 |
---|
| 62 | +#define TP10UBKBD_MUTE_LED 0x64 |
---|
| 63 | +#define TP10UBKBD_MICMUTE_LED 0x74 |
---|
| 64 | + |
---|
| 65 | +#define TP10UBKBD_LED_OFF 1 |
---|
| 66 | +#define TP10UBKBD_LED_ON 2 |
---|
| 67 | + |
---|
| 68 | +static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code, |
---|
| 69 | + enum led_brightness value) |
---|
| 70 | +{ |
---|
| 71 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
| 72 | + int ret; |
---|
| 73 | + |
---|
| 74 | + mutex_lock(&data->led_report_mutex); |
---|
| 75 | + |
---|
| 76 | + data->led_report[0] = TP10UBKBD_LED_OUTPUT_REPORT; |
---|
| 77 | + data->led_report[1] = led_code; |
---|
| 78 | + data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF; |
---|
| 79 | + ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3, |
---|
| 80 | + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); |
---|
| 81 | + if (ret != 3) { |
---|
| 82 | + if (ret != -ENODEV) |
---|
| 83 | + hid_err(hdev, "Set LED output report error: %d\n", ret); |
---|
| 84 | + |
---|
| 85 | + ret = ret < 0 ? ret : -EIO; |
---|
| 86 | + } else { |
---|
| 87 | + ret = 0; |
---|
| 88 | + } |
---|
| 89 | + |
---|
| 90 | + mutex_unlock(&data->led_report_mutex); |
---|
| 91 | + |
---|
| 92 | + return ret; |
---|
| 93 | +} |
---|
| 94 | + |
---|
| 95 | +static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work) |
---|
| 96 | +{ |
---|
| 97 | + struct lenovo_drvdata *data = |
---|
| 98 | + container_of(work, struct lenovo_drvdata, fn_lock_sync_work); |
---|
| 99 | + |
---|
| 100 | + lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED, |
---|
| 101 | + data->fn_lock); |
---|
| 102 | +} |
---|
57 | 103 | |
---|
58 | 104 | static const __u8 lenovo_pro_dock_need_fixup_collection[] = { |
---|
59 | 105 | 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */ |
---|
.. | .. |
---|
91 | 137 | if (usage->hid == (HID_UP_BUTTON | 0x0010)) { |
---|
92 | 138 | /* This sub-device contains trackpoint, mark it */ |
---|
93 | 139 | hid_set_drvdata(hdev, (void *)1); |
---|
94 | | - map_key_clear(KEY_MICMUTE); |
---|
| 140 | + map_key_clear(LENOVO_KEY_MICMUTE); |
---|
95 | 141 | return 1; |
---|
96 | 142 | } |
---|
97 | 143 | return 0; |
---|
.. | .. |
---|
106 | 152 | (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) { |
---|
107 | 153 | switch (usage->hid & HID_USAGE) { |
---|
108 | 154 | case 0x00f1: /* Fn-F4: Mic mute */ |
---|
109 | | - map_key_clear(KEY_MICMUTE); |
---|
| 155 | + map_key_clear(LENOVO_KEY_MICMUTE); |
---|
110 | 156 | return 1; |
---|
111 | 157 | case 0x00f2: /* Fn-F5: Brightness down */ |
---|
112 | 158 | map_key_clear(KEY_BRIGHTNESSDOWN); |
---|
.. | .. |
---|
182 | 228 | return 0; |
---|
183 | 229 | } |
---|
184 | 230 | |
---|
| 231 | +static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev, |
---|
| 232 | + struct hid_input *hi, struct hid_field *field, |
---|
| 233 | + struct hid_usage *usage, unsigned long **bit, int *max) |
---|
| 234 | +{ |
---|
| 235 | + /* |
---|
| 236 | + * The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for |
---|
| 237 | + * a bunch of keys which have no standard consumer page code. |
---|
| 238 | + */ |
---|
| 239 | + if (usage->hid == 0x000c0001) { |
---|
| 240 | + switch (usage->usage_index) { |
---|
| 241 | + case 8: /* Fn-Esc: Fn-lock toggle */ |
---|
| 242 | + map_key_clear(KEY_FN_ESC); |
---|
| 243 | + return 1; |
---|
| 244 | + case 9: /* Fn-F4: Mic mute */ |
---|
| 245 | + map_key_clear(LENOVO_KEY_MICMUTE); |
---|
| 246 | + return 1; |
---|
| 247 | + case 10: /* Fn-F7: Control panel */ |
---|
| 248 | + map_key_clear(KEY_CONFIG); |
---|
| 249 | + return 1; |
---|
| 250 | + case 11: /* Fn-F8: Search (magnifier glass) */ |
---|
| 251 | + map_key_clear(KEY_SEARCH); |
---|
| 252 | + return 1; |
---|
| 253 | + case 12: /* Fn-F10: Open My computer (6 boxes) */ |
---|
| 254 | + map_key_clear(KEY_FILE); |
---|
| 255 | + return 1; |
---|
| 256 | + } |
---|
| 257 | + } |
---|
| 258 | + |
---|
| 259 | + /* |
---|
| 260 | + * The Ultrabook Keyboard sends a spurious F23 key-press when resuming |
---|
| 261 | + * from suspend and it does not actually have a F23 key, ignore it. |
---|
| 262 | + */ |
---|
| 263 | + if (usage->hid == 0x00070072) |
---|
| 264 | + return -1; |
---|
| 265 | + |
---|
| 266 | + return 0; |
---|
| 267 | +} |
---|
| 268 | + |
---|
185 | 269 | static int lenovo_input_mapping(struct hid_device *hdev, |
---|
186 | 270 | struct hid_input *hi, struct hid_field *field, |
---|
187 | 271 | struct hid_usage *usage, unsigned long **bit, int *max) |
---|
.. | .. |
---|
202 | 286 | case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL: |
---|
203 | 287 | return lenovo_input_mapping_scrollpoint(hdev, hi, field, |
---|
204 | 288 | usage, bit, max); |
---|
| 289 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 290 | + return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field, |
---|
| 291 | + usage, bit, max); |
---|
205 | 292 | default: |
---|
206 | 293 | return 0; |
---|
207 | 294 | } |
---|
.. | .. |
---|
245 | 332 | static void lenovo_features_set_cptkbd(struct hid_device *hdev) |
---|
246 | 333 | { |
---|
247 | 334 | int ret; |
---|
248 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
| 335 | + struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); |
---|
249 | 336 | |
---|
250 | 337 | ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); |
---|
251 | 338 | if (ret) |
---|
.. | .. |
---|
256 | 343 | hid_err(hdev, "Sensitivity setting failed: %d\n", ret); |
---|
257 | 344 | } |
---|
258 | 345 | |
---|
259 | | -static ssize_t attr_fn_lock_show_cptkbd(struct device *dev, |
---|
| 346 | +static ssize_t attr_fn_lock_show(struct device *dev, |
---|
260 | 347 | struct device_attribute *attr, |
---|
261 | 348 | char *buf) |
---|
262 | 349 | { |
---|
263 | 350 | struct hid_device *hdev = to_hid_device(dev); |
---|
264 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
| 351 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
265 | 352 | |
---|
266 | | - return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock); |
---|
| 353 | + return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock); |
---|
267 | 354 | } |
---|
268 | 355 | |
---|
269 | | -static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, |
---|
| 356 | +static ssize_t attr_fn_lock_store(struct device *dev, |
---|
270 | 357 | struct device_attribute *attr, |
---|
271 | 358 | const char *buf, |
---|
272 | 359 | size_t count) |
---|
273 | 360 | { |
---|
274 | 361 | struct hid_device *hdev = to_hid_device(dev); |
---|
275 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
276 | | - int value; |
---|
| 362 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
| 363 | + int value, ret; |
---|
277 | 364 | |
---|
278 | 365 | if (kstrtoint(buf, 10, &value)) |
---|
279 | 366 | return -EINVAL; |
---|
280 | 367 | if (value < 0 || value > 1) |
---|
281 | 368 | return -EINVAL; |
---|
282 | 369 | |
---|
283 | | - cptkbd_data->fn_lock = !!value; |
---|
284 | | - lenovo_features_set_cptkbd(hdev); |
---|
| 370 | + data->fn_lock = !!value; |
---|
| 371 | + |
---|
| 372 | + switch (hdev->product) { |
---|
| 373 | + case USB_DEVICE_ID_LENOVO_CUSBKBD: |
---|
| 374 | + case USB_DEVICE_ID_LENOVO_CBTKBD: |
---|
| 375 | + lenovo_features_set_cptkbd(hdev); |
---|
| 376 | + break; |
---|
| 377 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 378 | + ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); |
---|
| 379 | + if (ret) |
---|
| 380 | + return ret; |
---|
| 381 | + break; |
---|
| 382 | + } |
---|
285 | 383 | |
---|
286 | 384 | return count; |
---|
287 | 385 | } |
---|
.. | .. |
---|
291 | 389 | char *buf) |
---|
292 | 390 | { |
---|
293 | 391 | struct hid_device *hdev = to_hid_device(dev); |
---|
294 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
| 392 | + struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); |
---|
295 | 393 | |
---|
296 | 394 | return snprintf(buf, PAGE_SIZE, "%u\n", |
---|
297 | 395 | cptkbd_data->sensitivity); |
---|
.. | .. |
---|
303 | 401 | size_t count) |
---|
304 | 402 | { |
---|
305 | 403 | struct hid_device *hdev = to_hid_device(dev); |
---|
306 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
| 404 | + struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); |
---|
307 | 405 | int value; |
---|
308 | 406 | |
---|
309 | 407 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) |
---|
.. | .. |
---|
316 | 414 | } |
---|
317 | 415 | |
---|
318 | 416 | |
---|
319 | | -static struct device_attribute dev_attr_fn_lock_cptkbd = |
---|
| 417 | +static struct device_attribute dev_attr_fn_lock = |
---|
320 | 418 | __ATTR(fn_lock, S_IWUSR | S_IRUGO, |
---|
321 | | - attr_fn_lock_show_cptkbd, |
---|
322 | | - attr_fn_lock_store_cptkbd); |
---|
| 419 | + attr_fn_lock_show, |
---|
| 420 | + attr_fn_lock_store); |
---|
323 | 421 | |
---|
324 | 422 | static struct device_attribute dev_attr_sensitivity_cptkbd = |
---|
325 | 423 | __ATTR(sensitivity, S_IWUSR | S_IRUGO, |
---|
.. | .. |
---|
328 | 426 | |
---|
329 | 427 | |
---|
330 | 428 | static struct attribute *lenovo_attributes_cptkbd[] = { |
---|
331 | | - &dev_attr_fn_lock_cptkbd.attr, |
---|
| 429 | + &dev_attr_fn_lock.attr, |
---|
332 | 430 | &dev_attr_sensitivity_cptkbd.attr, |
---|
333 | 431 | NULL |
---|
334 | 432 | }; |
---|
.. | .. |
---|
357 | 455 | return 0; |
---|
358 | 456 | } |
---|
359 | 457 | |
---|
| 458 | +static int lenovo_event_tp10ubkbd(struct hid_device *hdev, |
---|
| 459 | + struct hid_field *field, struct hid_usage *usage, __s32 value) |
---|
| 460 | +{ |
---|
| 461 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
| 462 | + |
---|
| 463 | + if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) { |
---|
| 464 | + /* |
---|
| 465 | + * The user has toggled the Fn-lock state. Toggle our own |
---|
| 466 | + * cached value of it and sync our value to the keyboard to |
---|
| 467 | + * ensure things are in sync (the sycning should be a no-op). |
---|
| 468 | + */ |
---|
| 469 | + data->fn_lock = !data->fn_lock; |
---|
| 470 | + schedule_work(&data->fn_lock_sync_work); |
---|
| 471 | + } |
---|
| 472 | + |
---|
| 473 | + return 0; |
---|
| 474 | +} |
---|
| 475 | + |
---|
360 | 476 | static int lenovo_event_cptkbd(struct hid_device *hdev, |
---|
361 | 477 | struct hid_field *field, struct hid_usage *usage, __s32 value) |
---|
362 | 478 | { |
---|
363 | | - struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); |
---|
| 479 | + struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); |
---|
364 | 480 | |
---|
365 | 481 | /* "wheel" scroll events */ |
---|
366 | 482 | if (usage->type == EV_REL && (usage->code == REL_WHEEL || |
---|
.. | .. |
---|
395 | 511 | static int lenovo_event(struct hid_device *hdev, struct hid_field *field, |
---|
396 | 512 | struct hid_usage *usage, __s32 value) |
---|
397 | 513 | { |
---|
| 514 | + if (!hid_get_drvdata(hdev)) |
---|
| 515 | + return 0; |
---|
| 516 | + |
---|
398 | 517 | switch (hdev->product) { |
---|
399 | 518 | case USB_DEVICE_ID_LENOVO_CUSBKBD: |
---|
400 | 519 | case USB_DEVICE_ID_LENOVO_CBTKBD: |
---|
401 | 520 | return lenovo_event_cptkbd(hdev, field, usage, value); |
---|
| 521 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 522 | + return lenovo_event_tp10ubkbd(hdev, field, usage, value); |
---|
402 | 523 | default: |
---|
403 | 524 | return 0; |
---|
404 | 525 | } |
---|
.. | .. |
---|
407 | 528 | static int lenovo_features_set_tpkbd(struct hid_device *hdev) |
---|
408 | 529 | { |
---|
409 | 530 | struct hid_report *report; |
---|
410 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 531 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
411 | 532 | |
---|
412 | 533 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; |
---|
413 | 534 | |
---|
.. | .. |
---|
428 | 549 | char *buf) |
---|
429 | 550 | { |
---|
430 | 551 | struct hid_device *hdev = to_hid_device(dev); |
---|
431 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 552 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
432 | 553 | |
---|
433 | 554 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); |
---|
434 | 555 | } |
---|
.. | .. |
---|
439 | 560 | size_t count) |
---|
440 | 561 | { |
---|
441 | 562 | struct hid_device *hdev = to_hid_device(dev); |
---|
442 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 563 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
443 | 564 | int value; |
---|
444 | 565 | |
---|
445 | 566 | if (kstrtoint(buf, 10, &value)) |
---|
.. | .. |
---|
458 | 579 | char *buf) |
---|
459 | 580 | { |
---|
460 | 581 | struct hid_device *hdev = to_hid_device(dev); |
---|
461 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 582 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
462 | 583 | |
---|
463 | 584 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); |
---|
464 | 585 | } |
---|
.. | .. |
---|
469 | 590 | size_t count) |
---|
470 | 591 | { |
---|
471 | 592 | struct hid_device *hdev = to_hid_device(dev); |
---|
472 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 593 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
473 | 594 | int value; |
---|
474 | 595 | |
---|
475 | 596 | if (kstrtoint(buf, 10, &value)) |
---|
.. | .. |
---|
488 | 609 | char *buf) |
---|
489 | 610 | { |
---|
490 | 611 | struct hid_device *hdev = to_hid_device(dev); |
---|
491 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 612 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
492 | 613 | |
---|
493 | 614 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); |
---|
494 | 615 | } |
---|
.. | .. |
---|
499 | 620 | size_t count) |
---|
500 | 621 | { |
---|
501 | 622 | struct hid_device *hdev = to_hid_device(dev); |
---|
502 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 623 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
503 | 624 | int value; |
---|
504 | 625 | |
---|
505 | 626 | if (kstrtoint(buf, 10, &value)) |
---|
.. | .. |
---|
518 | 639 | char *buf) |
---|
519 | 640 | { |
---|
520 | 641 | struct hid_device *hdev = to_hid_device(dev); |
---|
521 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 642 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
522 | 643 | |
---|
523 | 644 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); |
---|
524 | 645 | } |
---|
.. | .. |
---|
529 | 650 | size_t count) |
---|
530 | 651 | { |
---|
531 | 652 | struct hid_device *hdev = to_hid_device(dev); |
---|
532 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 653 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
533 | 654 | int value; |
---|
534 | 655 | |
---|
535 | 656 | if (kstrtoint(buf, 10, &value)) |
---|
.. | .. |
---|
548 | 669 | char *buf) |
---|
549 | 670 | { |
---|
550 | 671 | struct hid_device *hdev = to_hid_device(dev); |
---|
551 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 672 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
552 | 673 | |
---|
553 | 674 | return snprintf(buf, PAGE_SIZE, "%u\n", |
---|
554 | 675 | data_pointer->sensitivity); |
---|
.. | .. |
---|
560 | 681 | size_t count) |
---|
561 | 682 | { |
---|
562 | 683 | struct hid_device *hdev = to_hid_device(dev); |
---|
563 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 684 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
564 | 685 | int value; |
---|
565 | 686 | |
---|
566 | 687 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) |
---|
.. | .. |
---|
577 | 698 | char *buf) |
---|
578 | 699 | { |
---|
579 | 700 | struct hid_device *hdev = to_hid_device(dev); |
---|
580 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 701 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
581 | 702 | |
---|
582 | 703 | return snprintf(buf, PAGE_SIZE, "%u\n", |
---|
583 | 704 | data_pointer->press_speed); |
---|
.. | .. |
---|
589 | 710 | size_t count) |
---|
590 | 711 | { |
---|
591 | 712 | struct hid_device *hdev = to_hid_device(dev); |
---|
592 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 713 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
593 | 714 | int value; |
---|
594 | 715 | |
---|
595 | 716 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) |
---|
.. | .. |
---|
645 | 766 | .attrs = lenovo_attributes_tpkbd, |
---|
646 | 767 | }; |
---|
647 | 768 | |
---|
648 | | -static enum led_brightness lenovo_led_brightness_get_tpkbd( |
---|
| 769 | +static void lenovo_led_set_tpkbd(struct hid_device *hdev) |
---|
| 770 | +{ |
---|
| 771 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
| 772 | + struct hid_report *report; |
---|
| 773 | + |
---|
| 774 | + report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; |
---|
| 775 | + report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; |
---|
| 776 | + report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; |
---|
| 777 | + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); |
---|
| 778 | +} |
---|
| 779 | + |
---|
| 780 | +static enum led_brightness lenovo_led_brightness_get( |
---|
649 | 781 | struct led_classdev *led_cdev) |
---|
650 | 782 | { |
---|
651 | 783 | struct device *dev = led_cdev->dev->parent; |
---|
652 | 784 | struct hid_device *hdev = to_hid_device(dev); |
---|
653 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 785 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
654 | 786 | int led_nr = 0; |
---|
655 | 787 | |
---|
656 | 788 | if (led_cdev == &data_pointer->led_micmute) |
---|
.. | .. |
---|
661 | 793 | : LED_OFF; |
---|
662 | 794 | } |
---|
663 | 795 | |
---|
664 | | -static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev, |
---|
| 796 | +static int lenovo_led_brightness_set(struct led_classdev *led_cdev, |
---|
665 | 797 | enum led_brightness value) |
---|
666 | 798 | { |
---|
667 | 799 | struct device *dev = led_cdev->dev->parent; |
---|
668 | 800 | struct hid_device *hdev = to_hid_device(dev); |
---|
669 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
670 | | - struct hid_report *report; |
---|
| 801 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
| 802 | + u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED }; |
---|
671 | 803 | int led_nr = 0; |
---|
| 804 | + int ret = 0; |
---|
672 | 805 | |
---|
673 | 806 | if (led_cdev == &data_pointer->led_micmute) |
---|
674 | 807 | led_nr = 1; |
---|
.. | .. |
---|
678 | 811 | else |
---|
679 | 812 | data_pointer->led_state |= 1 << led_nr; |
---|
680 | 813 | |
---|
681 | | - report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; |
---|
682 | | - report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; |
---|
683 | | - report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; |
---|
684 | | - hid_hw_request(hdev, report, HID_REQ_SET_REPORT); |
---|
| 814 | + switch (hdev->product) { |
---|
| 815 | + case USB_DEVICE_ID_LENOVO_TPKBD: |
---|
| 816 | + lenovo_led_set_tpkbd(hdev); |
---|
| 817 | + break; |
---|
| 818 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 819 | + ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value); |
---|
| 820 | + break; |
---|
| 821 | + } |
---|
| 822 | + |
---|
| 823 | + return ret; |
---|
| 824 | +} |
---|
| 825 | + |
---|
| 826 | +static int lenovo_register_leds(struct hid_device *hdev) |
---|
| 827 | +{ |
---|
| 828 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
| 829 | + size_t name_sz = strlen(dev_name(&hdev->dev)) + 16; |
---|
| 830 | + char *name_mute, *name_micm; |
---|
| 831 | + int ret; |
---|
| 832 | + |
---|
| 833 | + name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); |
---|
| 834 | + name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); |
---|
| 835 | + if (name_mute == NULL || name_micm == NULL) { |
---|
| 836 | + hid_err(hdev, "Could not allocate memory for led data\n"); |
---|
| 837 | + return -ENOMEM; |
---|
| 838 | + } |
---|
| 839 | + snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev)); |
---|
| 840 | + snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev)); |
---|
| 841 | + |
---|
| 842 | + data->led_mute.name = name_mute; |
---|
| 843 | + data->led_mute.brightness_get = lenovo_led_brightness_get; |
---|
| 844 | + data->led_mute.brightness_set_blocking = lenovo_led_brightness_set; |
---|
| 845 | + data->led_mute.flags = LED_HW_PLUGGABLE; |
---|
| 846 | + data->led_mute.dev = &hdev->dev; |
---|
| 847 | + ret = led_classdev_register(&hdev->dev, &data->led_mute); |
---|
| 848 | + if (ret < 0) |
---|
| 849 | + return ret; |
---|
| 850 | + |
---|
| 851 | + data->led_micmute.name = name_micm; |
---|
| 852 | + data->led_micmute.brightness_get = lenovo_led_brightness_get; |
---|
| 853 | + data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set; |
---|
| 854 | + data->led_micmute.flags = LED_HW_PLUGGABLE; |
---|
| 855 | + data->led_micmute.dev = &hdev->dev; |
---|
| 856 | + ret = led_classdev_register(&hdev->dev, &data->led_micmute); |
---|
| 857 | + if (ret < 0) { |
---|
| 858 | + led_classdev_unregister(&data->led_mute); |
---|
| 859 | + return ret; |
---|
| 860 | + } |
---|
| 861 | + |
---|
| 862 | + return 0; |
---|
685 | 863 | } |
---|
686 | 864 | |
---|
687 | 865 | static int lenovo_probe_tpkbd(struct hid_device *hdev) |
---|
688 | 866 | { |
---|
689 | | - struct device *dev = &hdev->dev; |
---|
690 | | - struct lenovo_drvdata_tpkbd *data_pointer; |
---|
691 | | - size_t name_sz = strlen(dev_name(dev)) + 16; |
---|
692 | | - char *name_mute, *name_micmute; |
---|
693 | | - int i; |
---|
694 | | - int ret; |
---|
| 867 | + struct lenovo_drvdata *data_pointer; |
---|
| 868 | + int i, ret; |
---|
695 | 869 | |
---|
696 | 870 | /* |
---|
697 | 871 | * Only register extra settings against subdevice where input_mapping |
---|
.. | .. |
---|
715 | 889 | hid_warn(hdev, "Could not create sysfs group: %d\n", ret); |
---|
716 | 890 | |
---|
717 | 891 | data_pointer = devm_kzalloc(&hdev->dev, |
---|
718 | | - sizeof(struct lenovo_drvdata_tpkbd), |
---|
| 892 | + sizeof(struct lenovo_drvdata), |
---|
719 | 893 | GFP_KERNEL); |
---|
720 | 894 | if (data_pointer == NULL) { |
---|
721 | 895 | hid_err(hdev, "Could not allocate memory for driver data\n"); |
---|
.. | .. |
---|
727 | 901 | data_pointer->sensitivity = 0xa0; |
---|
728 | 902 | data_pointer->press_speed = 0x38; |
---|
729 | 903 | |
---|
730 | | - name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); |
---|
731 | | - name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); |
---|
732 | | - if (name_mute == NULL || name_micmute == NULL) { |
---|
733 | | - hid_err(hdev, "Could not allocate memory for led data\n"); |
---|
734 | | - ret = -ENOMEM; |
---|
735 | | - goto err; |
---|
736 | | - } |
---|
737 | | - snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); |
---|
738 | | - snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); |
---|
739 | | - |
---|
740 | 904 | hid_set_drvdata(hdev, data_pointer); |
---|
741 | 905 | |
---|
742 | | - data_pointer->led_mute.name = name_mute; |
---|
743 | | - data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd; |
---|
744 | | - data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd; |
---|
745 | | - data_pointer->led_mute.dev = dev; |
---|
746 | | - ret = led_classdev_register(dev, &data_pointer->led_mute); |
---|
747 | | - if (ret < 0) |
---|
| 906 | + ret = lenovo_register_leds(hdev); |
---|
| 907 | + if (ret) |
---|
748 | 908 | goto err; |
---|
749 | | - |
---|
750 | | - data_pointer->led_micmute.name = name_micmute; |
---|
751 | | - data_pointer->led_micmute.brightness_get = |
---|
752 | | - lenovo_led_brightness_get_tpkbd; |
---|
753 | | - data_pointer->led_micmute.brightness_set = |
---|
754 | | - lenovo_led_brightness_set_tpkbd; |
---|
755 | | - data_pointer->led_micmute.dev = dev; |
---|
756 | | - ret = led_classdev_register(dev, &data_pointer->led_micmute); |
---|
757 | | - if (ret < 0) { |
---|
758 | | - led_classdev_unregister(&data_pointer->led_mute); |
---|
759 | | - goto err; |
---|
760 | | - } |
---|
761 | 909 | |
---|
762 | 910 | lenovo_features_set_tpkbd(hdev); |
---|
763 | 911 | |
---|
.. | .. |
---|
770 | 918 | static int lenovo_probe_cptkbd(struct hid_device *hdev) |
---|
771 | 919 | { |
---|
772 | 920 | int ret; |
---|
773 | | - struct lenovo_drvdata_cptkbd *cptkbd_data; |
---|
| 921 | + struct lenovo_drvdata *cptkbd_data; |
---|
774 | 922 | |
---|
775 | 923 | /* All the custom action happens on the USBMOUSE device for USB */ |
---|
776 | 924 | if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD |
---|
.. | .. |
---|
814 | 962 | return 0; |
---|
815 | 963 | } |
---|
816 | 964 | |
---|
| 965 | +static struct attribute *lenovo_attributes_tp10ubkbd[] = { |
---|
| 966 | + &dev_attr_fn_lock.attr, |
---|
| 967 | + NULL |
---|
| 968 | +}; |
---|
| 969 | + |
---|
| 970 | +static const struct attribute_group lenovo_attr_group_tp10ubkbd = { |
---|
| 971 | + .attrs = lenovo_attributes_tp10ubkbd, |
---|
| 972 | +}; |
---|
| 973 | + |
---|
| 974 | +static int lenovo_probe_tp10ubkbd(struct hid_device *hdev) |
---|
| 975 | +{ |
---|
| 976 | + struct lenovo_drvdata *data; |
---|
| 977 | + int ret; |
---|
| 978 | + |
---|
| 979 | + /* All the custom action happens on the USBMOUSE device for USB */ |
---|
| 980 | + if (hdev->type != HID_TYPE_USBMOUSE) |
---|
| 981 | + return 0; |
---|
| 982 | + |
---|
| 983 | + data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL); |
---|
| 984 | + if (!data) |
---|
| 985 | + return -ENOMEM; |
---|
| 986 | + |
---|
| 987 | + mutex_init(&data->led_report_mutex); |
---|
| 988 | + INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock); |
---|
| 989 | + data->hdev = hdev; |
---|
| 990 | + |
---|
| 991 | + hid_set_drvdata(hdev, data); |
---|
| 992 | + |
---|
| 993 | + /* |
---|
| 994 | + * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on. |
---|
| 995 | + * We cannot read the state, only set it, so we force it to on here |
---|
| 996 | + * (which should be a no-op) to make sure that our state matches the |
---|
| 997 | + * keyboard's FN-lock state. This is the same as what Windows does. |
---|
| 998 | + */ |
---|
| 999 | + data->fn_lock = true; |
---|
| 1000 | + lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock); |
---|
| 1001 | + |
---|
| 1002 | + ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); |
---|
| 1003 | + if (ret) |
---|
| 1004 | + return ret; |
---|
| 1005 | + |
---|
| 1006 | + ret = lenovo_register_leds(hdev); |
---|
| 1007 | + if (ret) |
---|
| 1008 | + goto err; |
---|
| 1009 | + |
---|
| 1010 | + return 0; |
---|
| 1011 | +err: |
---|
| 1012 | + sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); |
---|
| 1013 | + return ret; |
---|
| 1014 | +} |
---|
| 1015 | + |
---|
817 | 1016 | static int lenovo_probe(struct hid_device *hdev, |
---|
818 | 1017 | const struct hid_device_id *id) |
---|
819 | 1018 | { |
---|
.. | .. |
---|
839 | 1038 | case USB_DEVICE_ID_LENOVO_CBTKBD: |
---|
840 | 1039 | ret = lenovo_probe_cptkbd(hdev); |
---|
841 | 1040 | break; |
---|
| 1041 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 1042 | + ret = lenovo_probe_tp10ubkbd(hdev); |
---|
| 1043 | + break; |
---|
842 | 1044 | default: |
---|
843 | 1045 | ret = 0; |
---|
844 | 1046 | break; |
---|
.. | .. |
---|
855 | 1057 | |
---|
856 | 1058 | static void lenovo_remove_tpkbd(struct hid_device *hdev) |
---|
857 | 1059 | { |
---|
858 | | - struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); |
---|
| 1060 | + struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); |
---|
859 | 1061 | |
---|
860 | 1062 | /* |
---|
861 | 1063 | * Only the trackpoint half of the keyboard has drvdata and stuff that |
---|
.. | .. |
---|
869 | 1071 | |
---|
870 | 1072 | led_classdev_unregister(&data_pointer->led_micmute); |
---|
871 | 1073 | led_classdev_unregister(&data_pointer->led_mute); |
---|
872 | | - |
---|
873 | | - hid_set_drvdata(hdev, NULL); |
---|
874 | 1074 | } |
---|
875 | 1075 | |
---|
876 | 1076 | static void lenovo_remove_cptkbd(struct hid_device *hdev) |
---|
877 | 1077 | { |
---|
878 | 1078 | sysfs_remove_group(&hdev->dev.kobj, |
---|
879 | 1079 | &lenovo_attr_group_cptkbd); |
---|
| 1080 | +} |
---|
| 1081 | + |
---|
| 1082 | +static void lenovo_remove_tp10ubkbd(struct hid_device *hdev) |
---|
| 1083 | +{ |
---|
| 1084 | + struct lenovo_drvdata *data = hid_get_drvdata(hdev); |
---|
| 1085 | + |
---|
| 1086 | + if (data == NULL) |
---|
| 1087 | + return; |
---|
| 1088 | + |
---|
| 1089 | + led_classdev_unregister(&data->led_micmute); |
---|
| 1090 | + led_classdev_unregister(&data->led_mute); |
---|
| 1091 | + |
---|
| 1092 | + sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); |
---|
| 1093 | + cancel_work_sync(&data->fn_lock_sync_work); |
---|
880 | 1094 | } |
---|
881 | 1095 | |
---|
882 | 1096 | static void lenovo_remove(struct hid_device *hdev) |
---|
.. | .. |
---|
888 | 1102 | case USB_DEVICE_ID_LENOVO_CUSBKBD: |
---|
889 | 1103 | case USB_DEVICE_ID_LENOVO_CBTKBD: |
---|
890 | 1104 | lenovo_remove_cptkbd(hdev); |
---|
| 1105 | + break; |
---|
| 1106 | + case USB_DEVICE_ID_LENOVO_TP10UBKBD: |
---|
| 1107 | + lenovo_remove_tp10ubkbd(hdev); |
---|
891 | 1108 | break; |
---|
892 | 1109 | } |
---|
893 | 1110 | |
---|
.. | .. |
---|
925 | 1142 | { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) }, |
---|
926 | 1143 | { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) }, |
---|
927 | 1144 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) }, |
---|
| 1145 | + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) }, |
---|
928 | 1146 | { } |
---|
929 | 1147 | }; |
---|
930 | 1148 | |
---|