0007-surface-typecover.patch 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. From 5c0caf4d38d2f819e4637bfff672a612b11543b3 Mon Sep 17 00:00:00 2001
  2. From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
  3. Date: Thu, 5 Nov 2020 13:09:45 +0100
  4. Subject: [PATCH] hid/multitouch: Turn off Type Cover keyboard backlight when
  5. suspending
  6. The Type Cover for Microsoft Surface devices supports a special usb
  7. control request to disable or enable the built-in keyboard backlight.
  8. On Windows, this request happens when putting the device into suspend or
  9. resuming it, without it the backlight of the Type Cover will remain
  10. enabled for some time even though the computer is suspended, which looks
  11. weird to the user.
  12. So add support for this special usb control request to hid-multitouch,
  13. which is the driver that's handling the Type Cover.
  14. The reason we have to use a pm_notifier for this instead of the usual
  15. suspend/resume methods is that those won't get called in case the usb
  16. device is already autosuspended.
  17. Also, if the device is autosuspended, we have to briefly autoresume it
  18. in order to send the request. Doing that should be fine, the usb-core
  19. driver does something similar during suspend inside choose_wakeup().
  20. To make sure we don't send that request to every device but only to
  21. devices which support it, add a new quirk
  22. MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk
  23. is only enabled for the usb id of the Surface Pro 2017 Type Cover, which
  24. is where I confirmed that it's working.
  25. Patchset: surface-typecover
  26. ---
  27. drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++-
  28. 1 file changed, 98 insertions(+), 2 deletions(-)
  29. diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
  30. index 8429ebe7097e..44d48e8bbe1a 100644
  31. --- a/drivers/hid/hid-multitouch.c
  32. +++ b/drivers/hid/hid-multitouch.c
  33. @@ -34,7 +34,10 @@
  34. #include <linux/device.h>
  35. #include <linux/hid.h>
  36. #include <linux/module.h>
  37. +#include <linux/pm_runtime.h>
  38. #include <linux/slab.h>
  39. +#include <linux/suspend.h>
  40. +#include <linux/usb.h>
  41. #include <linux/input/mt.h>
  42. #include <linux/jiffies.h>
  43. #include <linux/string.h>
  44. @@ -47,6 +50,7 @@ MODULE_DESCRIPTION("HID multitouch panels");
  45. MODULE_LICENSE("GPL");
  46. #include "hid-ids.h"
  47. +#include "usbhid/usbhid.h"
  48. /* quirks to control the device */
  49. #define MT_QUIRK_NOT_SEEN_MEANS_UP BIT(0)
  50. @@ -70,12 +74,15 @@ MODULE_LICENSE("GPL");
  51. #define MT_QUIRK_WIN8_PTP_BUTTONS BIT(18)
  52. #define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
  53. #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
  54. +#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(21)
  55. #define MT_INPUTMODE_TOUCHSCREEN 0x02
  56. #define MT_INPUTMODE_TOUCHPAD 0x03
  57. #define MT_BUTTONTYPE_CLICKPAD 0
  58. +#define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086
  59. +
  60. enum latency_mode {
  61. HID_LATENCY_NORMAL = 0,
  62. HID_LATENCY_HIGH = 1,
  63. @@ -167,6 +174,8 @@ struct mt_device {
  64. struct list_head applications;
  65. struct list_head reports;
  66. +
  67. + struct notifier_block pm_notifier;
  68. };
  69. static void mt_post_parse_default_settings(struct mt_device *td,
  70. @@ -208,6 +217,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
  71. #define MT_CLS_GOOGLE 0x0111
  72. #define MT_CLS_RAZER_BLADE_STEALTH 0x0112
  73. #define MT_CLS_SMART_TECH 0x0113
  74. +#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0114
  75. #define MT_DEFAULT_MAXCONTACT 10
  76. #define MT_MAX_MAXCONTACT 250
  77. @@ -367,6 +377,16 @@ static const struct mt_class mt_classes[] = {
  78. MT_QUIRK_CONTACT_CNT_ACCURATE |
  79. MT_QUIRK_SEPARATE_APP_REPORT,
  80. },
  81. + { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
  82. + .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT |
  83. + MT_QUIRK_ALWAYS_VALID |
  84. + MT_QUIRK_IGNORE_DUPLICATES |
  85. + MT_QUIRK_HOVERING |
  86. + MT_QUIRK_CONTACT_CNT_ACCURATE |
  87. + MT_QUIRK_STICKY_FINGERS |
  88. + MT_QUIRK_WIN8_PTP_BUTTONS,
  89. + .export_all_inputs = true
  90. + },
  91. { }
  92. };
  93. @@ -1674,6 +1694,69 @@ static void mt_expired_timeout(struct timer_list *t)
  94. clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
  95. }
  96. +static void get_type_cover_backlight_field(struct hid_device *hdev,
  97. + struct hid_field **field)
  98. +{
  99. + struct hid_report_enum *rep_enum;
  100. + struct hid_report *rep;
  101. + struct hid_field *cur_field;
  102. + int i, j;
  103. +
  104. + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
  105. + list_for_each_entry(rep, &rep_enum->report_list, list) {
  106. + for (i = 0; i < rep->maxfield; i++) {
  107. + cur_field = rep->field[i];
  108. +
  109. + for (j = 0; j < cur_field->maxusage; j++) {
  110. + if (cur_field->usage[j].hid
  111. + == MS_TYPE_COVER_FEATURE_REPORT_USAGE) {
  112. + *field = cur_field;
  113. + return;
  114. + }
  115. + }
  116. + }
  117. + }
  118. +}
  119. +
  120. +static void update_keyboard_backlight(struct hid_device *hdev, bool enabled)
  121. +{
  122. + struct usb_device *udev = hid_to_usb_dev(hdev);
  123. + struct hid_field *field = NULL;
  124. +
  125. + /* Wake up the device in case it's already suspended */
  126. + pm_runtime_get_sync(&udev->dev);
  127. +
  128. + get_type_cover_backlight_field(hdev, &field);
  129. + if (!field) {
  130. + hid_err(hdev, "couldn't find backlight field\n");
  131. + goto out;
  132. + }
  133. +
  134. + field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff;
  135. + hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT);
  136. +
  137. +out:
  138. + pm_runtime_put_sync(&udev->dev);
  139. +}
  140. +
  141. +static int mt_pm_notifier(struct notifier_block *notifier,
  142. + unsigned long pm_event,
  143. + void *unused)
  144. +{
  145. + struct mt_device *td =
  146. + container_of(notifier, struct mt_device, pm_notifier);
  147. + struct hid_device *hdev = td->hdev;
  148. +
  149. + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) {
  150. + if (pm_event == PM_SUSPEND_PREPARE)
  151. + update_keyboard_backlight(hdev, 0);
  152. + else if (pm_event == PM_POST_SUSPEND)
  153. + update_keyboard_backlight(hdev, 1);
  154. + }
  155. +
  156. + return NOTIFY_DONE;
  157. +}
  158. +
  159. static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  160. {
  161. int ret, i;
  162. @@ -1697,6 +1780,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  163. td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
  164. hid_set_drvdata(hdev, td);
  165. + td->pm_notifier.notifier_call = mt_pm_notifier;
  166. + register_pm_notifier(&td->pm_notifier);
  167. +
  168. INIT_LIST_HEAD(&td->applications);
  169. INIT_LIST_HEAD(&td->reports);
  170. @@ -1726,15 +1812,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
  171. timer_setup(&td->release_timer, mt_expired_timeout, 0);
  172. ret = hid_parse(hdev);
  173. - if (ret != 0)
  174. + if (ret != 0) {
  175. + unregister_pm_notifier(&td->pm_notifier);
  176. return ret;
  177. + }
  178. if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
  179. mt_fix_const_fields(hdev, HID_DG_CONTACTID);
  180. ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  181. - if (ret)
  182. + if (ret) {
  183. + unregister_pm_notifier(&td->pm_notifier);
  184. return ret;
  185. + }
  186. ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
  187. if (ret)
  188. @@ -1770,6 +1860,7 @@ static void mt_remove(struct hid_device *hdev)
  189. {
  190. struct mt_device *td = hid_get_drvdata(hdev);
  191. + unregister_pm_notifier(&td->pm_notifier);
  192. del_timer_sync(&td->release_timer);
  193. sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
  194. @@ -2121,6 +2212,11 @@ static const struct hid_device_id mt_devices[] = {
  195. MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
  196. USB_DEVICE_ID_XIROKU_CSR2) },
  197. + /* Microsoft Surface type cover */
  198. + { .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER,
  199. + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
  200. + USB_VENDOR_ID_MICROSOFT, 0x09c0) },
  201. +
  202. /* Google MT devices */
  203. { .driver_data = MT_CLS_GOOGLE,
  204. HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
  205. --
  206. 2.31.0