diff options
Diffstat (limited to 'drivers/hid/hid-asus.c')
-rw-r--r-- | drivers/hid/hid-asus.c | 265 |
1 files changed, 190 insertions, 75 deletions
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 78cdfb8b9a7a..4b45e31f0bab 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -52,6 +52,10 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define FEATURE_KBD_LED_REPORT_ID1 0x5d #define FEATURE_KBD_LED_REPORT_ID2 0x5e +#define ROG_ALLY_REPORT_SIZE 64 +#define ROG_ALLY_X_MIN_MCU 313 +#define ROG_ALLY_MIN_MCU 319 + #define SUPPORT_KBD_BACKLIGHT BIT(0) #define MAX_TOUCH_MAJOR 8 @@ -84,6 +88,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_MEDION_E1239T BIT(10) #define QUIRK_ROG_NKEY_KEYBOARD BIT(11) #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) +#define QUIRK_ROG_ALLY_XPAD BIT(13) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -335,36 +340,20 @@ static int asus_raw_event(struct hid_device *hdev, if (drvdata->quirks & QUIRK_MEDION_E1239T) return asus_e1239t_event(drvdata, data, size); - if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) { + /* + * Skip these report ID, the device emits a continuous stream associated + * with the AURA mode it is in which looks like an 'echo'. + */ + if (report->id == FEATURE_KBD_LED_REPORT_ID1 || report->id == FEATURE_KBD_LED_REPORT_ID2) + return -1; + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { /* - * Skip these report ID, the device emits a continuous stream associated - * with the AURA mode it is in which looks like an 'echo'. + * G713 and G733 send these codes on some keypresses, depending on + * the key pressed it can trigger a shutdown event if not caught. */ - if (report->id == FEATURE_KBD_LED_REPORT_ID1 || - report->id == FEATURE_KBD_LED_REPORT_ID2) { + if (data[0] == 0x02 && data[1] == 0x30) { return -1; - /* Additional report filtering */ - } else if (report->id == FEATURE_KBD_REPORT_ID) { - /* - * G14 and G15 send these codes on some keypresses with no - * discernable reason for doing so. We'll filter them out to avoid - * unmapped warning messages later. - */ - if (data[1] == 0xea || data[1] == 0xec || data[1] == 0x02 || - data[1] == 0x8a || data[1] == 0x9e) { - return -1; - } } - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - /* - * G713 and G733 send these codes on some keypresses, depending on - * the key pressed it can trigger a shutdown event if not caught. - */ - if(data[0] == 0x02 && data[1] == 0x30) { - return -1; - } - } - } if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) { @@ -402,9 +391,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu return ret; } -static int asus_kbd_init(struct hid_device *hdev) +static int asus_kbd_init(struct hid_device *hdev, u8 report_id) { - const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, + const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; int ret; @@ -416,9 +405,10 @@ static int asus_kbd_init(struct hid_device *hdev) } static int asus_kbd_get_functions(struct hid_device *hdev, - unsigned char *kbd_func) + unsigned char *kbd_func, + u8 report_id) { - const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }; + const u8 buf[] = { report_id, 0x05, 0x20, 0x31, 0x00, 0x08 }; u8 *readbuf; int ret; @@ -447,49 +437,24 @@ static int asus_kbd_get_functions(struct hid_device *hdev, return ret; } -static int rog_nkey_led_init(struct hid_device *hdev) +static int asus_kbd_disable_oobe(struct hid_device *hdev) { - const u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 }; - u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20, - 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; - u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1, - 0x05, 0x20, 0x31, 0x00, 0x08 }; + const u8 init[][6] = { + { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }, + { FEATURE_KBD_REPORT_ID, 0xBA, 0xC5, 0xC4 }, + { FEATURE_KBD_REPORT_ID, 0xD0, 0x8F, 0x01 }, + { FEATURE_KBD_REPORT_ID, 0xD0, 0x85, 0xFF } + }; int ret; - hid_info(hdev, "Asus initialise N-KEY Device"); - /* The first message is an init start */ - ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init start command: %d\n", ret); - return ret; - } - /* Followed by a string */ - ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 1.0: %d\n", ret); - return ret; - } - /* Followed by a string */ - ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 1.1: %d\n", ret); - return ret; - } - - /* begin second report ID with same data */ - buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2; - buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2; - - ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 2.0: %d\n", ret); - return ret; + for (size_t i = 0; i < ARRAY_SIZE(init); i++) { + ret = asus_kbd_set_report(hdev, init[i], sizeof(init[i])); + if (ret < 0) + return ret; } - ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3)); - if (ret < 0) - hid_warn(hdev, "Asus failed to send init command 2.1: %d\n", ret); - return ret; + hid_info(hdev, "Disabled OOBE for keyboard\n"); + return 0; } static void asus_schedule_work(struct asus_kbd_leds *led) @@ -552,12 +517,19 @@ static void asus_kbd_backlight_work(struct work_struct *work) */ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) { + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); u32 value; int ret; if (!IS_ENABLED(CONFIG_ASUS_WMI)) return false; + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && + dmi_check_system(asus_use_hid_led_dmi_ids)) { + hid_info(hdev, "using HID for asus::kbd_backlight\n"); + return false; + } + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); @@ -567,24 +539,141 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT); } +/* + * We don't care about any other part of the string except the version section. + * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01 + * The bytes "5a 05 03 31 00 1a 13" and possibly more come before the version + * string, and there may be additional bytes after the version string such as + * "75 00 74 00 65 00" or a postfix such as "_T01" + */ +static int mcu_parse_version_string(const u8 *response, size_t response_size) +{ + const u8 *end = response + response_size; + const u8 *p = response; + int dots, err, version; + char buf[4]; + + dots = 0; + while (p < end && dots < 2) { + if (*p++ == '.') + dots++; + } + + if (dots != 2 || p >= end || (p + 3) >= end) + return -EINVAL; + + memcpy(buf, p, 3); + buf[3] = '\0'; + + err = kstrtoint(buf, 10, &version); + if (err || version < 0) + return -EINVAL; + + return version; +} + +static int mcu_request_version(struct hid_device *hdev) +{ + u8 *response __free(kfree) = kzalloc(ROG_ALLY_REPORT_SIZE, GFP_KERNEL); + const u8 request[] = { 0x5a, 0x05, 0x03, 0x31, 0x00, 0x20 }; + int ret; + + if (!response) + return -ENOMEM; + + ret = asus_kbd_set_report(hdev, request, sizeof(request)); + if (ret < 0) + return ret; + + ret = hid_hw_raw_request(hdev, FEATURE_REPORT_ID, response, + ROG_ALLY_REPORT_SIZE, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret < 0) + return ret; + + ret = mcu_parse_version_string(response, ROG_ALLY_REPORT_SIZE); + if (ret < 0) { + pr_err("Failed to parse MCU version: %d\n", ret); + print_hex_dump(KERN_ERR, "MCU: ", DUMP_PREFIX_NONE, + 16, 1, response, ROG_ALLY_REPORT_SIZE, false); + } + + return ret; +} + +static void validate_mcu_fw_version(struct hid_device *hdev, int idProduct) +{ + int min_version, version; + + version = mcu_request_version(hdev); + if (version < 0) + return; + + switch (idProduct) { + case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY: + min_version = ROG_ALLY_MIN_MCU; + break; + case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X: + min_version = ROG_ALLY_X_MIN_MCU; + break; + default: + min_version = 0; + } + + if (version < min_version) { + hid_warn(hdev, + "The MCU firmware version must be %d or greater to avoid issues with suspend.\n", + min_version); + } else { + set_ally_mcu_hack(ASUS_WMI_ALLY_MCU_HACK_DISABLED); + set_ally_mcu_powersave(true); + } +} + static int asus_kbd_register_leds(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + struct usb_interface *intf; + struct usb_device *udev; unsigned char kbd_func; int ret; if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - ret = rog_nkey_led_init(hdev); + /* Initialize keyboard */ + ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); + if (ret < 0) + return ret; + + /* The LED endpoint is initialised in two HID */ + ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1); if (ret < 0) return ret; + + ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2); + if (ret < 0) + return ret; + + if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) { + ret = asus_kbd_disable_oobe(hdev); + if (ret < 0) + return ret; + } + + if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) { + intf = to_usb_interface(hdev->dev.parent); + udev = interface_to_usbdev(intf); + validate_mcu_fw_version(hdev, + le16_to_cpu(udev->descriptor.idProduct)); + } + } else { /* Initialize keyboard */ - ret = asus_kbd_init(hdev); + ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); if (ret < 0) return ret; /* Get keyboard functions */ - ret = asus_kbd_get_functions(hdev, &kbd_func); + ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID); if (ret < 0) return ret; @@ -897,7 +986,10 @@ static int asus_input_mapping(struct hid_device *hdev, case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */ case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */ case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */ - + case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */ + case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */ + case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */ + case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */ default: /* ASUS lazily declares 256 usages, ignore the rest, @@ -1166,7 +1258,7 @@ static const __u8 asus_g752_fixed_rdesc[] = { 0x2A, 0xFF, 0x00, /* Usage Maximum (0xFF) */ }; -static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); @@ -1223,7 +1315,7 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (drvdata->quirks & QUIRK_G752_KEYBOARD && *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) { - /* report is missing usage mninum and maximum */ + /* report is missing usage minimum and maximum */ __u8 *new_rdesc; size_t new_size = *rsize + sizeof(asus_g752_fixed_rdesc); @@ -1250,6 +1342,19 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[205] = 0x01; } + /* match many more n-key devices */ + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && *rsize > 15) { + for (int i = 0; i < *rsize - 15; i++) { + /* offset to the count from 0x5a report part always 14 */ + if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a && + rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) { + hid_info(hdev, "Fixing up Asus N-Key report descriptor\n"); + rdesc[i + 15] = 0x01; + break; + } + } + } + return rdesc; } @@ -1277,6 +1382,15 @@ static const struct hid_device_id asus_devices[] = { USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD}, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD), QUIRK_ROG_CLAYMORE_II_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, @@ -1319,4 +1433,5 @@ static struct hid_driver asus_driver = { }; module_hid_driver(asus_driver); -MODULE_LICENSE("GPL");
\ No newline at end of file +MODULE_IMPORT_NS("ASUS_WMI"); +MODULE_LICENSE("GPL"); |