hid-google-hammer.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * HID driver for Google Hammer device.
  4. *
  5. * Copyright (c) 2017 Google Inc.
  6. * Author: Wei-Ning Huang <wnhuang@google.com>
  7. */
  8. /*
  9. * This program is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by the Free
  11. * Software Foundation; either version 2 of the License, or (at your option)
  12. * any later version.
  13. */
  14. #include <linux/hid.h>
  15. #include <linux/leds.h>
  16. #include <linux/module.h>
  17. #include "hid-ids.h"
  18. #define MAX_BRIGHTNESS 100
  19. /* HID usage for keyboard backlight (Alphanumeric display brightness) */
  20. #define HID_AD_BRIGHTNESS 0x00140046
  21. struct hammer_kbd_leds {
  22. struct led_classdev cdev;
  23. struct hid_device *hdev;
  24. u8 buf[2] ____cacheline_aligned;
  25. };
  26. static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
  27. enum led_brightness br)
  28. {
  29. struct hammer_kbd_leds *led = container_of(cdev,
  30. struct hammer_kbd_leds,
  31. cdev);
  32. int ret;
  33. led->buf[0] = 0;
  34. led->buf[1] = br;
  35. /*
  36. * Request USB HID device to be in Full On mode, so that sending
  37. * hardware output report and hardware raw request won't fail.
  38. */
  39. ret = hid_hw_power(led->hdev, PM_HINT_FULLON);
  40. if (ret < 0) {
  41. hid_err(led->hdev, "failed: device not resumed %d\n", ret);
  42. return ret;
  43. }
  44. ret = hid_hw_output_report(led->hdev, led->buf, sizeof(led->buf));
  45. if (ret == -ENOSYS)
  46. ret = hid_hw_raw_request(led->hdev, 0, led->buf,
  47. sizeof(led->buf),
  48. HID_OUTPUT_REPORT,
  49. HID_REQ_SET_REPORT);
  50. if (ret < 0)
  51. hid_err(led->hdev, "failed to set keyboard backlight: %d\n",
  52. ret);
  53. /* Request USB HID device back to Normal Mode. */
  54. hid_hw_power(led->hdev, PM_HINT_NORMAL);
  55. return ret;
  56. }
  57. static int hammer_register_leds(struct hid_device *hdev)
  58. {
  59. struct hammer_kbd_leds *kbd_backlight;
  60. kbd_backlight = devm_kzalloc(&hdev->dev,
  61. sizeof(*kbd_backlight),
  62. GFP_KERNEL);
  63. if (!kbd_backlight)
  64. return -ENOMEM;
  65. kbd_backlight->hdev = hdev;
  66. kbd_backlight->cdev.name = "hammer::kbd_backlight";
  67. kbd_backlight->cdev.max_brightness = MAX_BRIGHTNESS;
  68. kbd_backlight->cdev.brightness_set_blocking =
  69. hammer_kbd_brightness_set_blocking;
  70. kbd_backlight->cdev.flags = LED_HW_PLUGGABLE;
  71. /* Set backlight to 0% initially. */
  72. hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
  73. return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
  74. }
  75. static int hammer_input_configured(struct hid_device *hdev,
  76. struct hid_input *hi)
  77. {
  78. struct list_head *report_list =
  79. &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
  80. struct hid_report *report;
  81. if (list_empty(report_list))
  82. return 0;
  83. report = list_first_entry(report_list, struct hid_report, list);
  84. if (report->maxfield == 1 &&
  85. report->field[0]->application == HID_GD_KEYBOARD &&
  86. report->field[0]->maxusage == 1 &&
  87. report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) {
  88. int err = hammer_register_leds(hdev);
  89. if (err)
  90. hid_warn(hdev,
  91. "Failed to register keyboard backlight: %d\n",
  92. err);
  93. }
  94. return 0;
  95. }
  96. static const struct hid_device_id hammer_devices[] = {
  97. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  98. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_DON) },
  99. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  100. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
  101. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  102. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) },
  103. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  104. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MASTERBALL) },
  105. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  106. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MOONBALL) },
  107. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  108. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
  109. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  110. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) },
  111. { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
  112. USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WHISKERS) },
  113. { }
  114. };
  115. MODULE_DEVICE_TABLE(hid, hammer_devices);
  116. static struct hid_driver hammer_driver = {
  117. .name = "hammer",
  118. .id_table = hammer_devices,
  119. .input_configured = hammer_input_configured,
  120. };
  121. module_hid_driver(hammer_driver);
  122. MODULE_LICENSE("GPL");