.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * HID driver for Asus notebook built-in keyboard. |
---|
3 | 4 | * Fixes small logical maximum to match usage maximum. |
---|
.. | .. |
---|
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/dmi.h> |
---|
30 | 27 | #include <linux/hid.h> |
---|
31 | 28 | #include <linux/module.h> |
---|
| 29 | +#include <linux/platform_data/x86/asus-wmi.h> |
---|
32 | 30 | #include <linux/input/mt.h> |
---|
33 | 31 | #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ |
---|
| 32 | +#include <linux/power_supply.h> |
---|
34 | 33 | |
---|
35 | 34 | #include "hid-ids.h" |
---|
36 | 35 | |
---|
.. | .. |
---|
41 | 40 | MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); |
---|
42 | 41 | |
---|
43 | 42 | #define T100_TPAD_INTF 2 |
---|
| 43 | +#define MEDION_E1239T_TPAD_INTF 1 |
---|
44 | 44 | |
---|
| 45 | +#define E1239T_TP_TOGGLE_REPORT_ID 0x05 |
---|
45 | 46 | #define T100CHI_MOUSE_REPORT_ID 0x06 |
---|
46 | 47 | #define FEATURE_REPORT_ID 0x0d |
---|
47 | 48 | #define INPUT_REPORT_ID 0x5d |
---|
.. | .. |
---|
60 | 61 | #define CONTACT_TOUCH_MAJOR_MASK 0x07 |
---|
61 | 62 | #define CONTACT_PRESSURE_MASK 0x7f |
---|
62 | 63 | |
---|
| 64 | +#define BATTERY_REPORT_ID (0x03) |
---|
| 65 | +#define BATTERY_REPORT_SIZE (1 + 8) |
---|
| 66 | +#define BATTERY_LEVEL_MAX ((u8)255) |
---|
| 67 | +#define BATTERY_STAT_DISCONNECT (0) |
---|
| 68 | +#define BATTERY_STAT_CHARGING (1) |
---|
| 69 | +#define BATTERY_STAT_FULL (2) |
---|
| 70 | + |
---|
63 | 71 | #define QUIRK_FIX_NOTEBOOK_REPORT BIT(0) |
---|
64 | 72 | #define QUIRK_NO_INIT_REPORTS BIT(1) |
---|
65 | 73 | #define QUIRK_SKIP_INPUT_MAPPING BIT(2) |
---|
.. | .. |
---|
69 | 77 | #define QUIRK_T100_KEYBOARD BIT(6) |
---|
70 | 78 | #define QUIRK_T100CHI BIT(7) |
---|
71 | 79 | #define QUIRK_G752_KEYBOARD BIT(8) |
---|
| 80 | +#define QUIRK_T101HA_DOCK BIT(9) |
---|
| 81 | +#define QUIRK_T90CHI BIT(10) |
---|
| 82 | +#define QUIRK_MEDION_E1239T BIT(11) |
---|
72 | 83 | |
---|
73 | 84 | #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ |
---|
74 | 85 | QUIRK_NO_INIT_REPORTS | \ |
---|
.. | .. |
---|
84 | 95 | struct hid_device *hdev; |
---|
85 | 96 | struct work_struct work; |
---|
86 | 97 | unsigned int brightness; |
---|
| 98 | + spinlock_t lock; |
---|
87 | 99 | bool removed; |
---|
88 | 100 | }; |
---|
89 | 101 | |
---|
.. | .. |
---|
94 | 106 | int res_y; |
---|
95 | 107 | int contact_size; |
---|
96 | 108 | int max_contacts; |
---|
| 109 | + int report_size; |
---|
97 | 110 | }; |
---|
98 | 111 | |
---|
99 | 112 | struct asus_drvdata { |
---|
100 | 113 | unsigned long quirks; |
---|
| 114 | + struct hid_device *hdev; |
---|
101 | 115 | struct input_dev *input; |
---|
| 116 | + struct input_dev *tp_kbd_input; |
---|
102 | 117 | struct asus_kbd_leds *kbd_backlight; |
---|
103 | 118 | const struct asus_touchpad_info *tp; |
---|
104 | 119 | bool enable_backlight; |
---|
| 120 | + struct power_supply *battery; |
---|
| 121 | + struct power_supply_desc battery_desc; |
---|
| 122 | + int battery_capacity; |
---|
| 123 | + int battery_stat; |
---|
| 124 | + bool battery_in_query; |
---|
| 125 | + unsigned long battery_next_query; |
---|
105 | 126 | }; |
---|
| 127 | + |
---|
| 128 | +static int asus_report_battery(struct asus_drvdata *, u8 *, int); |
---|
106 | 129 | |
---|
107 | 130 | static const struct asus_touchpad_info asus_i2c_tp = { |
---|
108 | 131 | .max_x = 2794, |
---|
109 | 132 | .max_y = 1758, |
---|
110 | 133 | .contact_size = 5, |
---|
111 | 134 | .max_contacts = 5, |
---|
| 135 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
112 | 136 | }; |
---|
113 | 137 | |
---|
114 | 138 | static const struct asus_touchpad_info asus_t100ta_tp = { |
---|
.. | .. |
---|
118 | 142 | .res_y = 27, /* units/mm */ |
---|
119 | 143 | .contact_size = 5, |
---|
120 | 144 | .max_contacts = 5, |
---|
| 145 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
121 | 146 | }; |
---|
122 | 147 | |
---|
123 | 148 | static const struct asus_touchpad_info asus_t100ha_tp = { |
---|
.. | .. |
---|
127 | 152 | .res_y = 29, /* units/mm */ |
---|
128 | 153 | .contact_size = 5, |
---|
129 | 154 | .max_contacts = 5, |
---|
| 155 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
130 | 156 | }; |
---|
131 | 157 | |
---|
132 | 158 | static const struct asus_touchpad_info asus_t200ta_tp = { |
---|
.. | .. |
---|
136 | 162 | .res_y = 28, /* units/mm */ |
---|
137 | 163 | .contact_size = 5, |
---|
138 | 164 | .max_contacts = 5, |
---|
| 165 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
139 | 166 | }; |
---|
140 | 167 | |
---|
141 | 168 | static const struct asus_touchpad_info asus_t100chi_tp = { |
---|
.. | .. |
---|
145 | 172 | .res_y = 29, /* units/mm */ |
---|
146 | 173 | .contact_size = 3, |
---|
147 | 174 | .max_contacts = 4, |
---|
| 175 | + .report_size = 15 /* 2 byte header + 3 * 4 + 1 byte footer */, |
---|
| 176 | +}; |
---|
| 177 | + |
---|
| 178 | +static const struct asus_touchpad_info medion_e1239t_tp = { |
---|
| 179 | + .max_x = 2640, |
---|
| 180 | + .max_y = 1380, |
---|
| 181 | + .res_x = 29, /* units/mm */ |
---|
| 182 | + .res_y = 28, /* units/mm */ |
---|
| 183 | + .contact_size = 5, |
---|
| 184 | + .max_contacts = 5, |
---|
| 185 | + .report_size = 32 /* 2 byte header + 5 * 5 + 5 byte footer */, |
---|
148 | 186 | }; |
---|
149 | 187 | |
---|
150 | 188 | static void asus_report_contact_down(struct asus_drvdata *drvdat, |
---|
.. | .. |
---|
212 | 250 | int i, toolType = MT_TOOL_FINGER; |
---|
213 | 251 | u8 *contactData = data + 2; |
---|
214 | 252 | |
---|
215 | | - if (size != 3 + drvdat->tp->contact_size * drvdat->tp->max_contacts) |
---|
| 253 | + if (size != drvdat->tp->report_size) |
---|
216 | 254 | return 0; |
---|
217 | 255 | |
---|
218 | 256 | for (i = 0; i < drvdat->tp->max_contacts; i++) { |
---|
.. | .. |
---|
240 | 278 | return 1; |
---|
241 | 279 | } |
---|
242 | 280 | |
---|
| 281 | +static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size) |
---|
| 282 | +{ |
---|
| 283 | + if (size != 3) |
---|
| 284 | + return 0; |
---|
| 285 | + |
---|
| 286 | + /* Handle broken mute key which only sends press events */ |
---|
| 287 | + if (!drvdat->tp && |
---|
| 288 | + data[0] == 0x02 && data[1] == 0xe2 && data[2] == 0x00) { |
---|
| 289 | + input_report_key(drvdat->input, KEY_MUTE, 1); |
---|
| 290 | + input_sync(drvdat->input); |
---|
| 291 | + input_report_key(drvdat->input, KEY_MUTE, 0); |
---|
| 292 | + input_sync(drvdat->input); |
---|
| 293 | + return 1; |
---|
| 294 | + } |
---|
| 295 | + |
---|
| 296 | + /* Handle custom touchpad toggle key which only sends press events */ |
---|
| 297 | + if (drvdat->tp_kbd_input && |
---|
| 298 | + data[0] == 0x05 && data[1] == 0x02 && data[2] == 0x28) { |
---|
| 299 | + input_report_key(drvdat->tp_kbd_input, KEY_F21, 1); |
---|
| 300 | + input_sync(drvdat->tp_kbd_input); |
---|
| 301 | + input_report_key(drvdat->tp_kbd_input, KEY_F21, 0); |
---|
| 302 | + input_sync(drvdat->tp_kbd_input); |
---|
| 303 | + return 1; |
---|
| 304 | + } |
---|
| 305 | + |
---|
| 306 | + return 0; |
---|
| 307 | +} |
---|
| 308 | + |
---|
| 309 | +static int asus_event(struct hid_device *hdev, struct hid_field *field, |
---|
| 310 | + struct hid_usage *usage, __s32 value) |
---|
| 311 | +{ |
---|
| 312 | + if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && |
---|
| 313 | + (usage->hid & HID_USAGE) != 0x00 && |
---|
| 314 | + (usage->hid & HID_USAGE) != 0xff && !usage->type) { |
---|
| 315 | + hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", |
---|
| 316 | + usage->hid & HID_USAGE); |
---|
| 317 | + } |
---|
| 318 | + |
---|
| 319 | + return 0; |
---|
| 320 | +} |
---|
| 321 | + |
---|
243 | 322 | static int asus_raw_event(struct hid_device *hdev, |
---|
244 | 323 | struct hid_report *report, u8 *data, int size) |
---|
245 | 324 | { |
---|
246 | 325 | struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
247 | 326 | |
---|
| 327 | + if (drvdata->battery && data[0] == BATTERY_REPORT_ID) |
---|
| 328 | + return asus_report_battery(drvdata, data, size); |
---|
| 329 | + |
---|
248 | 330 | if (drvdata->tp && data[0] == INPUT_REPORT_ID) |
---|
249 | 331 | return asus_report_input(drvdata, data, size); |
---|
| 332 | + |
---|
| 333 | + if (drvdata->quirks & QUIRK_MEDION_E1239T) |
---|
| 334 | + return asus_e1239t_event(drvdata, data, size); |
---|
250 | 335 | |
---|
251 | 336 | return 0; |
---|
252 | 337 | } |
---|
.. | .. |
---|
313 | 398 | return ret; |
---|
314 | 399 | } |
---|
315 | 400 | |
---|
| 401 | +static void asus_schedule_work(struct asus_kbd_leds *led) |
---|
| 402 | +{ |
---|
| 403 | + unsigned long flags; |
---|
| 404 | + |
---|
| 405 | + spin_lock_irqsave(&led->lock, flags); |
---|
| 406 | + if (!led->removed) |
---|
| 407 | + schedule_work(&led->work); |
---|
| 408 | + spin_unlock_irqrestore(&led->lock, flags); |
---|
| 409 | +} |
---|
| 410 | + |
---|
316 | 411 | static void asus_kbd_backlight_set(struct led_classdev *led_cdev, |
---|
317 | 412 | enum led_brightness brightness) |
---|
318 | 413 | { |
---|
319 | 414 | struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, |
---|
320 | 415 | cdev); |
---|
321 | | - if (led->brightness == brightness) |
---|
322 | | - return; |
---|
| 416 | + unsigned long flags; |
---|
323 | 417 | |
---|
| 418 | + spin_lock_irqsave(&led->lock, flags); |
---|
324 | 419 | led->brightness = brightness; |
---|
325 | | - schedule_work(&led->work); |
---|
| 420 | + spin_unlock_irqrestore(&led->lock, flags); |
---|
| 421 | + |
---|
| 422 | + asus_schedule_work(led); |
---|
326 | 423 | } |
---|
327 | 424 | |
---|
328 | 425 | static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev) |
---|
329 | 426 | { |
---|
330 | 427 | struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, |
---|
331 | 428 | cdev); |
---|
| 429 | + enum led_brightness brightness; |
---|
| 430 | + unsigned long flags; |
---|
332 | 431 | |
---|
333 | | - return led->brightness; |
---|
| 432 | + spin_lock_irqsave(&led->lock, flags); |
---|
| 433 | + brightness = led->brightness; |
---|
| 434 | + spin_unlock_irqrestore(&led->lock, flags); |
---|
| 435 | + |
---|
| 436 | + return brightness; |
---|
334 | 437 | } |
---|
335 | 438 | |
---|
336 | 439 | static void asus_kbd_backlight_work(struct work_struct *work) |
---|
.. | .. |
---|
338 | 441 | struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work); |
---|
339 | 442 | u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 }; |
---|
340 | 443 | int ret; |
---|
| 444 | + unsigned long flags; |
---|
341 | 445 | |
---|
342 | | - if (led->removed) |
---|
343 | | - return; |
---|
344 | | - |
---|
| 446 | + spin_lock_irqsave(&led->lock, flags); |
---|
345 | 447 | buf[4] = led->brightness; |
---|
| 448 | + spin_unlock_irqrestore(&led->lock, flags); |
---|
346 | 449 | |
---|
347 | 450 | ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf)); |
---|
348 | 451 | if (ret < 0) |
---|
349 | 452 | hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret); |
---|
| 453 | +} |
---|
| 454 | + |
---|
| 455 | +/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes |
---|
| 456 | + * precedence. We only activate HID-based backlight control when the |
---|
| 457 | + * WMI control is not available. |
---|
| 458 | + */ |
---|
| 459 | +static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) |
---|
| 460 | +{ |
---|
| 461 | + u32 value; |
---|
| 462 | + int ret; |
---|
| 463 | + |
---|
| 464 | + if (!IS_ENABLED(CONFIG_ASUS_WMI)) |
---|
| 465 | + return false; |
---|
| 466 | + |
---|
| 467 | + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, |
---|
| 468 | + ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); |
---|
| 469 | + hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); |
---|
| 470 | + if (ret) |
---|
| 471 | + return false; |
---|
| 472 | + |
---|
| 473 | + return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT); |
---|
350 | 474 | } |
---|
351 | 475 | |
---|
352 | 476 | static int asus_kbd_register_leds(struct hid_device *hdev) |
---|
.. | .. |
---|
383 | 507 | drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set; |
---|
384 | 508 | drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get; |
---|
385 | 509 | INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work); |
---|
| 510 | + spin_lock_init(&drvdata->kbd_backlight->lock); |
---|
386 | 511 | |
---|
387 | 512 | ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev); |
---|
388 | 513 | if (ret < 0) { |
---|
389 | 514 | /* No need to have this still around */ |
---|
390 | 515 | devm_kfree(&hdev->dev, drvdata->kbd_backlight); |
---|
391 | 516 | } |
---|
| 517 | + |
---|
| 518 | + return ret; |
---|
| 519 | +} |
---|
| 520 | + |
---|
| 521 | +/* |
---|
| 522 | + * [0] REPORT_ID (same value defined in report descriptor) |
---|
| 523 | + * [1] rest battery level. range [0..255] |
---|
| 524 | + * [2]..[7] Bluetooth hardware address (MAC address) |
---|
| 525 | + * [8] charging status |
---|
| 526 | + * = 0 : AC offline / discharging |
---|
| 527 | + * = 1 : AC online / charging |
---|
| 528 | + * = 2 : AC online / fully charged |
---|
| 529 | + */ |
---|
| 530 | +static int asus_parse_battery(struct asus_drvdata *drvdata, u8 *data, int size) |
---|
| 531 | +{ |
---|
| 532 | + u8 sts; |
---|
| 533 | + u8 lvl; |
---|
| 534 | + int val; |
---|
| 535 | + |
---|
| 536 | + lvl = data[1]; |
---|
| 537 | + sts = data[8]; |
---|
| 538 | + |
---|
| 539 | + drvdata->battery_capacity = ((int)lvl * 100) / (int)BATTERY_LEVEL_MAX; |
---|
| 540 | + |
---|
| 541 | + switch (sts) { |
---|
| 542 | + case BATTERY_STAT_CHARGING: |
---|
| 543 | + val = POWER_SUPPLY_STATUS_CHARGING; |
---|
| 544 | + break; |
---|
| 545 | + case BATTERY_STAT_FULL: |
---|
| 546 | + val = POWER_SUPPLY_STATUS_FULL; |
---|
| 547 | + break; |
---|
| 548 | + case BATTERY_STAT_DISCONNECT: |
---|
| 549 | + default: |
---|
| 550 | + val = POWER_SUPPLY_STATUS_DISCHARGING; |
---|
| 551 | + break; |
---|
| 552 | + } |
---|
| 553 | + drvdata->battery_stat = val; |
---|
| 554 | + |
---|
| 555 | + return 0; |
---|
| 556 | +} |
---|
| 557 | + |
---|
| 558 | +static int asus_report_battery(struct asus_drvdata *drvdata, u8 *data, int size) |
---|
| 559 | +{ |
---|
| 560 | + /* notify only the autonomous event by device */ |
---|
| 561 | + if ((drvdata->battery_in_query == false) && |
---|
| 562 | + (size == BATTERY_REPORT_SIZE)) |
---|
| 563 | + power_supply_changed(drvdata->battery); |
---|
| 564 | + |
---|
| 565 | + return 0; |
---|
| 566 | +} |
---|
| 567 | + |
---|
| 568 | +static int asus_battery_query(struct asus_drvdata *drvdata) |
---|
| 569 | +{ |
---|
| 570 | + u8 *buf; |
---|
| 571 | + int ret = 0; |
---|
| 572 | + |
---|
| 573 | + buf = kmalloc(BATTERY_REPORT_SIZE, GFP_KERNEL); |
---|
| 574 | + if (!buf) |
---|
| 575 | + return -ENOMEM; |
---|
| 576 | + |
---|
| 577 | + drvdata->battery_in_query = true; |
---|
| 578 | + ret = hid_hw_raw_request(drvdata->hdev, BATTERY_REPORT_ID, |
---|
| 579 | + buf, BATTERY_REPORT_SIZE, |
---|
| 580 | + HID_INPUT_REPORT, HID_REQ_GET_REPORT); |
---|
| 581 | + drvdata->battery_in_query = false; |
---|
| 582 | + if (ret == BATTERY_REPORT_SIZE) |
---|
| 583 | + ret = asus_parse_battery(drvdata, buf, BATTERY_REPORT_SIZE); |
---|
| 584 | + else |
---|
| 585 | + ret = -ENODATA; |
---|
| 586 | + |
---|
| 587 | + kfree(buf); |
---|
| 588 | + |
---|
| 589 | + return ret; |
---|
| 590 | +} |
---|
| 591 | + |
---|
| 592 | +static enum power_supply_property asus_battery_props[] = { |
---|
| 593 | + POWER_SUPPLY_PROP_STATUS, |
---|
| 594 | + POWER_SUPPLY_PROP_PRESENT, |
---|
| 595 | + POWER_SUPPLY_PROP_CAPACITY, |
---|
| 596 | + POWER_SUPPLY_PROP_SCOPE, |
---|
| 597 | + POWER_SUPPLY_PROP_MODEL_NAME, |
---|
| 598 | +}; |
---|
| 599 | + |
---|
| 600 | +#define QUERY_MIN_INTERVAL (60 * HZ) /* 60[sec] */ |
---|
| 601 | + |
---|
| 602 | +static int asus_battery_get_property(struct power_supply *psy, |
---|
| 603 | + enum power_supply_property psp, |
---|
| 604 | + union power_supply_propval *val) |
---|
| 605 | +{ |
---|
| 606 | + struct asus_drvdata *drvdata = power_supply_get_drvdata(psy); |
---|
| 607 | + int ret = 0; |
---|
| 608 | + |
---|
| 609 | + switch (psp) { |
---|
| 610 | + case POWER_SUPPLY_PROP_STATUS: |
---|
| 611 | + case POWER_SUPPLY_PROP_CAPACITY: |
---|
| 612 | + if (time_before(drvdata->battery_next_query, jiffies)) { |
---|
| 613 | + drvdata->battery_next_query = |
---|
| 614 | + jiffies + QUERY_MIN_INTERVAL; |
---|
| 615 | + ret = asus_battery_query(drvdata); |
---|
| 616 | + if (ret) |
---|
| 617 | + return ret; |
---|
| 618 | + } |
---|
| 619 | + if (psp == POWER_SUPPLY_PROP_STATUS) |
---|
| 620 | + val->intval = drvdata->battery_stat; |
---|
| 621 | + else |
---|
| 622 | + val->intval = drvdata->battery_capacity; |
---|
| 623 | + break; |
---|
| 624 | + case POWER_SUPPLY_PROP_PRESENT: |
---|
| 625 | + val->intval = 1; |
---|
| 626 | + break; |
---|
| 627 | + case POWER_SUPPLY_PROP_SCOPE: |
---|
| 628 | + val->intval = POWER_SUPPLY_SCOPE_DEVICE; |
---|
| 629 | + break; |
---|
| 630 | + case POWER_SUPPLY_PROP_MODEL_NAME: |
---|
| 631 | + val->strval = drvdata->hdev->name; |
---|
| 632 | + break; |
---|
| 633 | + default: |
---|
| 634 | + ret = -EINVAL; |
---|
| 635 | + break; |
---|
| 636 | + } |
---|
| 637 | + |
---|
| 638 | + return ret; |
---|
| 639 | +} |
---|
| 640 | + |
---|
| 641 | +static int asus_battery_probe(struct hid_device *hdev) |
---|
| 642 | +{ |
---|
| 643 | + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
| 644 | + struct power_supply_config pscfg = { .drv_data = drvdata }; |
---|
| 645 | + int ret = 0; |
---|
| 646 | + |
---|
| 647 | + drvdata->battery_capacity = 0; |
---|
| 648 | + drvdata->battery_stat = POWER_SUPPLY_STATUS_UNKNOWN; |
---|
| 649 | + drvdata->battery_in_query = false; |
---|
| 650 | + |
---|
| 651 | + drvdata->battery_desc.properties = asus_battery_props; |
---|
| 652 | + drvdata->battery_desc.num_properties = ARRAY_SIZE(asus_battery_props); |
---|
| 653 | + drvdata->battery_desc.get_property = asus_battery_get_property; |
---|
| 654 | + drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; |
---|
| 655 | + drvdata->battery_desc.use_for_apm = 0; |
---|
| 656 | + drvdata->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, |
---|
| 657 | + "asus-keyboard-%s-battery", |
---|
| 658 | + strlen(hdev->uniq) ? |
---|
| 659 | + hdev->uniq : dev_name(&hdev->dev)); |
---|
| 660 | + if (!drvdata->battery_desc.name) |
---|
| 661 | + return -ENOMEM; |
---|
| 662 | + |
---|
| 663 | + drvdata->battery_next_query = jiffies; |
---|
| 664 | + |
---|
| 665 | + drvdata->battery = devm_power_supply_register(&hdev->dev, |
---|
| 666 | + &(drvdata->battery_desc), &pscfg); |
---|
| 667 | + if (IS_ERR(drvdata->battery)) { |
---|
| 668 | + ret = PTR_ERR(drvdata->battery); |
---|
| 669 | + drvdata->battery = NULL; |
---|
| 670 | + hid_err(hdev, "Unable to register battery device\n"); |
---|
| 671 | + return ret; |
---|
| 672 | + } |
---|
| 673 | + |
---|
| 674 | + power_supply_powers(drvdata->battery, &hdev->dev); |
---|
392 | 675 | |
---|
393 | 676 | return ret; |
---|
394 | 677 | } |
---|
.. | .. |
---|
402 | 685 | if (drvdata->quirks & QUIRK_T100CHI && |
---|
403 | 686 | hi->report->id != T100CHI_MOUSE_REPORT_ID) |
---|
404 | 687 | return 0; |
---|
| 688 | + |
---|
| 689 | + /* Handle MULTI_INPUT on E1239T mouse/touchpad USB interface */ |
---|
| 690 | + if (drvdata->tp && (drvdata->quirks & QUIRK_MEDION_E1239T)) { |
---|
| 691 | + switch (hi->report->id) { |
---|
| 692 | + case E1239T_TP_TOGGLE_REPORT_ID: |
---|
| 693 | + input_set_capability(input, EV_KEY, KEY_F21); |
---|
| 694 | + input->name = "Asus Touchpad Keys"; |
---|
| 695 | + drvdata->tp_kbd_input = input; |
---|
| 696 | + return 0; |
---|
| 697 | + case INPUT_REPORT_ID: |
---|
| 698 | + break; /* Touchpad report, handled below */ |
---|
| 699 | + default: |
---|
| 700 | + return 0; /* Ignore other reports */ |
---|
| 701 | + } |
---|
| 702 | + } |
---|
405 | 703 | |
---|
406 | 704 | if (drvdata->tp) { |
---|
407 | 705 | int ret; |
---|
.. | .. |
---|
436 | 734 | |
---|
437 | 735 | drvdata->input = input; |
---|
438 | 736 | |
---|
439 | | - if (drvdata->enable_backlight && asus_kbd_register_leds(hdev)) |
---|
| 737 | + if (drvdata->enable_backlight && |
---|
| 738 | + !asus_kbd_wmi_led_control_present(hdev) && |
---|
| 739 | + asus_kbd_register_leds(hdev)) |
---|
440 | 740 | hid_warn(hdev, "Failed to initialize backlight.\n"); |
---|
441 | 741 | |
---|
442 | 742 | return 0; |
---|
.. | .. |
---|
463 | 763 | * This avoids a bunch of non-functional hid_input devices getting |
---|
464 | 764 | * created because of the T100CHI using HID_QUIRK_MULTI_INPUT. |
---|
465 | 765 | */ |
---|
466 | | - if (drvdata->quirks & QUIRK_T100CHI) { |
---|
467 | | - if (field->application == (HID_UP_GENDESK | 0x0080) || |
---|
468 | | - usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) || |
---|
469 | | - usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) || |
---|
470 | | - usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)) |
---|
471 | | - return -1; |
---|
472 | | - /* |
---|
473 | | - * We use the hid_input for the mouse report for the touchpad, |
---|
474 | | - * keep the left button, to avoid the core removing it. |
---|
475 | | - */ |
---|
476 | | - if (field->application == HID_GD_MOUSE && |
---|
477 | | - usage->hid != (HID_UP_BUTTON | 1)) |
---|
478 | | - return -1; |
---|
479 | | - } |
---|
| 766 | + if ((drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) && |
---|
| 767 | + (field->application == (HID_UP_GENDESK | 0x0080) || |
---|
| 768 | + field->application == HID_GD_MOUSE || |
---|
| 769 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) || |
---|
| 770 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) || |
---|
| 771 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0026))) |
---|
| 772 | + return -1; |
---|
480 | 773 | |
---|
481 | 774 | /* ASUS-specific keyboard hotkeys */ |
---|
482 | 775 | if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) { |
---|
483 | | - set_bit(EV_REP, hi->input->evbit); |
---|
484 | 776 | switch (usage->hid & HID_USAGE) { |
---|
485 | 777 | case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break; |
---|
486 | 778 | case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break; |
---|
487 | 779 | case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break; |
---|
488 | 780 | case 0x6c: asus_map_key_clear(KEY_SLEEP); break; |
---|
| 781 | + case 0x7c: asus_map_key_clear(KEY_MICMUTE); break; |
---|
489 | 782 | case 0x82: asus_map_key_clear(KEY_CAMERA); break; |
---|
490 | 783 | case 0x88: asus_map_key_clear(KEY_RFKILL); break; |
---|
491 | 784 | case 0xb5: asus_map_key_clear(KEY_CALC); break; |
---|
.. | .. |
---|
504 | 797 | /* Fn+Space Power4Gear Hybrid */ |
---|
505 | 798 | case 0x5c: asus_map_key_clear(KEY_PROG3); break; |
---|
506 | 799 | |
---|
| 800 | + /* Fn+F5 "fan" symbol on FX503VD */ |
---|
| 801 | + case 0x99: asus_map_key_clear(KEY_PROG4); break; |
---|
| 802 | + |
---|
507 | 803 | default: |
---|
508 | 804 | /* ASUS lazily declares 256 usages, ignore the rest, |
---|
509 | 805 | * as some make the keyboard appear as a pointer device. */ |
---|
.. | .. |
---|
519 | 815 | if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) |
---|
520 | 816 | drvdata->enable_backlight = true; |
---|
521 | 817 | |
---|
| 818 | + set_bit(EV_REP, hi->input->evbit); |
---|
522 | 819 | return 1; |
---|
523 | 820 | } |
---|
524 | 821 | |
---|
525 | 822 | if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) { |
---|
526 | | - set_bit(EV_REP, hi->input->evbit); |
---|
527 | 823 | switch (usage->hid & HID_USAGE) { |
---|
528 | 824 | case 0xff01: asus_map_key_clear(BTN_1); break; |
---|
529 | 825 | case 0xff02: asus_map_key_clear(BTN_2); break; |
---|
.. | .. |
---|
546 | 842 | return 0; |
---|
547 | 843 | } |
---|
548 | 844 | |
---|
| 845 | + set_bit(EV_REP, hi->input->evbit); |
---|
549 | 846 | return 1; |
---|
550 | 847 | } |
---|
551 | 848 | |
---|
.. | .. |
---|
562 | 859 | */ |
---|
563 | 860 | return -1; |
---|
564 | 861 | } |
---|
| 862 | + } |
---|
| 863 | + |
---|
| 864 | + /* |
---|
| 865 | + * The mute button is broken and only sends press events, we |
---|
| 866 | + * deal with this in our raw_event handler, so do not map it. |
---|
| 867 | + */ |
---|
| 868 | + if ((drvdata->quirks & QUIRK_MEDION_E1239T) && |
---|
| 869 | + usage->hid == (HID_UP_CONSUMER | 0xe2)) { |
---|
| 870 | + input_set_capability(hi->input, EV_KEY, KEY_MUTE); |
---|
| 871 | + return -1; |
---|
565 | 872 | } |
---|
566 | 873 | |
---|
567 | 874 | return 0; |
---|
.. | .. |
---|
619 | 926 | |
---|
620 | 927 | drvdata->quirks = id->driver_data; |
---|
621 | 928 | |
---|
| 929 | + /* |
---|
| 930 | + * T90CHI's keyboard dock returns same ID values as T100CHI's dock. |
---|
| 931 | + * Thus, identify T90CHI dock with product name string. |
---|
| 932 | + */ |
---|
| 933 | + if (strstr(hdev->name, "T90CHI")) { |
---|
| 934 | + drvdata->quirks &= ~QUIRK_T100CHI; |
---|
| 935 | + drvdata->quirks |= QUIRK_T90CHI; |
---|
| 936 | + } |
---|
| 937 | + |
---|
622 | 938 | if (drvdata->quirks & QUIRK_IS_MULTITOUCH) |
---|
623 | 939 | drvdata->tp = &asus_i2c_tp; |
---|
624 | 940 | |
---|
.. | .. |
---|
650 | 966 | drvdata->tp = &asus_t100chi_tp; |
---|
651 | 967 | } |
---|
652 | 968 | |
---|
| 969 | + if ((drvdata->quirks & QUIRK_MEDION_E1239T) && hid_is_usb(hdev)) { |
---|
| 970 | + struct usb_host_interface *alt = |
---|
| 971 | + to_usb_interface(hdev->dev.parent)->altsetting; |
---|
| 972 | + |
---|
| 973 | + if (alt->desc.bInterfaceNumber == MEDION_E1239T_TPAD_INTF) { |
---|
| 974 | + /* For separate input-devs for tp and tp toggle key */ |
---|
| 975 | + hdev->quirks |= HID_QUIRK_MULTI_INPUT; |
---|
| 976 | + drvdata->quirks |= QUIRK_SKIP_INPUT_MAPPING; |
---|
| 977 | + drvdata->tp = &medion_e1239t_tp; |
---|
| 978 | + } |
---|
| 979 | + } |
---|
| 980 | + |
---|
653 | 981 | if (drvdata->quirks & QUIRK_NO_INIT_REPORTS) |
---|
654 | 982 | hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; |
---|
| 983 | + |
---|
| 984 | + drvdata->hdev = hdev; |
---|
| 985 | + |
---|
| 986 | + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { |
---|
| 987 | + ret = asus_battery_probe(hdev); |
---|
| 988 | + if (ret) { |
---|
| 989 | + hid_err(hdev, |
---|
| 990 | + "Asus hid battery_probe failed: %d\n", ret); |
---|
| 991 | + return ret; |
---|
| 992 | + } |
---|
| 993 | + } |
---|
655 | 994 | |
---|
656 | 995 | ret = hid_parse(hdev); |
---|
657 | 996 | if (ret) { |
---|
658 | 997 | hid_err(hdev, "Asus hid parse failed: %d\n", ret); |
---|
659 | 998 | return ret; |
---|
660 | 999 | } |
---|
| 1000 | + |
---|
| 1001 | + /* use hid-multitouch for T101HA touchpad */ |
---|
| 1002 | + if (id->driver_data & QUIRK_T101HA_DOCK && |
---|
| 1003 | + hdev->collection->usage == HID_GD_MOUSE) |
---|
| 1004 | + return -ENODEV; |
---|
661 | 1005 | |
---|
662 | 1006 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
---|
663 | 1007 | if (ret) { |
---|
.. | .. |
---|
692 | 1036 | static void asus_remove(struct hid_device *hdev) |
---|
693 | 1037 | { |
---|
694 | 1038 | struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
| 1039 | + unsigned long flags; |
---|
695 | 1040 | |
---|
696 | 1041 | if (drvdata->kbd_backlight) { |
---|
| 1042 | + spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags); |
---|
697 | 1043 | drvdata->kbd_backlight->removed = true; |
---|
| 1044 | + spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags); |
---|
| 1045 | + |
---|
698 | 1046 | cancel_work_sync(&drvdata->kbd_backlight->work); |
---|
699 | 1047 | } |
---|
700 | 1048 | |
---|
.. | .. |
---|
723 | 1071 | hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n"); |
---|
724 | 1072 | rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT; |
---|
725 | 1073 | } |
---|
726 | | - /* For the T100CHI keyboard dock */ |
---|
727 | | - if (drvdata->quirks & QUIRK_T100CHI && |
---|
728 | | - *rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) { |
---|
| 1074 | + /* For the T100CHI/T90CHI keyboard dock */ |
---|
| 1075 | + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { |
---|
| 1076 | + int rsize_orig; |
---|
| 1077 | + int offs; |
---|
| 1078 | + |
---|
| 1079 | + if (drvdata->quirks & QUIRK_T100CHI) { |
---|
| 1080 | + rsize_orig = 403; |
---|
| 1081 | + offs = 388; |
---|
| 1082 | + } else { |
---|
| 1083 | + rsize_orig = 306; |
---|
| 1084 | + offs = 291; |
---|
| 1085 | + } |
---|
| 1086 | + |
---|
729 | 1087 | /* |
---|
730 | 1088 | * Change Usage (76h) to Usage Minimum (00h), Usage Maximum |
---|
731 | 1089 | * (FFh) and clear the flags in the Input() byte. |
---|
732 | 1090 | * Note the descriptor has a bogus 0 byte at the end so we |
---|
733 | 1091 | * only need 1 extra byte. |
---|
734 | 1092 | */ |
---|
735 | | - *rsize = 404; |
---|
736 | | - rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); |
---|
737 | | - if (!rdesc) |
---|
738 | | - return NULL; |
---|
| 1093 | + if (*rsize == rsize_orig && |
---|
| 1094 | + rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) { |
---|
| 1095 | + *rsize = rsize_orig + 1; |
---|
| 1096 | + rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); |
---|
| 1097 | + if (!rdesc) |
---|
| 1098 | + return NULL; |
---|
739 | 1099 | |
---|
740 | | - hid_info(hdev, "Fixing up T100CHI keyb report descriptor\n"); |
---|
741 | | - memmove(rdesc + 392, rdesc + 390, 12); |
---|
742 | | - rdesc[388] = 0x19; |
---|
743 | | - rdesc[389] = 0x00; |
---|
744 | | - rdesc[390] = 0x29; |
---|
745 | | - rdesc[391] = 0xff; |
---|
746 | | - rdesc[402] = 0x00; |
---|
| 1100 | + hid_info(hdev, "Fixing up %s keyb report descriptor\n", |
---|
| 1101 | + drvdata->quirks & QUIRK_T100CHI ? |
---|
| 1102 | + "T100CHI" : "T90CHI"); |
---|
| 1103 | + memmove(rdesc + offs + 4, rdesc + offs + 2, 12); |
---|
| 1104 | + rdesc[offs] = 0x19; |
---|
| 1105 | + rdesc[offs + 1] = 0x00; |
---|
| 1106 | + rdesc[offs + 2] = 0x29; |
---|
| 1107 | + rdesc[offs + 3] = 0xff; |
---|
| 1108 | + rdesc[offs + 14] = 0x00; |
---|
| 1109 | + } |
---|
747 | 1110 | } |
---|
| 1111 | + |
---|
748 | 1112 | if (drvdata->quirks & QUIRK_G752_KEYBOARD && |
---|
749 | 1113 | *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) { |
---|
750 | 1114 | /* report is missing usage mninum and maximum */ |
---|
.. | .. |
---|
782 | 1146 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
783 | 1147 | USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD }, |
---|
784 | 1148 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
| 1149 | + USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD), |
---|
| 1150 | + QUIRK_USE_KBD_BACKLIGHT }, |
---|
| 1151 | + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
785 | 1152 | USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD), |
---|
786 | 1153 | QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, |
---|
787 | 1154 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
788 | 1155 | USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD), |
---|
789 | 1156 | QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, |
---|
| 1157 | + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
| 1158 | + USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK }, |
---|
790 | 1159 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, |
---|
791 | 1160 | { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, |
---|
792 | 1161 | { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, |
---|
793 | 1162 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
794 | 1163 | USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), QUIRK_T100CHI }, |
---|
795 | | - |
---|
| 1164 | + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE_MEDION_E1239T), |
---|
| 1165 | + QUIRK_MEDION_E1239T }, |
---|
796 | 1166 | { } |
---|
797 | 1167 | }; |
---|
798 | 1168 | MODULE_DEVICE_TABLE(hid, asus_devices); |
---|
.. | .. |
---|
808 | 1178 | #ifdef CONFIG_PM |
---|
809 | 1179 | .reset_resume = asus_reset_resume, |
---|
810 | 1180 | #endif |
---|
| 1181 | + .event = asus_event, |
---|
811 | 1182 | .raw_event = asus_raw_event |
---|
812 | 1183 | }; |
---|
813 | 1184 | module_hid_driver(asus_driver); |
---|