nzxt-kraken2.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * nzxt-kraken2.c - hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers
  4. *
  5. * The device asynchronously sends HID reports (with id 0x04) twice a second to
  6. * communicate current fan speed, pump speed and coolant temperature. The
  7. * device does not respond to Get_Report requests for this status report.
  8. *
  9. * Copyright 2019-2021 Jonas Malaco <[email protected]>
  10. */
  11. #include <asm/unaligned.h>
  12. #include <linux/hid.h>
  13. #include <linux/hwmon.h>
  14. #include <linux/jiffies.h>
  15. #include <linux/module.h>
  16. #define STATUS_REPORT_ID 0x04
  17. #define STATUS_VALIDITY 2 /* seconds; equivalent to 4 missed updates */
  18. static const char *const kraken2_temp_label[] = {
  19. "Coolant",
  20. };
  21. static const char *const kraken2_fan_label[] = {
  22. "Fan",
  23. "Pump",
  24. };
  25. struct kraken2_priv_data {
  26. struct hid_device *hid_dev;
  27. struct device *hwmon_dev;
  28. s32 temp_input[1];
  29. u16 fan_input[2];
  30. unsigned long updated; /* jiffies */
  31. };
  32. static umode_t kraken2_is_visible(const void *data,
  33. enum hwmon_sensor_types type,
  34. u32 attr, int channel)
  35. {
  36. return 0444;
  37. }
  38. static int kraken2_read(struct device *dev, enum hwmon_sensor_types type,
  39. u32 attr, int channel, long *val)
  40. {
  41. struct kraken2_priv_data *priv = dev_get_drvdata(dev);
  42. if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ))
  43. return -ENODATA;
  44. switch (type) {
  45. case hwmon_temp:
  46. *val = priv->temp_input[channel];
  47. break;
  48. case hwmon_fan:
  49. *val = priv->fan_input[channel];
  50. break;
  51. default:
  52. return -EOPNOTSUPP; /* unreachable */
  53. }
  54. return 0;
  55. }
  56. static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type,
  57. u32 attr, int channel, const char **str)
  58. {
  59. switch (type) {
  60. case hwmon_temp:
  61. *str = kraken2_temp_label[channel];
  62. break;
  63. case hwmon_fan:
  64. *str = kraken2_fan_label[channel];
  65. break;
  66. default:
  67. return -EOPNOTSUPP; /* unreachable */
  68. }
  69. return 0;
  70. }
  71. static const struct hwmon_ops kraken2_hwmon_ops = {
  72. .is_visible = kraken2_is_visible,
  73. .read = kraken2_read,
  74. .read_string = kraken2_read_string,
  75. };
  76. static const struct hwmon_channel_info *kraken2_info[] = {
  77. HWMON_CHANNEL_INFO(temp,
  78. HWMON_T_INPUT | HWMON_T_LABEL),
  79. HWMON_CHANNEL_INFO(fan,
  80. HWMON_F_INPUT | HWMON_F_LABEL,
  81. HWMON_F_INPUT | HWMON_F_LABEL),
  82. NULL
  83. };
  84. static const struct hwmon_chip_info kraken2_chip_info = {
  85. .ops = &kraken2_hwmon_ops,
  86. .info = kraken2_info,
  87. };
  88. static int kraken2_raw_event(struct hid_device *hdev,
  89. struct hid_report *report, u8 *data, int size)
  90. {
  91. struct kraken2_priv_data *priv;
  92. if (size < 7 || report->id != STATUS_REPORT_ID)
  93. return 0;
  94. priv = hid_get_drvdata(hdev);
  95. /*
  96. * The fractional byte of the coolant temperature has been observed to
  97. * be in the interval [1,9], but some of these steps are also
  98. * consistently skipped for certain integer parts.
  99. *
  100. * For the lack of a better idea, assume that the resolution is 0.1°C,
  101. * and that the missing steps are artifacts of how the firmware
  102. * processes the raw sensor data.
  103. */
  104. priv->temp_input[0] = data[1] * 1000 + data[2] * 100;
  105. priv->fan_input[0] = get_unaligned_be16(data + 3);
  106. priv->fan_input[1] = get_unaligned_be16(data + 5);
  107. priv->updated = jiffies;
  108. return 0;
  109. }
  110. static int kraken2_probe(struct hid_device *hdev,
  111. const struct hid_device_id *id)
  112. {
  113. struct kraken2_priv_data *priv;
  114. int ret;
  115. priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
  116. if (!priv)
  117. return -ENOMEM;
  118. priv->hid_dev = hdev;
  119. hid_set_drvdata(hdev, priv);
  120. /*
  121. * Initialize ->updated to STATUS_VALIDITY seconds in the past, making
  122. * the initial empty data invalid for kraken2_read without the need for
  123. * a special case there.
  124. */
  125. priv->updated = jiffies - STATUS_VALIDITY * HZ;
  126. ret = hid_parse(hdev);
  127. if (ret) {
  128. hid_err(hdev, "hid parse failed with %d\n", ret);
  129. return ret;
  130. }
  131. /*
  132. * Enable hidraw so existing user-space tools can continue to work.
  133. */
  134. ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
  135. if (ret) {
  136. hid_err(hdev, "hid hw start failed with %d\n", ret);
  137. return ret;
  138. }
  139. ret = hid_hw_open(hdev);
  140. if (ret) {
  141. hid_err(hdev, "hid hw open failed with %d\n", ret);
  142. goto fail_and_stop;
  143. }
  144. priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "kraken2",
  145. priv, &kraken2_chip_info,
  146. NULL);
  147. if (IS_ERR(priv->hwmon_dev)) {
  148. ret = PTR_ERR(priv->hwmon_dev);
  149. hid_err(hdev, "hwmon registration failed with %d\n", ret);
  150. goto fail_and_close;
  151. }
  152. return 0;
  153. fail_and_close:
  154. hid_hw_close(hdev);
  155. fail_and_stop:
  156. hid_hw_stop(hdev);
  157. return ret;
  158. }
  159. static void kraken2_remove(struct hid_device *hdev)
  160. {
  161. struct kraken2_priv_data *priv = hid_get_drvdata(hdev);
  162. hwmon_device_unregister(priv->hwmon_dev);
  163. hid_hw_close(hdev);
  164. hid_hw_stop(hdev);
  165. }
  166. static const struct hid_device_id kraken2_table[] = {
  167. { HID_USB_DEVICE(0x1e71, 0x170e) }, /* NZXT Kraken X42/X52/X62/X72 */
  168. { }
  169. };
  170. MODULE_DEVICE_TABLE(hid, kraken2_table);
  171. static struct hid_driver kraken2_driver = {
  172. .name = "nzxt-kraken2",
  173. .id_table = kraken2_table,
  174. .probe = kraken2_probe,
  175. .remove = kraken2_remove,
  176. .raw_event = kraken2_raw_event,
  177. };
  178. static int __init kraken2_init(void)
  179. {
  180. return hid_register_driver(&kraken2_driver);
  181. }
  182. static void __exit kraken2_exit(void)
  183. {
  184. hid_unregister_driver(&kraken2_driver);
  185. }
  186. /*
  187. * When compiled into the kernel, initialize after the hid bus.
  188. */
  189. late_initcall(kraken2_init);
  190. module_exit(kraken2_exit);
  191. MODULE_LICENSE("GPL");
  192. MODULE_AUTHOR("Jonas Malaco <[email protected]>");
  193. MODULE_DESCRIPTION("Hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers");