123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- From 5c0caf4d38d2f819e4637bfff672a612b11543b3 Mon Sep 17 00:00:00 2001
- From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
- Date: Thu, 5 Nov 2020 13:09:45 +0100
- Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
- suspending
- The Type Cover for Microsoft Surface devices supports a special usb
- control request to disable or enable the built-in keyboard backlight.
- On Windows, this request happens when putting the device into suspend or
- resuming it, without it the backlight of the Type Cover will remain
- enabled for some time even though the computer is suspended, which looks
- weird to the user.
- So add support for this special usb control request to hid-multitouch,
- which is the driver that's handling the Type Cover.
- The reason we have to use a pm_notifier for this instead of the usual
- suspend/resume methods is that those won't get called in case the usb
- device is already autosuspended.
- Also, if the device is autosuspended, we have to briefly autoresume it
- in order to send the request. Doing that should be fine, the usb-core
- driver does something similar during suspend inside choose_wakeup().
- To make sure we don't send that request to every device but only to
- devices which support it, add a new quirk
- MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk
- is only enabled for the usb id of the Surface Pro 2017 Type Cover, which
- is where I confirmed that it's working.
- Patchset: surface-typecover
- ---
- drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++-
- 1 file changed, 98 insertions(+), 2 deletions(-)
- diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
- index 8429ebe7097e..44d48e8bbe1a 100644
- --- a/drivers/hid/hid-multitouch.c
- +++ b/drivers/hid/hid-multitouch.c
- @@ -34,7 +34,10 @@
- #include <linux/device.h>
- #include <linux/hid.h>
- #include <linux/module.h>
- +#include <linux/pm_runtime.h>
- #include <linux/slab.h>
- +#include <linux/suspend.h>
- +#include <linux/usb.h>
- #include <linux/input/mt.h>
- #include <linux/jiffies.h>
- #include <linux/string.h>
- @@ -47,6 +50,7 @@ MODULE_DESCRIPTION("HID multitouch panels");
- MODULE_LICENSE("GPL");
-
- #include "hid-ids.h"
- +#include "usbhid/usbhid.h"
-
- /* quirks to control the device */
- #define MT_QUIRK_NOT_SEEN_MEANS_UP BIT(0)
- @@ -70,12 +74,15 @@ MODULE_LICENSE("GPL");
- #define MT_QUIRK_WIN8_PTP_BUTTONS BIT(18)
- #define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
- #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
- +#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(21)
-
- #define MT_INPUTMODE_TOUCHSCREEN 0x02
- #define MT_INPUTMODE_TOUCHPAD 0x03
-
- #define MT_BUTTONTYPE_CLICKPAD 0
-
- +#define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086
- +
- enum latency_mode {
- HID_LATENCY_NORMAL = 0,
- HID_LATENCY_HIGH = 1,
- @@ -167,6 +174,8 @@ struct mt_device {
-
- struct list_head applications;
- struct list_head reports;
- +
- + struct notifier_block pm_notifier;
- };
-
- static void mt_post_parse_default_settings(struct mt_device *td,
- @@ -208,6 +217,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
- #define MT_CLS_GOOGLE 0x0111
- #define MT_CLS_RAZER_BLADE_STEALTH 0x0112
- #define MT_CLS_SMART_TECH 0x0113
- +#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0114
-
- #define MT_DEFAULT_MAXCONTACT 10
- #define MT_MAX_MAXCONTACT 250
- @@ -367,6 +377,16 @@ static const struct mt_class mt_classes[] = {
- MT_QUIRK_CONTACT_CNT_ACCURATE |
- MT_QUIRK_SEPARATE_APP_REPORT,
- },
- + { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
- + .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT |
- + MT_QUIRK_ALWAYS_VALID |
- + MT_QUIRK_IGNORE_DUPLICATES |
- + MT_QUIRK_HOVERING |
- + MT_QUIRK_CONTACT_CNT_ACCURATE |
- + MT_QUIRK_STICKY_FINGERS |
- + MT_QUIRK_WIN8_PTP_BUTTONS,
- + .export_all_inputs = true
- + },
- { }
- };
-
- @@ -1674,6 +1694,69 @@ static void mt_expired_timeout(struct timer_list *t)
- clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
- }
-
- +static void get_type_cover_backlight_field(struct hid_device *hdev,
- + struct hid_field **field)
- +{
- + struct hid_report_enum *rep_enum;
- + struct hid_report *rep;
- + struct hid_field *cur_field;
- + int i, j;
- +
- + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
- + list_for_each_entry(rep, &rep_enum->report_list, list) {
- + for (i = 0; i < rep->maxfield; i++) {
- + cur_field = rep->field[i];
- +
- + for (j = 0; j < cur_field->maxusage; j++) {
- + if (cur_field->usage[j].hid
- + == MS_TYPE_COVER_FEATURE_REPORT_USAGE) {
- + *field = cur_field;
- + return;
- + }
- + }
- + }
- + }
- +}
- +
- +static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
- +{
- + struct usb_device *udev = hid_to_usb_dev(hdev);
- + struct hid_field *field = NULL;
- +
- + /* Wake up the device in case it's already suspended */
- + pm_runtime_get_sync(&udev->dev);
- +
- + get_type_cover_backlight_field(hdev, &field);
- + if (!field) {
- + hid_err(hdev, "couldn't find backlight field\n");
- + goto out;
- + }
- +
- + field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff;
- + hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT);
- +
- +out:
- + pm_runtime_put_sync(&udev->dev);
- +}
- +
- +static int mt_pm_notifier(struct notifier_block *notifier,
- + unsigned long pm_event,
- + void *unused)
- +{
- + struct mt_device *td =
- + container_of(notifier, struct mt_device, pm_notifier);
- + struct hid_device *hdev = td->hdev;
- +
- + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) {
- + if (pm_event == PM_SUSPEND_PREPARE)
- + update_keyboard_backlight(hdev, 0);
- + else if (pm_event == PM_POST_SUSPEND)
- + update_keyboard_backlight(hdev, 1);
- + }
- +
- + return NOTIFY_DONE;
- +}
- +
- static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
- {
- int ret, i;
- @@ -1697,6 +1780,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
- td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
- hid_set_drvdata(hdev, td);
-
- + td->pm_notifier.notifier_call = mt_pm_notifier;
- + register_pm_notifier(&td->pm_notifier);
- +
- INIT_LIST_HEAD(&td->applications);
- INIT_LIST_HEAD(&td->reports);
-
- @@ -1726,15 +1812,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
- timer_setup(&td->release_timer, mt_expired_timeout, 0);
-
- ret = hid_parse(hdev);
- - if (ret != 0)
- + if (ret != 0) {
- + unregister_pm_notifier(&td->pm_notifier);
- return ret;
- + }
-
- if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
- mt_fix_const_fields(hdev, HID_DG_CONTACTID);
-
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- - if (ret)
- + if (ret) {
- + unregister_pm_notifier(&td->pm_notifier);
- return ret;
- + }
-
- ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
- if (ret)
- @@ -1770,6 +1860,7 @@ static void mt_remove(struct hid_device *hdev)
- {
- struct mt_device *td = hid_get_drvdata(hdev);
-
- + unregister_pm_notifier(&td->pm_notifier);
- del_timer_sync(&td->release_timer);
-
- sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
- @@ -2121,6 +2212,11 @@ static const struct hid_device_id mt_devices[] = {
- MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
- USB_DEVICE_ID_XIROKU_CSR2) },
-
- + /* Microsoft Surface type cover */
- + { .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
- + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
- + USB_VENDOR_ID_MICROSOFT, 0x09c0) },
- +
- /* Google MT devices */
- { .driver_data = MT_CLS_GOOGLE,
- HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
- --
- 2.31.0
|