1 // SPDX-License-Identifier: GPL-2.0+
3 * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk 360)
5 * Aquacomputer devices send HID reports (with ID 0x01) every second to report
8 * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
11 #include <linux/debugfs.h>
12 #include <linux/hid.h>
13 #include <linux/hwmon.h>
14 #include <linux/jiffies.h>
15 #include <linux/module.h>
16 #include <linux/seq_file.h>
17 #include <asm/unaligned.h>
19 #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
20 #define USB_PRODUCT_ID_D5NEXT 0xf00e
21 #define USB_PRODUCT_ID_FARBWERK360 0xf010
23 enum kinds { d5next, farbwerk360 };
25 static const char *const aqc_device_names[] = {
27 [farbwerk360] = "farbwerk360"
30 #define DRIVER_NAME "aquacomputer_d5next"
32 #define STATUS_REPORT_ID 0x01
33 #define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */
34 #define SERIAL_FIRST_PART 3
35 #define SERIAL_SECOND_PART 5
36 #define FIRMWARE_VERSION 13
38 /* Register offsets for the D5 Next pump */
39 #define D5NEXT_POWER_CYCLES 24
41 #define D5NEXT_COOLANT_TEMP 87
43 #define D5NEXT_PUMP_SPEED 116
44 #define D5NEXT_FAN_SPEED 103
46 #define D5NEXT_PUMP_POWER 114
47 #define D5NEXT_FAN_POWER 101
49 #define D5NEXT_PUMP_VOLTAGE 110
50 #define D5NEXT_FAN_VOLTAGE 97
51 #define D5NEXT_5V_VOLTAGE 57
53 #define D5NEXT_PUMP_CURRENT 112
54 #define D5NEXT_FAN_CURRENT 99
56 /* Register offsets for the Farbwerk 360 RGB controller */
57 #define FARBWERK360_NUM_SENSORS 4
58 #define FARBWERK360_SENSOR_START 0x32
59 #define FARBWERK360_SENSOR_SIZE 0x02
60 #define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
62 /* Labels for D5 Next */
63 #define L_D5NEXT_COOLANT_TEMP "Coolant temp"
65 static const char *const label_d5next_speeds[] = {
70 static const char *const label_d5next_power[] = {
75 static const char *const label_d5next_voltages[] = {
81 static const char *const label_d5next_current[] = {
86 /* Labels for Farbwerk 360 temperature sensors */
87 static const char *const label_temp_sensors[] = {
95 struct hid_device *hdev;
96 struct device *hwmon_dev;
97 struct dentry *debugfs;
101 /* General info, same across all devices */
102 u32 serial_number[2];
103 u16 firmware_version;
105 /* D5 Next specific - how many times the device was powered on */
112 u16 voltage_input[3];
113 u16 current_input[2];
115 unsigned long updated;
118 static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
121 const struct aqc_data *priv = data;
125 switch (priv->kind) {
140 switch (priv->kind) {
154 static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
155 int channel, long *val)
157 struct aqc_data *priv = dev_get_drvdata(dev);
159 if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL))
164 if (priv->temp_input[channel] == -ENODATA)
167 *val = priv->temp_input[channel];
170 *val = priv->speed_input[channel];
173 *val = priv->power_input[channel];
176 *val = priv->voltage_input[channel];
179 *val = priv->current_input[channel];
188 static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
189 int channel, const char **str)
191 struct aqc_data *priv = dev_get_drvdata(dev);
195 switch (priv->kind) {
197 *str = L_D5NEXT_COOLANT_TEMP;
200 *str = label_temp_sensors[channel];
207 switch (priv->kind) {
209 *str = label_d5next_speeds[channel];
216 switch (priv->kind) {
218 *str = label_d5next_power[channel];
225 switch (priv->kind) {
227 *str = label_d5next_voltages[channel];
234 switch (priv->kind) {
236 *str = label_d5next_current[channel];
249 static const struct hwmon_ops aqc_hwmon_ops = {
250 .is_visible = aqc_is_visible,
252 .read_string = aqc_read_string,
255 static const struct hwmon_channel_info *aqc_info[] = {
256 HWMON_CHANNEL_INFO(temp,
257 HWMON_T_INPUT | HWMON_T_LABEL,
258 HWMON_T_INPUT | HWMON_T_LABEL,
259 HWMON_T_INPUT | HWMON_T_LABEL,
260 HWMON_T_INPUT | HWMON_T_LABEL),
261 HWMON_CHANNEL_INFO(fan,
262 HWMON_F_INPUT | HWMON_F_LABEL,
263 HWMON_F_INPUT | HWMON_F_LABEL),
264 HWMON_CHANNEL_INFO(power,
265 HWMON_P_INPUT | HWMON_P_LABEL,
266 HWMON_P_INPUT | HWMON_P_LABEL),
267 HWMON_CHANNEL_INFO(in,
268 HWMON_I_INPUT | HWMON_I_LABEL,
269 HWMON_I_INPUT | HWMON_I_LABEL,
270 HWMON_I_INPUT | HWMON_I_LABEL),
271 HWMON_CHANNEL_INFO(curr,
272 HWMON_C_INPUT | HWMON_C_LABEL,
273 HWMON_C_INPUT | HWMON_C_LABEL),
277 static const struct hwmon_chip_info aqc_chip_info = {
278 .ops = &aqc_hwmon_ops,
282 static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
286 struct aqc_data *priv;
288 if (report->id != STATUS_REPORT_ID)
291 priv = hid_get_drvdata(hdev);
293 /* Info provided with every report */
294 priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART);
295 priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
296 priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
298 /* Sensor readings */
299 switch (priv->kind) {
301 priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
303 priv->temp_input[0] = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10;
305 priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED);
306 priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED);
308 priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000;
309 priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000;
311 priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10;
312 priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10;
313 priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
315 priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
316 priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
319 /* Temperature sensor readings */
320 for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
321 sensor_value = get_unaligned_be16(data + FARBWERK360_SENSOR_START +
322 i * FARBWERK360_SENSOR_SIZE);
323 if (sensor_value == FARBWERK360_SENSOR_DISCONNECTED)
324 priv->temp_input[i] = -ENODATA;
326 priv->temp_input[i] = sensor_value * 10;
333 priv->updated = jiffies;
338 #ifdef CONFIG_DEBUG_FS
340 static int serial_number_show(struct seq_file *seqf, void *unused)
342 struct aqc_data *priv = seqf->private;
344 seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]);
348 DEFINE_SHOW_ATTRIBUTE(serial_number);
350 static int firmware_version_show(struct seq_file *seqf, void *unused)
352 struct aqc_data *priv = seqf->private;
354 seq_printf(seqf, "%u\n", priv->firmware_version);
358 DEFINE_SHOW_ATTRIBUTE(firmware_version);
360 static int power_cycles_show(struct seq_file *seqf, void *unused)
362 struct aqc_data *priv = seqf->private;
364 seq_printf(seqf, "%u\n", priv->power_cycles);
368 DEFINE_SHOW_ATTRIBUTE(power_cycles);
370 static void aqc_debugfs_init(struct aqc_data *priv)
374 scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name,
375 dev_name(&priv->hdev->dev));
377 priv->debugfs = debugfs_create_dir(name, NULL);
378 debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
379 debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
381 if (priv->kind == d5next)
382 debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
387 static void aqc_debugfs_init(struct aqc_data *priv)
393 static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
395 struct aqc_data *priv;
398 priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
403 hid_set_drvdata(hdev, priv);
405 priv->updated = jiffies - STATUS_UPDATE_INTERVAL;
407 ret = hid_parse(hdev);
411 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
415 ret = hid_hw_open(hdev);
419 switch (hdev->product) {
420 case USB_PRODUCT_ID_D5NEXT:
423 case USB_PRODUCT_ID_FARBWERK360:
424 priv->kind = farbwerk360;
430 priv->name = aqc_device_names[priv->kind];
432 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv,
433 &aqc_chip_info, NULL);
435 if (IS_ERR(priv->hwmon_dev)) {
436 ret = PTR_ERR(priv->hwmon_dev);
440 aqc_debugfs_init(priv);
451 static void aqc_remove(struct hid_device *hdev)
453 struct aqc_data *priv = hid_get_drvdata(hdev);
455 debugfs_remove_recursive(priv->debugfs);
456 hwmon_device_unregister(priv->hwmon_dev);
462 static const struct hid_device_id aqc_table[] = {
463 { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) },
464 { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
468 MODULE_DEVICE_TABLE(hid, aqc_table);
470 static struct hid_driver aqc_driver = {
472 .id_table = aqc_table,
474 .remove = aqc_remove,
475 .raw_event = aqc_raw_event,
478 static int __init aqc_init(void)
480 return hid_register_driver(&aqc_driver);
483 static void __exit aqc_exit(void)
485 hid_unregister_driver(&aqc_driver);
488 /* Request to initialize after the HID bus to ensure it's not being loaded before */
489 late_initcall(aqc_init);
490 module_exit(aqc_exit);
492 MODULE_LICENSE("GPL");
493 MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
494 MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices");