Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'platform-drivers-x86-asus-kbd' into for-next

+329 -194
+105 -117
drivers/hid/hid-asus.c
··· 27 27 #include <linux/hid.h> 28 28 #include <linux/module.h> 29 29 #include <linux/platform_data/x86/asus-wmi.h> 30 - #include <linux/platform_data/x86/asus-wmi-leds-ids.h> 31 30 #include <linux/input/mt.h> 32 31 #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ 33 32 #include <linux/power_supply.h> ··· 47 48 #define T100CHI_MOUSE_REPORT_ID 0x06 48 49 #define FEATURE_REPORT_ID 0x0d 49 50 #define INPUT_REPORT_ID 0x5d 51 + #define HID_USAGE_PAGE_VENDOR 0xff310000 50 52 #define FEATURE_KBD_REPORT_ID 0x5a 51 - #define FEATURE_KBD_REPORT_SIZE 16 53 + #define FEATURE_KBD_REPORT_SIZE 64 52 54 #define FEATURE_KBD_LED_REPORT_ID1 0x5d 53 55 #define FEATURE_KBD_LED_REPORT_ID2 0x5e 54 56 ··· 90 90 #define QUIRK_ROG_NKEY_KEYBOARD BIT(11) 91 91 #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) 92 92 #define QUIRK_ROG_ALLY_XPAD BIT(13) 93 + #define QUIRK_ROG_NKEY_ID1ID2_INIT BIT(14) 93 94 94 95 #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ 95 96 QUIRK_NO_INIT_REPORTS | \ ··· 102 101 #define TRKID_SGN ((TRKID_MAX + 1) >> 1) 103 102 104 103 struct asus_kbd_leds { 105 - struct led_classdev cdev; 104 + struct asus_hid_listener listener; 106 105 struct hid_device *hdev; 107 106 struct work_struct work; 108 107 unsigned int brightness; ··· 127 126 struct input_dev *tp_kbd_input; 128 127 struct asus_kbd_leds *kbd_backlight; 129 128 const struct asus_touchpad_info *tp; 130 - bool enable_backlight; 131 129 struct power_supply *battery; 132 130 struct power_supply_desc battery_desc; 133 131 int battery_capacity; ··· 317 317 static int asus_event(struct hid_device *hdev, struct hid_field *field, 318 318 struct hid_usage *usage, __s32 value) 319 319 { 320 - if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && 320 + if ((usage->hid & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR && 321 321 (usage->hid & HID_USAGE) != 0x00 && 322 322 (usage->hid & HID_USAGE) != 0xff && !usage->type) { 323 323 hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", 324 324 usage->hid & HID_USAGE); 325 + } 326 + 327 + if (usage->type == EV_KEY && value) { 328 + switch (usage->code) { 329 + case KEY_KBDILLUMUP: 330 + return !asus_hid_event(ASUS_EV_BRTUP); 331 + case KEY_KBDILLUMDOWN: 332 + return !asus_hid_event(ASUS_EV_BRTDOWN); 333 + case KEY_KBDILLUMTOGGLE: 334 + return !asus_hid_event(ASUS_EV_BRTTOGGLE); 335 + } 325 336 } 326 337 327 338 return 0; ··· 405 394 406 395 static int asus_kbd_init(struct hid_device *hdev, u8 report_id) 407 396 { 397 + /* 398 + * The handshake is first sent as a set_report, then retrieved 399 + * from a get_report. They should be equal. 400 + */ 408 401 const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 409 402 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; 410 403 int ret; 411 404 412 405 ret = asus_kbd_set_report(hdev, buf, sizeof(buf)); 413 - if (ret < 0) 414 - hid_err(hdev, "Asus failed to send init command: %d\n", ret); 406 + if (ret < 0) { 407 + hid_err(hdev, "Asus handshake %02x failed to send: %d\n", 408 + report_id, ret); 409 + return ret; 410 + } 415 411 416 - return ret; 412 + u8 *readbuf __free(kfree) = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL); 413 + if (!readbuf) 414 + return -ENOMEM; 415 + 416 + ret = hid_hw_raw_request(hdev, report_id, readbuf, 417 + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT, 418 + HID_REQ_GET_REPORT); 419 + if (ret < 0) { 420 + hid_warn(hdev, "Asus handshake %02x failed to receive ack: %d\n", 421 + report_id, ret); 422 + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) { 423 + hid_warn(hdev, "Asus handshake %02x returned invalid response: %*ph\n", 424 + report_id, FEATURE_KBD_REPORT_SIZE, readbuf); 425 + } 426 + 427 + /* 428 + * Do not return error if handshake is wrong until this is 429 + * verified to work for all devices. 430 + */ 431 + return 0; 417 432 } 418 433 419 434 static int asus_kbd_get_functions(struct hid_device *hdev, ··· 460 423 if (!readbuf) 461 424 return -ENOMEM; 462 425 463 - ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf, 426 + ret = hid_hw_raw_request(hdev, report_id, readbuf, 464 427 FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT, 465 428 HID_REQ_GET_REPORT); 466 429 if (ret < 0) { ··· 505 468 spin_unlock_irqrestore(&led->lock, flags); 506 469 } 507 470 508 - static void asus_kbd_backlight_set(struct led_classdev *led_cdev, 509 - enum led_brightness brightness) 471 + static void asus_kbd_backlight_set(struct asus_hid_listener *listener, 472 + int brightness) 510 473 { 511 - struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, 512 - cdev); 474 + struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, 475 + listener); 513 476 unsigned long flags; 514 477 515 478 spin_lock_irqsave(&led->lock, flags); ··· 517 480 spin_unlock_irqrestore(&led->lock, flags); 518 481 519 482 asus_schedule_work(led); 520 - } 521 - 522 - static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev) 523 - { 524 - struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, 525 - cdev); 526 - enum led_brightness brightness; 527 - unsigned long flags; 528 - 529 - spin_lock_irqsave(&led->lock, flags); 530 - brightness = led->brightness; 531 - spin_unlock_irqrestore(&led->lock, flags); 532 - 533 - return brightness; 534 483 } 535 484 536 485 static void asus_kbd_backlight_work(struct work_struct *work) ··· 533 510 ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf)); 534 511 if (ret < 0) 535 512 hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret); 536 - } 537 - 538 - /* WMI-based keyboard backlight LED control (via asus-wmi driver) takes 539 - * precedence. We only activate HID-based backlight control when the 540 - * WMI control is not available. 541 - */ 542 - static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) 543 - { 544 - struct asus_drvdata *drvdata = hid_get_drvdata(hdev); 545 - u32 value; 546 - int ret; 547 - 548 - if (!IS_ENABLED(CONFIG_ASUS_WMI)) 549 - return false; 550 - 551 - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && 552 - dmi_check_system(asus_use_hid_led_dmi_ids)) { 553 - hid_info(hdev, "using HID for asus::kbd_backlight\n"); 554 - return false; 555 - } 556 - 557 - ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 558 - ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); 559 - hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); 560 - if (ret) 561 - return false; 562 - 563 - return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT); 564 513 } 565 514 566 515 /* ··· 634 639 unsigned char kbd_func; 635 640 int ret; 636 641 637 - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { 638 - /* Initialize keyboard */ 639 - ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); 642 + ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); 643 + if (ret < 0) 644 + return ret; 645 + 646 + /* Get keyboard functions */ 647 + ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID); 648 + if (ret < 0) 649 + return ret; 650 + 651 + /* Check for backlight support */ 652 + if (!(kbd_func & SUPPORT_KBD_BACKLIGHT)) 653 + return -ENODEV; 654 + 655 + if (drvdata->quirks & QUIRK_ROG_NKEY_ID1ID2_INIT) { 656 + asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1); 657 + asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2); 658 + } 659 + 660 + if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) { 661 + ret = asus_kbd_disable_oobe(hdev); 640 662 if (ret < 0) 641 663 return ret; 664 + } 642 665 643 - /* The LED endpoint is initialised in two HID */ 644 - ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1); 645 - if (ret < 0) 646 - return ret; 647 - 648 - ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2); 649 - if (ret < 0) 650 - return ret; 651 - 652 - if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) { 653 - ret = asus_kbd_disable_oobe(hdev); 654 - if (ret < 0) 655 - return ret; 656 - } 657 - 658 - if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) { 659 - intf = to_usb_interface(hdev->dev.parent); 660 - udev = interface_to_usbdev(intf); 661 - validate_mcu_fw_version(hdev, 662 - le16_to_cpu(udev->descriptor.idProduct)); 663 - } 664 - 665 - } else { 666 - /* Initialize keyboard */ 667 - ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); 668 - if (ret < 0) 669 - return ret; 670 - 671 - /* Get keyboard functions */ 672 - ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID); 673 - if (ret < 0) 674 - return ret; 675 - 676 - /* Check for backlight support */ 677 - if (!(kbd_func & SUPPORT_KBD_BACKLIGHT)) 678 - return -ENODEV; 666 + if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) { 667 + intf = to_usb_interface(hdev->dev.parent); 668 + udev = interface_to_usbdev(intf); 669 + validate_mcu_fw_version(hdev, 670 + le16_to_cpu(udev->descriptor.idProduct)); 679 671 } 680 672 681 673 drvdata->kbd_backlight = devm_kzalloc(&hdev->dev, ··· 674 692 drvdata->kbd_backlight->removed = false; 675 693 drvdata->kbd_backlight->brightness = 0; 676 694 drvdata->kbd_backlight->hdev = hdev; 677 - drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight"; 678 - drvdata->kbd_backlight->cdev.max_brightness = 3; 679 - drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set; 680 - drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get; 695 + drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; 681 696 INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work); 682 697 spin_lock_init(&drvdata->kbd_backlight->lock); 683 698 684 - ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev); 699 + ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener); 685 700 if (ret < 0) { 686 701 /* No need to have this still around */ 687 702 devm_kfree(&hdev->dev, drvdata->kbd_backlight); ··· 903 924 904 925 drvdata->input = input; 905 926 906 - if (drvdata->enable_backlight && 907 - !asus_kbd_wmi_led_control_present(hdev) && 908 - asus_kbd_register_leds(hdev)) 909 - hid_warn(hdev, "Failed to initialize backlight.\n"); 910 - 911 927 return 0; 912 928 } 913 929 ··· 974 1000 * as some make the keyboard appear as a pointer device. */ 975 1001 return -1; 976 1002 } 977 - 978 - /* 979 - * Check and enable backlight only on devices with UsagePage == 980 - * 0xff31 to avoid initializing the keyboard firmware multiple 981 - * times on devices with multiple HID descriptors but same 982 - * PID/VID. 983 - */ 984 - if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) 985 - drvdata->enable_backlight = true; 986 1003 987 1004 set_bit(EV_REP, hi->input->evbit); 988 1005 return 1; ··· 1067 1102 1068 1103 if (drvdata->kbd_backlight) { 1069 1104 const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 1070 - drvdata->kbd_backlight->cdev.brightness }; 1105 + drvdata->kbd_backlight->brightness }; 1071 1106 ret = asus_kbd_set_report(hdev, buf, sizeof(buf)); 1072 1107 if (ret < 0) { 1073 1108 hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret); ··· 1091 1126 1092 1127 static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) 1093 1128 { 1094 - int ret; 1129 + struct hid_report_enum *rep_enum; 1095 1130 struct asus_drvdata *drvdata; 1131 + struct hid_report *rep; 1132 + bool is_vendor = false; 1133 + int ret; 1096 1134 1097 1135 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 1098 1136 if (drvdata == NULL) { ··· 1179 1211 return ret; 1180 1212 } 1181 1213 1214 + /* Check for vendor for RGB init and handle generic devices properly. */ 1215 + rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; 1216 + list_for_each_entry(rep, &rep_enum->report_list, list) { 1217 + if ((rep->application & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR) 1218 + is_vendor = true; 1219 + } 1220 + 1182 1221 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 1183 1222 if (ret) { 1184 1223 hid_err(hdev, "Asus hw start failed: %d\n", ret); 1185 1224 return ret; 1186 1225 } 1226 + 1227 + if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) && 1228 + asus_kbd_register_leds(hdev)) 1229 + hid_warn(hdev, "Failed to initialize backlight.\n"); 1230 + 1231 + /* 1232 + * For ROG keyboards, skip rename for consistency and ->input check as 1233 + * some devices do not have inputs. 1234 + */ 1235 + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) 1236 + return 0; 1187 1237 1188 1238 /* 1189 1239 * Check that input registration succeeded. Checking that ··· 1239 1253 unsigned long flags; 1240 1254 1241 1255 if (drvdata->kbd_backlight) { 1256 + asus_hid_unregister_listener(&drvdata->kbd_backlight->listener); 1257 + 1242 1258 spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags); 1243 1259 drvdata->kbd_backlight->removed = true; 1244 1260 spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags); ··· 1372 1384 QUIRK_USE_KBD_BACKLIGHT }, 1373 1385 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, 1374 1386 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD), 1375 - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, 1387 + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT }, 1376 1388 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, 1377 1389 USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2), 1378 - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, 1390 + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_ID1ID2_INIT }, 1379 1391 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, 1380 1392 USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR), 1381 1393 QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+196 -27
drivers/platform/x86/asus-wmi.c
··· 31 31 #include <linux/pci.h> 32 32 #include <linux/pci_hotplug.h> 33 33 #include <linux/platform_data/x86/asus-wmi.h> 34 - #include <linux/platform_data/x86/asus-wmi-leds-ids.h> 35 34 #include <linux/platform_device.h> 36 35 #include <linux/platform_profile.h> 37 36 #include <linux/power_supply.h> 38 37 #include <linux/rfkill.h> 39 38 #include <linux/seq_file.h> 40 39 #include <linux/slab.h> 40 + #include <linux/spinlock.h> 41 41 #include <linux/types.h> 42 42 #include <linux/units.h> 43 43 ··· 256 256 int tpd_led_wk; 257 257 struct led_classdev kbd_led; 258 258 int kbd_led_wk; 259 + bool kbd_led_notify; 260 + bool kbd_led_avail; 261 + bool kbd_led_registered; 259 262 struct led_classdev lightbar_led; 260 263 int lightbar_led_wk; 261 264 struct led_classdev micmute_led; ··· 267 264 struct work_struct tpd_led_work; 268 265 struct work_struct wlan_led_work; 269 266 struct work_struct lightbar_led_work; 267 + struct work_struct kbd_led_work; 270 268 271 269 struct asus_rfkill wlan; 272 270 struct asus_rfkill bluetooth; ··· 1619 1615 1620 1616 /* LEDs ***********************************************************************/ 1621 1617 1618 + struct asus_hid_ref { 1619 + struct list_head listeners; 1620 + struct asus_wmi *asus; 1621 + /* Protects concurrent access from hid-asus and asus-wmi to leds */ 1622 + spinlock_t lock; 1623 + }; 1624 + 1625 + static struct asus_hid_ref asus_ref = { 1626 + .listeners = LIST_HEAD_INIT(asus_ref.listeners), 1627 + .asus = NULL, 1628 + /* 1629 + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other 1630 + * asus variables are read-only after .asus is set. 1631 + * 1632 + * The led cdev device is not protected because it calls backlight_get 1633 + * during initialization, which would result in a nested lock attempt. 1634 + * 1635 + * The led cdev is safe to access without a lock because if 1636 + * kbd_led_avail is true it is initialized before .asus is set and never 1637 + * changed until .asus is dropped. If kbd_led_avail is false, the led 1638 + * cdev is registered by the workqueue, which is single-threaded and 1639 + * cancelled before asus-wmi would access the led cdev to unregister it. 1640 + * 1641 + * A spinlock is used, because the protected variables can be accessed 1642 + * from an IRQ context from asus-hid. 1643 + */ 1644 + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock), 1645 + }; 1646 + 1647 + /* 1648 + * Allows registering hid-asus listeners that want to be notified of 1649 + * keyboard backlight changes. 1650 + */ 1651 + int asus_hid_register_listener(struct asus_hid_listener *bdev) 1652 + { 1653 + struct asus_wmi *asus; 1654 + 1655 + guard(spinlock_irqsave)(&asus_ref.lock); 1656 + list_add_tail(&bdev->list, &asus_ref.listeners); 1657 + asus = asus_ref.asus; 1658 + if (asus) 1659 + queue_work(asus->led_workqueue, &asus->kbd_led_work); 1660 + return 0; 1661 + } 1662 + EXPORT_SYMBOL_GPL(asus_hid_register_listener); 1663 + 1664 + /* 1665 + * Allows unregistering hid-asus listeners that were added with 1666 + * asus_hid_register_listener(). 1667 + */ 1668 + void asus_hid_unregister_listener(struct asus_hid_listener *bdev) 1669 + { 1670 + guard(spinlock_irqsave)(&asus_ref.lock); 1671 + list_del(&bdev->list); 1672 + } 1673 + EXPORT_SYMBOL_GPL(asus_hid_unregister_listener); 1674 + 1675 + static void do_kbd_led_set(struct led_classdev *led_cdev, int value); 1676 + 1677 + static void kbd_led_update_all(struct work_struct *work) 1678 + { 1679 + struct asus_wmi *asus; 1680 + bool registered, notify; 1681 + int ret, value; 1682 + 1683 + asus = container_of(work, struct asus_wmi, kbd_led_work); 1684 + 1685 + scoped_guard(spinlock_irqsave, &asus_ref.lock) { 1686 + registered = asus->kbd_led_registered; 1687 + value = asus->kbd_led_wk; 1688 + notify = asus->kbd_led_notify; 1689 + } 1690 + 1691 + if (!registered) { 1692 + /* 1693 + * This workqueue runs under asus-wmi, which means probe has 1694 + * completed and asus-wmi will keep running until it finishes. 1695 + * Therefore, we can safely register the LED without holding 1696 + * a spinlock. 1697 + */ 1698 + ret = devm_led_classdev_register(&asus->platform_device->dev, 1699 + &asus->kbd_led); 1700 + if (!ret) { 1701 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1702 + asus->kbd_led_registered = true; 1703 + } else { 1704 + pr_warn("Failed to register keyboard backlight LED: %d\n", ret); 1705 + return; 1706 + } 1707 + } 1708 + 1709 + if (value >= 0) 1710 + do_kbd_led_set(&asus->kbd_led, value); 1711 + if (notify) { 1712 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1713 + asus->kbd_led_notify = false; 1714 + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value); 1715 + } 1716 + } 1717 + 1718 + /* 1719 + * This function is called from hid-asus to inform asus-wmi of brightness 1720 + * changes initiated by the keyboard backlight keys. 1721 + */ 1722 + int asus_hid_event(enum asus_hid_event event) 1723 + { 1724 + struct asus_wmi *asus; 1725 + int brightness; 1726 + 1727 + guard(spinlock_irqsave)(&asus_ref.lock); 1728 + asus = asus_ref.asus; 1729 + if (!asus || !asus->kbd_led_registered) 1730 + return -EBUSY; 1731 + 1732 + brightness = asus->kbd_led_wk; 1733 + 1734 + switch (event) { 1735 + case ASUS_EV_BRTUP: 1736 + brightness += 1; 1737 + break; 1738 + case ASUS_EV_BRTDOWN: 1739 + brightness -= 1; 1740 + break; 1741 + case ASUS_EV_BRTTOGGLE: 1742 + if (brightness >= ASUS_EV_MAX_BRIGHTNESS) 1743 + brightness = 0; 1744 + else 1745 + brightness += 1; 1746 + break; 1747 + } 1748 + 1749 + asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS); 1750 + asus->kbd_led_notify = true; 1751 + queue_work(asus->led_workqueue, &asus->kbd_led_work); 1752 + return 0; 1753 + } 1754 + EXPORT_SYMBOL_GPL(asus_hid_event); 1755 + 1622 1756 /* 1623 1757 * These functions actually update the LED's, and are called from a 1624 1758 * workqueue. By doing this as separate work rather than when the LED ··· 1803 1661 { 1804 1662 int ctrl_param = 0; 1805 1663 1806 - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); 1664 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1665 + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); 1807 1666 asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); 1808 1667 } 1809 1668 ··· 1837 1694 1838 1695 static void do_kbd_led_set(struct led_classdev *led_cdev, int value) 1839 1696 { 1697 + struct asus_hid_listener *listener; 1840 1698 struct asus_wmi *asus; 1841 - int max_level; 1842 1699 1843 1700 asus = container_of(led_cdev, struct asus_wmi, kbd_led); 1844 - max_level = asus->kbd_led.max_brightness; 1845 1701 1846 - asus->kbd_led_wk = clamp_val(value, 0, max_level); 1847 - kbd_led_update(asus); 1702 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1703 + asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS); 1704 + 1705 + if (asus->kbd_led_avail) 1706 + kbd_led_update(asus); 1707 + 1708 + scoped_guard(spinlock_irqsave, &asus_ref.lock) { 1709 + list_for_each_entry(listener, &asus_ref.listeners, list) 1710 + listener->brightness_set(listener, asus->kbd_led_wk); 1711 + } 1848 1712 } 1849 1713 1850 1714 static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) ··· 1866 1716 1867 1717 static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) 1868 1718 { 1869 - struct led_classdev *led_cdev = &asus->kbd_led; 1870 - 1871 - do_kbd_led_set(led_cdev, value); 1872 - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk); 1719 + scoped_guard(spinlock_irqsave, &asus_ref.lock) { 1720 + asus->kbd_led_wk = value; 1721 + asus->kbd_led_notify = true; 1722 + } 1723 + queue_work(asus->led_workqueue, &asus->kbd_led_work); 1873 1724 } 1874 1725 1875 1726 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) ··· 1880 1729 1881 1730 asus = container_of(led_cdev, struct asus_wmi, kbd_led); 1882 1731 1732 + scoped_guard(spinlock_irqsave, &asus_ref.lock) { 1733 + if (!asus->kbd_led_avail) 1734 + return asus->kbd_led_wk; 1735 + } 1736 + 1883 1737 retval = kbd_led_read(asus, &value, NULL); 1884 1738 if (retval < 0) 1885 1739 return retval; 1740 + 1741 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1742 + asus->kbd_led_wk = value; 1886 1743 1887 1744 return value; 1888 1745 } ··· 2003 1844 2004 1845 static void asus_wmi_led_exit(struct asus_wmi *asus) 2005 1846 { 2006 - led_classdev_unregister(&asus->kbd_led); 1847 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 1848 + asus_ref.asus = NULL; 1849 + 2007 1850 led_classdev_unregister(&asus->tpd_led); 2008 1851 led_classdev_unregister(&asus->wlan_led); 2009 1852 led_classdev_unregister(&asus->lightbar_led); ··· 2043 1882 goto error; 2044 1883 } 2045 1884 2046 - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) { 2047 - pr_info("using asus-wmi for asus::kbd_backlight\n"); 2048 - asus->kbd_led_wk = led_val; 2049 - asus->kbd_led.name = "asus::kbd_backlight"; 2050 - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; 2051 - asus->kbd_led.brightness_set_blocking = kbd_led_set; 2052 - asus->kbd_led.brightness_get = kbd_led_get; 2053 - asus->kbd_led.max_brightness = 3; 1885 + asus->kbd_led.name = "asus::kbd_backlight"; 1886 + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; 1887 + asus->kbd_led.brightness_set_blocking = kbd_led_set; 1888 + asus->kbd_led.brightness_get = kbd_led_get; 1889 + asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS; 1890 + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL); 1891 + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all); 2054 1892 1893 + if (asus->kbd_led_avail) { 1894 + asus->kbd_led_wk = led_val; 2055 1895 if (num_rgb_groups != 0) 2056 1896 asus->kbd_led.groups = kbd_rgb_mode_groups; 1897 + } else { 1898 + asus->kbd_led_wk = -1; 1899 + } 2057 1900 2058 - rv = led_classdev_register(&asus->platform_device->dev, 2059 - &asus->kbd_led); 2060 - if (rv) 2061 - goto error; 1901 + scoped_guard(spinlock_irqsave, &asus_ref.lock) { 1902 + asus_ref.asus = asus; 1903 + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners)) 1904 + queue_work(asus->led_workqueue, &asus->kbd_led_work); 2062 1905 } 2063 1906 2064 1907 if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED) ··· 4537 4372 4538 4373 static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) 4539 4374 { 4375 + enum led_brightness led_value; 4540 4376 unsigned int key_value = 1; 4541 4377 bool autorelease = 1; 4542 4378 ··· 4554 4388 return; 4555 4389 } 4556 4390 4391 + scoped_guard(spinlock_irqsave, &asus_ref.lock) 4392 + led_value = asus->kbd_led_wk; 4393 + 4557 4394 if (code == NOTIFY_KBD_BRTUP) { 4558 - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); 4395 + kbd_led_set_by_kbd(asus, led_value + 1); 4559 4396 return; 4560 4397 } 4561 4398 if (code == NOTIFY_KBD_BRTDWN) { 4562 - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); 4399 + kbd_led_set_by_kbd(asus, led_value - 1); 4563 4400 return; 4564 4401 } 4565 4402 if (code == NOTIFY_KBD_BRTTOGGLE) { 4566 - if (asus->kbd_led_wk == asus->kbd_led.max_brightness) 4403 + if (led_value >= ASUS_EV_MAX_BRIGHTNESS) 4567 4404 kbd_led_set_by_kbd(asus, 0); 4568 4405 else 4569 - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); 4406 + kbd_led_set_by_kbd(asus, led_value + 1); 4570 4407 return; 4571 4408 } 4572 4409
-50
include/linux/platform_data/x86/asus-wmi-leds-ids.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 2 - #ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H 3 - #define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H 4 - 5 - #include <linux/dmi.h> 6 - #include <linux/types.h> 7 - 8 - /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ 9 - #if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS) 10 - static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { 11 - { 12 - .matches = { 13 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), 14 - }, 15 - }, 16 - { 17 - .matches = { 18 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), 19 - }, 20 - }, 21 - { 22 - .matches = { 23 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), 24 - }, 25 - }, 26 - { 27 - .matches = { 28 - DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), 29 - }, 30 - }, 31 - { 32 - .matches = { 33 - DMI_MATCH(DMI_BOARD_NAME, "GA403U"), 34 - }, 35 - }, 36 - { 37 - .matches = { 38 - DMI_MATCH(DMI_BOARD_NAME, "GU605M"), 39 - }, 40 - }, 41 - { 42 - .matches = { 43 - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), 44 - }, 45 - }, 46 - { }, 47 - }; 48 - #endif 49 - 50 - #endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */
+28
include/linux/platform_data/x86/asus-wmi.h
··· 172 172 ASUS_WMI_ALLY_MCU_HACK_DISABLED, 173 173 }; 174 174 175 + /* Used to notify hid-asus when asus-wmi changes keyboard backlight */ 176 + struct asus_hid_listener { 177 + struct list_head list; 178 + void (*brightness_set)(struct asus_hid_listener *listener, int brightness); 179 + }; 180 + 181 + enum asus_hid_event { 182 + ASUS_EV_BRTUP, 183 + ASUS_EV_BRTDOWN, 184 + ASUS_EV_BRTTOGGLE, 185 + }; 186 + 187 + #define ASUS_EV_MAX_BRIGHTNESS 3 188 + 175 189 #if IS_REACHABLE(CONFIG_ASUS_WMI) 176 190 void set_ally_mcu_hack(enum asus_ally_mcu_hack status); 177 191 void set_ally_mcu_powersave(bool enabled); 178 192 int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval); 179 193 int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); 180 194 int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); 195 + int asus_hid_register_listener(struct asus_hid_listener *cdev); 196 + void asus_hid_unregister_listener(struct asus_hid_listener *cdev); 197 + int asus_hid_event(enum asus_hid_event event); 181 198 #else 182 199 static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) 183 200 { ··· 212 195 } 213 196 static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, 214 197 u32 *retval) 198 + { 199 + return -ENODEV; 200 + } 201 + static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) 202 + { 203 + return -ENODEV; 204 + } 205 + static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) 206 + { 207 + } 208 + static inline int asus_hid_event(enum asus_hid_event event) 215 209 { 216 210 return -ENODEV; 217 211 }