.. | .. |
---|
| 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 | \ |
---|
.. | .. |
---|
94 | 105 | int res_y; |
---|
95 | 106 | int contact_size; |
---|
96 | 107 | int max_contacts; |
---|
| 108 | + int report_size; |
---|
97 | 109 | }; |
---|
98 | 110 | |
---|
99 | 111 | struct asus_drvdata { |
---|
100 | 112 | unsigned long quirks; |
---|
| 113 | + struct hid_device *hdev; |
---|
101 | 114 | struct input_dev *input; |
---|
| 115 | + struct input_dev *tp_kbd_input; |
---|
102 | 116 | struct asus_kbd_leds *kbd_backlight; |
---|
103 | 117 | const struct asus_touchpad_info *tp; |
---|
104 | 118 | bool enable_backlight; |
---|
| 119 | + struct power_supply *battery; |
---|
| 120 | + struct power_supply_desc battery_desc; |
---|
| 121 | + int battery_capacity; |
---|
| 122 | + int battery_stat; |
---|
| 123 | + bool battery_in_query; |
---|
| 124 | + unsigned long battery_next_query; |
---|
105 | 125 | }; |
---|
| 126 | + |
---|
| 127 | +static int asus_report_battery(struct asus_drvdata *, u8 *, int); |
---|
106 | 128 | |
---|
107 | 129 | static const struct asus_touchpad_info asus_i2c_tp = { |
---|
108 | 130 | .max_x = 2794, |
---|
109 | 131 | .max_y = 1758, |
---|
110 | 132 | .contact_size = 5, |
---|
111 | 133 | .max_contacts = 5, |
---|
| 134 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
112 | 135 | }; |
---|
113 | 136 | |
---|
114 | 137 | static const struct asus_touchpad_info asus_t100ta_tp = { |
---|
.. | .. |
---|
118 | 141 | .res_y = 27, /* units/mm */ |
---|
119 | 142 | .contact_size = 5, |
---|
120 | 143 | .max_contacts = 5, |
---|
| 144 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
121 | 145 | }; |
---|
122 | 146 | |
---|
123 | 147 | static const struct asus_touchpad_info asus_t100ha_tp = { |
---|
.. | .. |
---|
127 | 151 | .res_y = 29, /* units/mm */ |
---|
128 | 152 | .contact_size = 5, |
---|
129 | 153 | .max_contacts = 5, |
---|
| 154 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
130 | 155 | }; |
---|
131 | 156 | |
---|
132 | 157 | static const struct asus_touchpad_info asus_t200ta_tp = { |
---|
.. | .. |
---|
136 | 161 | .res_y = 28, /* units/mm */ |
---|
137 | 162 | .contact_size = 5, |
---|
138 | 163 | .max_contacts = 5, |
---|
| 164 | + .report_size = 28 /* 2 byte header + 5 * 5 + 1 byte footer */, |
---|
139 | 165 | }; |
---|
140 | 166 | |
---|
141 | 167 | static const struct asus_touchpad_info asus_t100chi_tp = { |
---|
.. | .. |
---|
145 | 171 | .res_y = 29, /* units/mm */ |
---|
146 | 172 | .contact_size = 3, |
---|
147 | 173 | .max_contacts = 4, |
---|
| 174 | + .report_size = 15 /* 2 byte header + 3 * 4 + 1 byte footer */, |
---|
| 175 | +}; |
---|
| 176 | + |
---|
| 177 | +static const struct asus_touchpad_info medion_e1239t_tp = { |
---|
| 178 | + .max_x = 2640, |
---|
| 179 | + .max_y = 1380, |
---|
| 180 | + .res_x = 29, /* units/mm */ |
---|
| 181 | + .res_y = 28, /* units/mm */ |
---|
| 182 | + .contact_size = 5, |
---|
| 183 | + .max_contacts = 5, |
---|
| 184 | + .report_size = 32 /* 2 byte header + 5 * 5 + 5 byte footer */, |
---|
148 | 185 | }; |
---|
149 | 186 | |
---|
150 | 187 | static void asus_report_contact_down(struct asus_drvdata *drvdat, |
---|
.. | .. |
---|
212 | 249 | int i, toolType = MT_TOOL_FINGER; |
---|
213 | 250 | u8 *contactData = data + 2; |
---|
214 | 251 | |
---|
215 | | - if (size != 3 + drvdat->tp->contact_size * drvdat->tp->max_contacts) |
---|
| 252 | + if (size != drvdat->tp->report_size) |
---|
216 | 253 | return 0; |
---|
217 | 254 | |
---|
218 | 255 | for (i = 0; i < drvdat->tp->max_contacts; i++) { |
---|
.. | .. |
---|
240 | 277 | return 1; |
---|
241 | 278 | } |
---|
242 | 279 | |
---|
| 280 | +static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size) |
---|
| 281 | +{ |
---|
| 282 | + if (size != 3) |
---|
| 283 | + return 0; |
---|
| 284 | + |
---|
| 285 | + /* Handle broken mute key which only sends press events */ |
---|
| 286 | + if (!drvdat->tp && |
---|
| 287 | + data[0] == 0x02 && data[1] == 0xe2 && data[2] == 0x00) { |
---|
| 288 | + input_report_key(drvdat->input, KEY_MUTE, 1); |
---|
| 289 | + input_sync(drvdat->input); |
---|
| 290 | + input_report_key(drvdat->input, KEY_MUTE, 0); |
---|
| 291 | + input_sync(drvdat->input); |
---|
| 292 | + return 1; |
---|
| 293 | + } |
---|
| 294 | + |
---|
| 295 | + /* Handle custom touchpad toggle key which only sends press events */ |
---|
| 296 | + if (drvdat->tp_kbd_input && |
---|
| 297 | + data[0] == 0x05 && data[1] == 0x02 && data[2] == 0x28) { |
---|
| 298 | + input_report_key(drvdat->tp_kbd_input, KEY_F21, 1); |
---|
| 299 | + input_sync(drvdat->tp_kbd_input); |
---|
| 300 | + input_report_key(drvdat->tp_kbd_input, KEY_F21, 0); |
---|
| 301 | + input_sync(drvdat->tp_kbd_input); |
---|
| 302 | + return 1; |
---|
| 303 | + } |
---|
| 304 | + |
---|
| 305 | + return 0; |
---|
| 306 | +} |
---|
| 307 | + |
---|
| 308 | +static int asus_event(struct hid_device *hdev, struct hid_field *field, |
---|
| 309 | + struct hid_usage *usage, __s32 value) |
---|
| 310 | +{ |
---|
| 311 | + if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && |
---|
| 312 | + (usage->hid & HID_USAGE) != 0x00 && |
---|
| 313 | + (usage->hid & HID_USAGE) != 0xff && !usage->type) { |
---|
| 314 | + hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", |
---|
| 315 | + usage->hid & HID_USAGE); |
---|
| 316 | + } |
---|
| 317 | + |
---|
| 318 | + return 0; |
---|
| 319 | +} |
---|
| 320 | + |
---|
243 | 321 | static int asus_raw_event(struct hid_device *hdev, |
---|
244 | 322 | struct hid_report *report, u8 *data, int size) |
---|
245 | 323 | { |
---|
246 | 324 | struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
247 | 325 | |
---|
| 326 | + if (drvdata->battery && data[0] == BATTERY_REPORT_ID) |
---|
| 327 | + return asus_report_battery(drvdata, data, size); |
---|
| 328 | + |
---|
248 | 329 | if (drvdata->tp && data[0] == INPUT_REPORT_ID) |
---|
249 | 330 | return asus_report_input(drvdata, data, size); |
---|
| 331 | + |
---|
| 332 | + if (drvdata->quirks & QUIRK_MEDION_E1239T) |
---|
| 333 | + return asus_e1239t_event(drvdata, data, size); |
---|
250 | 334 | |
---|
251 | 335 | return 0; |
---|
252 | 336 | } |
---|
.. | .. |
---|
349 | 433 | hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret); |
---|
350 | 434 | } |
---|
351 | 435 | |
---|
| 436 | +/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes |
---|
| 437 | + * precedence. We only activate HID-based backlight control when the |
---|
| 438 | + * WMI control is not available. |
---|
| 439 | + */ |
---|
| 440 | +static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) |
---|
| 441 | +{ |
---|
| 442 | + u32 value; |
---|
| 443 | + int ret; |
---|
| 444 | + |
---|
| 445 | + if (!IS_ENABLED(CONFIG_ASUS_WMI)) |
---|
| 446 | + return false; |
---|
| 447 | + |
---|
| 448 | + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, |
---|
| 449 | + ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); |
---|
| 450 | + hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); |
---|
| 451 | + if (ret) |
---|
| 452 | + return false; |
---|
| 453 | + |
---|
| 454 | + return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT); |
---|
| 455 | +} |
---|
| 456 | + |
---|
352 | 457 | static int asus_kbd_register_leds(struct hid_device *hdev) |
---|
353 | 458 | { |
---|
354 | 459 | struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
.. | .. |
---|
393 | 498 | return ret; |
---|
394 | 499 | } |
---|
395 | 500 | |
---|
| 501 | +/* |
---|
| 502 | + * [0] REPORT_ID (same value defined in report descriptor) |
---|
| 503 | + * [1] rest battery level. range [0..255] |
---|
| 504 | + * [2]..[7] Bluetooth hardware address (MAC address) |
---|
| 505 | + * [8] charging status |
---|
| 506 | + * = 0 : AC offline / discharging |
---|
| 507 | + * = 1 : AC online / charging |
---|
| 508 | + * = 2 : AC online / fully charged |
---|
| 509 | + */ |
---|
| 510 | +static int asus_parse_battery(struct asus_drvdata *drvdata, u8 *data, int size) |
---|
| 511 | +{ |
---|
| 512 | + u8 sts; |
---|
| 513 | + u8 lvl; |
---|
| 514 | + int val; |
---|
| 515 | + |
---|
| 516 | + lvl = data[1]; |
---|
| 517 | + sts = data[8]; |
---|
| 518 | + |
---|
| 519 | + drvdata->battery_capacity = ((int)lvl * 100) / (int)BATTERY_LEVEL_MAX; |
---|
| 520 | + |
---|
| 521 | + switch (sts) { |
---|
| 522 | + case BATTERY_STAT_CHARGING: |
---|
| 523 | + val = POWER_SUPPLY_STATUS_CHARGING; |
---|
| 524 | + break; |
---|
| 525 | + case BATTERY_STAT_FULL: |
---|
| 526 | + val = POWER_SUPPLY_STATUS_FULL; |
---|
| 527 | + break; |
---|
| 528 | + case BATTERY_STAT_DISCONNECT: |
---|
| 529 | + default: |
---|
| 530 | + val = POWER_SUPPLY_STATUS_DISCHARGING; |
---|
| 531 | + break; |
---|
| 532 | + } |
---|
| 533 | + drvdata->battery_stat = val; |
---|
| 534 | + |
---|
| 535 | + return 0; |
---|
| 536 | +} |
---|
| 537 | + |
---|
| 538 | +static int asus_report_battery(struct asus_drvdata *drvdata, u8 *data, int size) |
---|
| 539 | +{ |
---|
| 540 | + /* notify only the autonomous event by device */ |
---|
| 541 | + if ((drvdata->battery_in_query == false) && |
---|
| 542 | + (size == BATTERY_REPORT_SIZE)) |
---|
| 543 | + power_supply_changed(drvdata->battery); |
---|
| 544 | + |
---|
| 545 | + return 0; |
---|
| 546 | +} |
---|
| 547 | + |
---|
| 548 | +static int asus_battery_query(struct asus_drvdata *drvdata) |
---|
| 549 | +{ |
---|
| 550 | + u8 *buf; |
---|
| 551 | + int ret = 0; |
---|
| 552 | + |
---|
| 553 | + buf = kmalloc(BATTERY_REPORT_SIZE, GFP_KERNEL); |
---|
| 554 | + if (!buf) |
---|
| 555 | + return -ENOMEM; |
---|
| 556 | + |
---|
| 557 | + drvdata->battery_in_query = true; |
---|
| 558 | + ret = hid_hw_raw_request(drvdata->hdev, BATTERY_REPORT_ID, |
---|
| 559 | + buf, BATTERY_REPORT_SIZE, |
---|
| 560 | + HID_INPUT_REPORT, HID_REQ_GET_REPORT); |
---|
| 561 | + drvdata->battery_in_query = false; |
---|
| 562 | + if (ret == BATTERY_REPORT_SIZE) |
---|
| 563 | + ret = asus_parse_battery(drvdata, buf, BATTERY_REPORT_SIZE); |
---|
| 564 | + else |
---|
| 565 | + ret = -ENODATA; |
---|
| 566 | + |
---|
| 567 | + kfree(buf); |
---|
| 568 | + |
---|
| 569 | + return ret; |
---|
| 570 | +} |
---|
| 571 | + |
---|
| 572 | +static enum power_supply_property asus_battery_props[] = { |
---|
| 573 | + POWER_SUPPLY_PROP_STATUS, |
---|
| 574 | + POWER_SUPPLY_PROP_PRESENT, |
---|
| 575 | + POWER_SUPPLY_PROP_CAPACITY, |
---|
| 576 | + POWER_SUPPLY_PROP_SCOPE, |
---|
| 577 | + POWER_SUPPLY_PROP_MODEL_NAME, |
---|
| 578 | +}; |
---|
| 579 | + |
---|
| 580 | +#define QUERY_MIN_INTERVAL (60 * HZ) /* 60[sec] */ |
---|
| 581 | + |
---|
| 582 | +static int asus_battery_get_property(struct power_supply *psy, |
---|
| 583 | + enum power_supply_property psp, |
---|
| 584 | + union power_supply_propval *val) |
---|
| 585 | +{ |
---|
| 586 | + struct asus_drvdata *drvdata = power_supply_get_drvdata(psy); |
---|
| 587 | + int ret = 0; |
---|
| 588 | + |
---|
| 589 | + switch (psp) { |
---|
| 590 | + case POWER_SUPPLY_PROP_STATUS: |
---|
| 591 | + case POWER_SUPPLY_PROP_CAPACITY: |
---|
| 592 | + if (time_before(drvdata->battery_next_query, jiffies)) { |
---|
| 593 | + drvdata->battery_next_query = |
---|
| 594 | + jiffies + QUERY_MIN_INTERVAL; |
---|
| 595 | + ret = asus_battery_query(drvdata); |
---|
| 596 | + if (ret) |
---|
| 597 | + return ret; |
---|
| 598 | + } |
---|
| 599 | + if (psp == POWER_SUPPLY_PROP_STATUS) |
---|
| 600 | + val->intval = drvdata->battery_stat; |
---|
| 601 | + else |
---|
| 602 | + val->intval = drvdata->battery_capacity; |
---|
| 603 | + break; |
---|
| 604 | + case POWER_SUPPLY_PROP_PRESENT: |
---|
| 605 | + val->intval = 1; |
---|
| 606 | + break; |
---|
| 607 | + case POWER_SUPPLY_PROP_SCOPE: |
---|
| 608 | + val->intval = POWER_SUPPLY_SCOPE_DEVICE; |
---|
| 609 | + break; |
---|
| 610 | + case POWER_SUPPLY_PROP_MODEL_NAME: |
---|
| 611 | + val->strval = drvdata->hdev->name; |
---|
| 612 | + break; |
---|
| 613 | + default: |
---|
| 614 | + ret = -EINVAL; |
---|
| 615 | + break; |
---|
| 616 | + } |
---|
| 617 | + |
---|
| 618 | + return ret; |
---|
| 619 | +} |
---|
| 620 | + |
---|
| 621 | +static int asus_battery_probe(struct hid_device *hdev) |
---|
| 622 | +{ |
---|
| 623 | + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); |
---|
| 624 | + struct power_supply_config pscfg = { .drv_data = drvdata }; |
---|
| 625 | + int ret = 0; |
---|
| 626 | + |
---|
| 627 | + drvdata->battery_capacity = 0; |
---|
| 628 | + drvdata->battery_stat = POWER_SUPPLY_STATUS_UNKNOWN; |
---|
| 629 | + drvdata->battery_in_query = false; |
---|
| 630 | + |
---|
| 631 | + drvdata->battery_desc.properties = asus_battery_props; |
---|
| 632 | + drvdata->battery_desc.num_properties = ARRAY_SIZE(asus_battery_props); |
---|
| 633 | + drvdata->battery_desc.get_property = asus_battery_get_property; |
---|
| 634 | + drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; |
---|
| 635 | + drvdata->battery_desc.use_for_apm = 0; |
---|
| 636 | + drvdata->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, |
---|
| 637 | + "asus-keyboard-%s-battery", |
---|
| 638 | + strlen(hdev->uniq) ? |
---|
| 639 | + hdev->uniq : dev_name(&hdev->dev)); |
---|
| 640 | + if (!drvdata->battery_desc.name) |
---|
| 641 | + return -ENOMEM; |
---|
| 642 | + |
---|
| 643 | + drvdata->battery_next_query = jiffies; |
---|
| 644 | + |
---|
| 645 | + drvdata->battery = devm_power_supply_register(&hdev->dev, |
---|
| 646 | + &(drvdata->battery_desc), &pscfg); |
---|
| 647 | + if (IS_ERR(drvdata->battery)) { |
---|
| 648 | + ret = PTR_ERR(drvdata->battery); |
---|
| 649 | + drvdata->battery = NULL; |
---|
| 650 | + hid_err(hdev, "Unable to register battery device\n"); |
---|
| 651 | + return ret; |
---|
| 652 | + } |
---|
| 653 | + |
---|
| 654 | + power_supply_powers(drvdata->battery, &hdev->dev); |
---|
| 655 | + |
---|
| 656 | + return ret; |
---|
| 657 | +} |
---|
| 658 | + |
---|
396 | 659 | static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) |
---|
397 | 660 | { |
---|
398 | 661 | struct input_dev *input = hi->input; |
---|
.. | .. |
---|
402 | 665 | if (drvdata->quirks & QUIRK_T100CHI && |
---|
403 | 666 | hi->report->id != T100CHI_MOUSE_REPORT_ID) |
---|
404 | 667 | return 0; |
---|
| 668 | + |
---|
| 669 | + /* Handle MULTI_INPUT on E1239T mouse/touchpad USB interface */ |
---|
| 670 | + if (drvdata->tp && (drvdata->quirks & QUIRK_MEDION_E1239T)) { |
---|
| 671 | + switch (hi->report->id) { |
---|
| 672 | + case E1239T_TP_TOGGLE_REPORT_ID: |
---|
| 673 | + input_set_capability(input, EV_KEY, KEY_F21); |
---|
| 674 | + input->name = "Asus Touchpad Keys"; |
---|
| 675 | + drvdata->tp_kbd_input = input; |
---|
| 676 | + return 0; |
---|
| 677 | + case INPUT_REPORT_ID: |
---|
| 678 | + break; /* Touchpad report, handled below */ |
---|
| 679 | + default: |
---|
| 680 | + return 0; /* Ignore other reports */ |
---|
| 681 | + } |
---|
| 682 | + } |
---|
405 | 683 | |
---|
406 | 684 | if (drvdata->tp) { |
---|
407 | 685 | int ret; |
---|
.. | .. |
---|
436 | 714 | |
---|
437 | 715 | drvdata->input = input; |
---|
438 | 716 | |
---|
439 | | - if (drvdata->enable_backlight && asus_kbd_register_leds(hdev)) |
---|
| 717 | + if (drvdata->enable_backlight && |
---|
| 718 | + !asus_kbd_wmi_led_control_present(hdev) && |
---|
| 719 | + asus_kbd_register_leds(hdev)) |
---|
440 | 720 | hid_warn(hdev, "Failed to initialize backlight.\n"); |
---|
441 | 721 | |
---|
442 | 722 | return 0; |
---|
.. | .. |
---|
463 | 743 | * This avoids a bunch of non-functional hid_input devices getting |
---|
464 | 744 | * created because of the T100CHI using HID_QUIRK_MULTI_INPUT. |
---|
465 | 745 | */ |
---|
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 | | - } |
---|
| 746 | + if ((drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) && |
---|
| 747 | + (field->application == (HID_UP_GENDESK | 0x0080) || |
---|
| 748 | + field->application == HID_GD_MOUSE || |
---|
| 749 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) || |
---|
| 750 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) || |
---|
| 751 | + usage->hid == (HID_UP_GENDEVCTRLS | 0x0026))) |
---|
| 752 | + return -1; |
---|
480 | 753 | |
---|
481 | 754 | /* ASUS-specific keyboard hotkeys */ |
---|
482 | 755 | if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) { |
---|
483 | | - set_bit(EV_REP, hi->input->evbit); |
---|
484 | 756 | switch (usage->hid & HID_USAGE) { |
---|
485 | 757 | case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break; |
---|
486 | 758 | case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break; |
---|
487 | 759 | case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break; |
---|
488 | 760 | case 0x6c: asus_map_key_clear(KEY_SLEEP); break; |
---|
| 761 | + case 0x7c: asus_map_key_clear(KEY_MICMUTE); break; |
---|
489 | 762 | case 0x82: asus_map_key_clear(KEY_CAMERA); break; |
---|
490 | 763 | case 0x88: asus_map_key_clear(KEY_RFKILL); break; |
---|
491 | 764 | case 0xb5: asus_map_key_clear(KEY_CALC); break; |
---|
.. | .. |
---|
504 | 777 | /* Fn+Space Power4Gear Hybrid */ |
---|
505 | 778 | case 0x5c: asus_map_key_clear(KEY_PROG3); break; |
---|
506 | 779 | |
---|
| 780 | + /* Fn+F5 "fan" symbol on FX503VD */ |
---|
| 781 | + case 0x99: asus_map_key_clear(KEY_PROG4); break; |
---|
| 782 | + |
---|
507 | 783 | default: |
---|
508 | 784 | /* ASUS lazily declares 256 usages, ignore the rest, |
---|
509 | 785 | * as some make the keyboard appear as a pointer device. */ |
---|
.. | .. |
---|
519 | 795 | if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) |
---|
520 | 796 | drvdata->enable_backlight = true; |
---|
521 | 797 | |
---|
| 798 | + set_bit(EV_REP, hi->input->evbit); |
---|
522 | 799 | return 1; |
---|
523 | 800 | } |
---|
524 | 801 | |
---|
525 | 802 | if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) { |
---|
526 | | - set_bit(EV_REP, hi->input->evbit); |
---|
527 | 803 | switch (usage->hid & HID_USAGE) { |
---|
528 | 804 | case 0xff01: asus_map_key_clear(BTN_1); break; |
---|
529 | 805 | case 0xff02: asus_map_key_clear(BTN_2); break; |
---|
.. | .. |
---|
546 | 822 | return 0; |
---|
547 | 823 | } |
---|
548 | 824 | |
---|
| 825 | + set_bit(EV_REP, hi->input->evbit); |
---|
549 | 826 | return 1; |
---|
550 | 827 | } |
---|
551 | 828 | |
---|
.. | .. |
---|
562 | 839 | */ |
---|
563 | 840 | return -1; |
---|
564 | 841 | } |
---|
| 842 | + } |
---|
| 843 | + |
---|
| 844 | + /* |
---|
| 845 | + * The mute button is broken and only sends press events, we |
---|
| 846 | + * deal with this in our raw_event handler, so do not map it. |
---|
| 847 | + */ |
---|
| 848 | + if ((drvdata->quirks & QUIRK_MEDION_E1239T) && |
---|
| 849 | + usage->hid == (HID_UP_CONSUMER | 0xe2)) { |
---|
| 850 | + input_set_capability(hi->input, EV_KEY, KEY_MUTE); |
---|
| 851 | + return -1; |
---|
565 | 852 | } |
---|
566 | 853 | |
---|
567 | 854 | return 0; |
---|
.. | .. |
---|
619 | 906 | |
---|
620 | 907 | drvdata->quirks = id->driver_data; |
---|
621 | 908 | |
---|
| 909 | + /* |
---|
| 910 | + * T90CHI's keyboard dock returns same ID values as T100CHI's dock. |
---|
| 911 | + * Thus, identify T90CHI dock with product name string. |
---|
| 912 | + */ |
---|
| 913 | + if (strstr(hdev->name, "T90CHI")) { |
---|
| 914 | + drvdata->quirks &= ~QUIRK_T100CHI; |
---|
| 915 | + drvdata->quirks |= QUIRK_T90CHI; |
---|
| 916 | + } |
---|
| 917 | + |
---|
622 | 918 | if (drvdata->quirks & QUIRK_IS_MULTITOUCH) |
---|
623 | 919 | drvdata->tp = &asus_i2c_tp; |
---|
624 | 920 | |
---|
.. | .. |
---|
650 | 946 | drvdata->tp = &asus_t100chi_tp; |
---|
651 | 947 | } |
---|
652 | 948 | |
---|
| 949 | + if ((drvdata->quirks & QUIRK_MEDION_E1239T) && hid_is_usb(hdev)) { |
---|
| 950 | + struct usb_host_interface *alt = |
---|
| 951 | + to_usb_interface(hdev->dev.parent)->altsetting; |
---|
| 952 | + |
---|
| 953 | + if (alt->desc.bInterfaceNumber == MEDION_E1239T_TPAD_INTF) { |
---|
| 954 | + /* For separate input-devs for tp and tp toggle key */ |
---|
| 955 | + hdev->quirks |= HID_QUIRK_MULTI_INPUT; |
---|
| 956 | + drvdata->quirks |= QUIRK_SKIP_INPUT_MAPPING; |
---|
| 957 | + drvdata->tp = &medion_e1239t_tp; |
---|
| 958 | + } |
---|
| 959 | + } |
---|
| 960 | + |
---|
653 | 961 | if (drvdata->quirks & QUIRK_NO_INIT_REPORTS) |
---|
654 | 962 | hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; |
---|
| 963 | + |
---|
| 964 | + drvdata->hdev = hdev; |
---|
| 965 | + |
---|
| 966 | + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { |
---|
| 967 | + ret = asus_battery_probe(hdev); |
---|
| 968 | + if (ret) { |
---|
| 969 | + hid_err(hdev, |
---|
| 970 | + "Asus hid battery_probe failed: %d\n", ret); |
---|
| 971 | + return ret; |
---|
| 972 | + } |
---|
| 973 | + } |
---|
655 | 974 | |
---|
656 | 975 | ret = hid_parse(hdev); |
---|
657 | 976 | if (ret) { |
---|
658 | 977 | hid_err(hdev, "Asus hid parse failed: %d\n", ret); |
---|
659 | 978 | return ret; |
---|
660 | 979 | } |
---|
| 980 | + |
---|
| 981 | + /* use hid-multitouch for T101HA touchpad */ |
---|
| 982 | + if (id->driver_data & QUIRK_T101HA_DOCK && |
---|
| 983 | + hdev->collection->usage == HID_GD_MOUSE) |
---|
| 984 | + return -ENODEV; |
---|
661 | 985 | |
---|
662 | 986 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
---|
663 | 987 | if (ret) { |
---|
.. | .. |
---|
723 | 1047 | hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n"); |
---|
724 | 1048 | rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT; |
---|
725 | 1049 | } |
---|
726 | | - /* For the T100CHI keyboard dock */ |
---|
727 | | - if (drvdata->quirks & QUIRK_T100CHI && |
---|
728 | | - *rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) { |
---|
| 1050 | + /* For the T100CHI/T90CHI keyboard dock */ |
---|
| 1051 | + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { |
---|
| 1052 | + int rsize_orig; |
---|
| 1053 | + int offs; |
---|
| 1054 | + |
---|
| 1055 | + if (drvdata->quirks & QUIRK_T100CHI) { |
---|
| 1056 | + rsize_orig = 403; |
---|
| 1057 | + offs = 388; |
---|
| 1058 | + } else { |
---|
| 1059 | + rsize_orig = 306; |
---|
| 1060 | + offs = 291; |
---|
| 1061 | + } |
---|
| 1062 | + |
---|
729 | 1063 | /* |
---|
730 | 1064 | * Change Usage (76h) to Usage Minimum (00h), Usage Maximum |
---|
731 | 1065 | * (FFh) and clear the flags in the Input() byte. |
---|
732 | 1066 | * Note the descriptor has a bogus 0 byte at the end so we |
---|
733 | 1067 | * only need 1 extra byte. |
---|
734 | 1068 | */ |
---|
735 | | - *rsize = 404; |
---|
736 | | - rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); |
---|
737 | | - if (!rdesc) |
---|
738 | | - return NULL; |
---|
| 1069 | + if (*rsize == rsize_orig && |
---|
| 1070 | + rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) { |
---|
| 1071 | + *rsize = rsize_orig + 1; |
---|
| 1072 | + rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); |
---|
| 1073 | + if (!rdesc) |
---|
| 1074 | + return NULL; |
---|
739 | 1075 | |
---|
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; |
---|
| 1076 | + hid_info(hdev, "Fixing up %s keyb report descriptor\n", |
---|
| 1077 | + drvdata->quirks & QUIRK_T100CHI ? |
---|
| 1078 | + "T100CHI" : "T90CHI"); |
---|
| 1079 | + memmove(rdesc + offs + 4, rdesc + offs + 2, 12); |
---|
| 1080 | + rdesc[offs] = 0x19; |
---|
| 1081 | + rdesc[offs + 1] = 0x00; |
---|
| 1082 | + rdesc[offs + 2] = 0x29; |
---|
| 1083 | + rdesc[offs + 3] = 0xff; |
---|
| 1084 | + rdesc[offs + 14] = 0x00; |
---|
| 1085 | + } |
---|
747 | 1086 | } |
---|
| 1087 | + |
---|
748 | 1088 | if (drvdata->quirks & QUIRK_G752_KEYBOARD && |
---|
749 | 1089 | *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) { |
---|
750 | 1090 | /* report is missing usage mninum and maximum */ |
---|
.. | .. |
---|
782 | 1122 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
783 | 1123 | USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD }, |
---|
784 | 1124 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
| 1125 | + USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD), |
---|
| 1126 | + QUIRK_USE_KBD_BACKLIGHT }, |
---|
| 1127 | + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
785 | 1128 | USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD), |
---|
786 | 1129 | QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, |
---|
787 | 1130 | { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
788 | 1131 | USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD), |
---|
789 | 1132 | QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, |
---|
| 1133 | + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
| 1134 | + USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK }, |
---|
790 | 1135 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, |
---|
791 | 1136 | { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, |
---|
792 | 1137 | { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, |
---|
793 | 1138 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, |
---|
794 | 1139 | USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), QUIRK_T100CHI }, |
---|
795 | | - |
---|
| 1140 | + { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE_MEDION_E1239T), |
---|
| 1141 | + QUIRK_MEDION_E1239T }, |
---|
796 | 1142 | { } |
---|
797 | 1143 | }; |
---|
798 | 1144 | MODULE_DEVICE_TABLE(hid, asus_devices); |
---|
.. | .. |
---|
808 | 1154 | #ifdef CONFIG_PM |
---|
809 | 1155 | .reset_resume = asus_reset_resume, |
---|
810 | 1156 | #endif |
---|
| 1157 | + .event = asus_event, |
---|
811 | 1158 | .raw_event = asus_raw_event |
---|
812 | 1159 | }; |
---|
813 | 1160 | module_hid_driver(asus_driver); |
---|