Commit 2fd3eec1 authored by Aleksa Savic's avatar Aleksa Savic Committed by Guenter Roeck

hwmon: (aquacomputer_d5next) Add support for Aquacomputer Farbwerk 360

Extend aquacomputer_d5next driver to expose hardware temperature sensors
of the Aquacomputer Farbwerk 360 RGB controller, which communicates through
a proprietary USB HID protocol.

Four temperature sensors are available. Additionally, serial number and
firmware version are exposed through debugfs.

This driver has been tested on x86_64.
Signed-off-by: default avatarAleksa Savic <savicaleksa83@gmail.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent aa9f833d
...@@ -6,22 +6,21 @@ Kernel driver aquacomputer-d5next ...@@ -6,22 +6,21 @@ Kernel driver aquacomputer-d5next
Supported devices: Supported devices:
* Aquacomputer D5 Next watercooling pump * Aquacomputer D5 Next watercooling pump
* Aquacomputer Farbwerk 360 RGB controller
Author: Aleksa Savic Author: Aleksa Savic
Description Description
----------- -----------
This driver exposes hardware sensors of the Aquacomputer D5 Next watercooling This driver exposes hardware sensors of listed Aquacomputer devices, which
pump, which communicates through a proprietary USB HID protocol. communicate through proprietary USB HID protocols.
Available sensors are pump and fan speed, power, voltage and current, as For the D5 Next pump, available sensors are pump and fan speed, power, voltage
well as coolant temperature. Also available through debugfs are the serial and current, as well as coolant temperature. Also available through debugfs are
number, firmware version and power-on count. the serial number, firmware version and power-on count. Attaching a fan to it is
optional and allows it to be controlled using temperature curves directly from the
Attaching a fan is optional and allows it to be controlled using temperature pump. If it's not connected, the fan-related sensors will report zeroes.
curves directly from the pump. If it's not connected, the fan-related sensors
will report zeroes.
The pump can be configured either through software or via its physical The pump can be configured either through software or via its physical
interface. Configuring the pump through this driver is not implemented, as it interface. Configuring the pump through this driver is not implemented, as it
...@@ -29,33 +28,31 @@ seems to require sending it a complete configuration. That includes addressable ...@@ -29,33 +28,31 @@ seems to require sending it a complete configuration. That includes addressable
RGB LEDs, for which there is no standard sysfs interface. Thus, that task is RGB LEDs, for which there is no standard sysfs interface. Thus, that task is
better suited for userspace tools. better suited for userspace tools.
The Farbwerk 360 exposes four temperature sensors. Depending on the device,
not all sysfs and debugfs entries will be available.
Usage notes Usage notes
----------- -----------
The pump communicates via HID reports. The driver is loaded automatically by The devices communicate via HID reports. The driver is loaded automatically by
the kernel and supports hotswapping. the kernel and supports hotswapping.
Sysfs entries Sysfs entries
------------- -------------
============ ============================================= ================ =============================================
temp1_input Coolant temperature (in millidegrees Celsius) temp[1-4]_input Temperature sensors (in millidegrees Celsius)
fan1_input Pump speed (in RPM) fan[1-2]_input Pump/fan speed (in RPM)
fan2_input Fan speed (in RPM) power[1-2]_input Pump/fan power (in micro Watts)
power1_input Pump power (in micro Watts) in[0-2]_input Pump/fan voltage (in milli Volts)
power2_input Fan power (in micro Watts) curr[1-2]_input Pump/fan current (in milli Amperes)
in0_input Pump voltage (in milli Volts) ================ =============================================
in1_input Fan voltage (in milli Volts)
in2_input +5V rail voltage (in milli Volts)
curr1_input Pump current (in milli Amperes)
curr2_input Fan current (in milli Amperes)
============ =============================================
Debugfs entries Debugfs entries
--------------- ---------------
================ =============================================== ================ =================================================
serial_number Serial number of the pump serial_number Serial number of the device
firmware_version Version of installed firmware firmware_version Version of installed firmware
power_cycles Count of how many times the pump was powered on power_cycles Count of how many times the device was powered on
================ =============================================== ================ =================================================
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
/* /*
* hwmon driver for Aquacomputer D5 Next watercooling pump * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk 360)
* *
* The D5 Next sends HID reports (with ID 0x01) every second to report sensor values * Aquacomputer devices send HID reports (with ID 0x01) every second to report
* (coolant temperature, pump and fan speed, voltage, current and power). It responds to * sensor values.
* Get_Report requests, but returns a dummy value of no use.
* *
* Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
*/ */
#include <asm/unaligned.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/unaligned.h>
#define DRIVER_NAME "aquacomputer-d5next" #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
#define USB_PRODUCT_ID_D5NEXT 0xf00e
#define USB_PRODUCT_ID_FARBWERK360 0xf010
#define D5NEXT_STATUS_REPORT_ID 0x01 enum kinds { d5next, farbwerk360 };
#define D5NEXT_STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */
/* Register offsets for the D5 Next pump */ static const char *const aqc_device_names[] = {
[d5next] = "d5next",
[farbwerk360] = "farbwerk360"
};
#define DRIVER_NAME "aquacomputer_d5next"
#define STATUS_REPORT_ID 0x01
#define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */
#define SERIAL_FIRST_PART 3
#define SERIAL_SECOND_PART 5
#define FIRMWARE_VERSION 13
#define D5NEXT_SERIAL_FIRST_PART 3 /* Register offsets for the D5 Next pump */
#define D5NEXT_SERIAL_SECOND_PART 5
#define D5NEXT_FIRMWARE_VERSION 13
#define D5NEXT_POWER_CYCLES 24 #define D5NEXT_POWER_CYCLES 24
#define D5NEXT_COOLANT_TEMP 87 #define D5NEXT_COOLANT_TEMP 87
...@@ -44,76 +53,118 @@ ...@@ -44,76 +53,118 @@
#define D5NEXT_PUMP_CURRENT 112 #define D5NEXT_PUMP_CURRENT 112
#define D5NEXT_FAN_CURRENT 99 #define D5NEXT_FAN_CURRENT 99
/* Labels for provided values */ /* Register offsets for the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4
#define L_COOLANT_TEMP "Coolant temp" #define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_SENSOR_SIZE 0x02
#define L_PUMP_SPEED "Pump speed" #define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
#define L_FAN_SPEED "Fan speed"
#define L_PUMP_POWER "Pump power" /* Labels for D5 Next */
#define L_FAN_POWER "Fan power" #define L_D5NEXT_COOLANT_TEMP "Coolant temp"
#define L_PUMP_VOLTAGE "Pump voltage" static const char *const label_d5next_speeds[] = {
#define L_FAN_VOLTAGE "Fan voltage" "Pump speed",
#define L_5V_VOLTAGE "+5V voltage" "Fan speed"
};
#define L_PUMP_CURRENT "Pump current"
#define L_FAN_CURRENT "Fan current"
static const char *const label_speeds[] = { static const char *const label_d5next_power[] = {
L_PUMP_SPEED, "Pump power",
L_FAN_SPEED, "Fan power"
}; };
static const char *const label_power[] = { static const char *const label_d5next_voltages[] = {
L_PUMP_POWER, "Pump voltage",
L_FAN_POWER, "Fan voltage",
"+5V voltage"
}; };
static const char *const label_voltages[] = { static const char *const label_d5next_current[] = {
L_PUMP_VOLTAGE, "Pump current",
L_FAN_VOLTAGE, "Fan current"
L_5V_VOLTAGE,
}; };
static const char *const label_current[] = { /* Labels for Farbwerk 360 temperature sensors */
L_PUMP_CURRENT, static const char *const label_temp_sensors[] = {
L_FAN_CURRENT, "Sensor 1",
"Sensor 2",
"Sensor 3",
"Sensor 4"
}; };
struct d5next_data { struct aqc_data {
struct hid_device *hdev; struct hid_device *hdev;
struct device *hwmon_dev; struct device *hwmon_dev;
struct dentry *debugfs; struct dentry *debugfs;
s32 temp_input; enum kinds kind;
const char *name;
/* General info, same across all devices */
u32 serial_number[2];
u16 firmware_version;
/* D5 Next specific - how many times the device was powered on */
u32 power_cycles;
/* Sensor values */
s32 temp_input[4];
u16 speed_input[2]; u16 speed_input[2];
u32 power_input[2]; u32 power_input[2];
u16 voltage_input[3]; u16 voltage_input[3];
u16 current_input[2]; u16 current_input[2];
u32 serial_number[2];
u16 firmware_version;
u32 power_cycles; /* How many times the device was powered on */
unsigned long updated; unsigned long updated;
}; };
static umode_t d5next_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
int channel) int channel)
{ {
const struct aqc_data *priv = data;
switch (type) {
case hwmon_temp:
switch (priv->kind) {
case d5next:
if (channel == 0)
return 0444; return 0444;
break;
case farbwerk360:
return 0444;
default:
break;
}
break;
case hwmon_fan:
case hwmon_power:
case hwmon_in:
case hwmon_curr:
switch (priv->kind) {
case d5next:
return 0444;
default:
break;
}
break;
default:
break;
}
return 0;
} }
static int d5next_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
long *val) int channel, long *val)
{ {
struct d5next_data *priv = dev_get_drvdata(dev); struct aqc_data *priv = dev_get_drvdata(dev);
if (time_after(jiffies, priv->updated + D5NEXT_STATUS_UPDATE_INTERVAL)) if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL))
return -ENODATA; return -ENODATA;
switch (type) { switch (type) {
case hwmon_temp: case hwmon_temp:
*val = priv->temp_input; if (priv->temp_input[channel] == -ENODATA)
return -ENODATA;
*val = priv->temp_input[channel];
break; break;
case hwmon_fan: case hwmon_fan:
*val = priv->speed_input[channel]; *val = priv->speed_input[channel];
...@@ -134,24 +185,59 @@ static int d5next_read(struct device *dev, enum hwmon_sensor_types type, u32 att ...@@ -134,24 +185,59 @@ static int d5next_read(struct device *dev, enum hwmon_sensor_types type, u32 att
return 0; return 0;
} }
static int d5next_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **str) int channel, const char **str)
{ {
struct aqc_data *priv = dev_get_drvdata(dev);
switch (type) { switch (type) {
case hwmon_temp: case hwmon_temp:
*str = L_COOLANT_TEMP; switch (priv->kind) {
case d5next:
*str = L_D5NEXT_COOLANT_TEMP;
break;
case farbwerk360:
*str = label_temp_sensors[channel];
break;
default:
break;
}
break; break;
case hwmon_fan: case hwmon_fan:
*str = label_speeds[channel]; switch (priv->kind) {
case d5next:
*str = label_d5next_speeds[channel];
break;
default:
break;
}
break; break;
case hwmon_power: case hwmon_power:
*str = label_power[channel]; switch (priv->kind) {
case d5next:
*str = label_d5next_power[channel];
break;
default:
break;
}
break; break;
case hwmon_in: case hwmon_in:
*str = label_voltages[channel]; switch (priv->kind) {
case d5next:
*str = label_d5next_voltages[channel];
break;
default:
break;
}
break; break;
case hwmon_curr: case hwmon_curr:
*str = label_current[channel]; switch (priv->kind) {
case d5next:
*str = label_d5next_current[channel];
break;
default:
break;
}
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -160,47 +246,61 @@ static int d5next_read_string(struct device *dev, enum hwmon_sensor_types type, ...@@ -160,47 +246,61 @@ static int d5next_read_string(struct device *dev, enum hwmon_sensor_types type,
return 0; return 0;
} }
static const struct hwmon_ops d5next_hwmon_ops = { static const struct hwmon_ops aqc_hwmon_ops = {
.is_visible = d5next_is_visible, .is_visible = aqc_is_visible,
.read = d5next_read, .read = aqc_read,
.read_string = d5next_read_string, .read_string = aqc_read_string,
}; };
static const struct hwmon_channel_info *d5next_info[] = { static const struct hwmon_channel_info *aqc_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), HWMON_CHANNEL_INFO(temp,
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL), HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL, HWMON_P_INPUT | HWMON_P_LABEL), HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL), HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL), HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
NULL NULL
}; };
static const struct hwmon_chip_info d5next_chip_info = { static const struct hwmon_chip_info aqc_chip_info = {
.ops = &d5next_hwmon_ops, .ops = &aqc_hwmon_ops,
.info = d5next_info, .info = aqc_info,
}; };
static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
int size)
{ {
struct d5next_data *priv; int i, sensor_value;
struct aqc_data *priv;
if (report->id != D5NEXT_STATUS_REPORT_ID) if (report->id != STATUS_REPORT_ID)
return 0; return 0;
priv = hid_get_drvdata(hdev); priv = hid_get_drvdata(hdev);
/* Info provided with every report */ /* Info provided with every report */
priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART);
priv->serial_number[0] = get_unaligned_be16(data + D5NEXT_SERIAL_FIRST_PART); priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
priv->serial_number[1] = get_unaligned_be16(data + D5NEXT_SERIAL_SECOND_PART); priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
priv->firmware_version = get_unaligned_be16(data + D5NEXT_FIRMWARE_VERSION);
priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
/* Sensor readings */ /* Sensor readings */
switch (priv->kind) {
case d5next:
priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
priv->temp_input = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10; priv->temp_input[0] = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10;
priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED); priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED);
priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED); priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED);
...@@ -214,6 +314,21 @@ static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report, ...@@ -214,6 +314,21 @@ static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report,
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT); priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT); priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
break;
case farbwerk360:
/* Temperature sensor readings */
for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
sensor_value = get_unaligned_be16(data + FARBWERK360_SENSOR_START +
i * FARBWERK360_SENSOR_SIZE);
if (sensor_value == FARBWERK360_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
break;
default:
break;
}
priv->updated = jiffies; priv->updated = jiffies;
...@@ -224,7 +339,7 @@ static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report, ...@@ -224,7 +339,7 @@ static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report,
static int serial_number_show(struct seq_file *seqf, void *unused) static int serial_number_show(struct seq_file *seqf, void *unused)
{ {
struct d5next_data *priv = seqf->private; struct aqc_data *priv = seqf->private;
seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]);
...@@ -234,7 +349,7 @@ DEFINE_SHOW_ATTRIBUTE(serial_number); ...@@ -234,7 +349,7 @@ DEFINE_SHOW_ATTRIBUTE(serial_number);
static int firmware_version_show(struct seq_file *seqf, void *unused) static int firmware_version_show(struct seq_file *seqf, void *unused)
{ {
struct d5next_data *priv = seqf->private; struct aqc_data *priv = seqf->private;
seq_printf(seqf, "%u\n", priv->firmware_version); seq_printf(seqf, "%u\n", priv->firmware_version);
...@@ -244,7 +359,7 @@ DEFINE_SHOW_ATTRIBUTE(firmware_version); ...@@ -244,7 +359,7 @@ DEFINE_SHOW_ATTRIBUTE(firmware_version);
static int power_cycles_show(struct seq_file *seqf, void *unused) static int power_cycles_show(struct seq_file *seqf, void *unused)
{ {
struct d5next_data *priv = seqf->private; struct aqc_data *priv = seqf->private;
seq_printf(seqf, "%u\n", priv->power_cycles); seq_printf(seqf, "%u\n", priv->power_cycles);
...@@ -252,29 +367,32 @@ static int power_cycles_show(struct seq_file *seqf, void *unused) ...@@ -252,29 +367,32 @@ static int power_cycles_show(struct seq_file *seqf, void *unused)
} }
DEFINE_SHOW_ATTRIBUTE(power_cycles); DEFINE_SHOW_ATTRIBUTE(power_cycles);
static void d5next_debugfs_init(struct d5next_data *priv) static void aqc_debugfs_init(struct aqc_data *priv)
{ {
char name[32]; char name[64];
scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name,
dev_name(&priv->hdev->dev));
priv->debugfs = debugfs_create_dir(name, NULL); priv->debugfs = debugfs_create_dir(name, NULL);
debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
if (priv->kind == d5next)
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
} }
#else #else
static void d5next_debugfs_init(struct d5next_data *priv) static void aqc_debugfs_init(struct aqc_data *priv)
{ {
} }
#endif #endif
static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id) static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
struct d5next_data *priv; struct aqc_data *priv;
int ret; int ret;
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
...@@ -284,7 +402,7 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -284,7 +402,7 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->hdev = hdev; priv->hdev = hdev;
hid_set_drvdata(hdev, priv); hid_set_drvdata(hdev, priv);
priv->updated = jiffies - D5NEXT_STATUS_UPDATE_INTERVAL; priv->updated = jiffies - STATUS_UPDATE_INTERVAL;
ret = hid_parse(hdev); ret = hid_parse(hdev);
if (ret) if (ret)
...@@ -298,15 +416,28 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -298,15 +416,28 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret) if (ret)
goto fail_and_stop; goto fail_and_stop;
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "d5next", priv, switch (hdev->product) {
&d5next_chip_info, NULL); case USB_PRODUCT_ID_D5NEXT:
priv->kind = d5next;
break;
case USB_PRODUCT_ID_FARBWERK360:
priv->kind = farbwerk360;
break;
default:
break;
}
priv->name = aqc_device_names[priv->kind];
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv,
&aqc_chip_info, NULL);
if (IS_ERR(priv->hwmon_dev)) { if (IS_ERR(priv->hwmon_dev)) {
ret = PTR_ERR(priv->hwmon_dev); ret = PTR_ERR(priv->hwmon_dev);
goto fail_and_close; goto fail_and_close;
} }
d5next_debugfs_init(priv); aqc_debugfs_init(priv);
return 0; return 0;
...@@ -317,9 +448,9 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -317,9 +448,9 @@ static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret; return ret;
} }
static void d5next_remove(struct hid_device *hdev) static void aqc_remove(struct hid_device *hdev)
{ {
struct d5next_data *priv = hid_get_drvdata(hdev); struct aqc_data *priv = hid_get_drvdata(hdev);
debugfs_remove_recursive(priv->debugfs); debugfs_remove_recursive(priv->debugfs);
hwmon_device_unregister(priv->hwmon_dev); hwmon_device_unregister(priv->hwmon_dev);
...@@ -328,36 +459,36 @@ static void d5next_remove(struct hid_device *hdev) ...@@ -328,36 +459,36 @@ static void d5next_remove(struct hid_device *hdev)
hid_hw_stop(hdev); hid_hw_stop(hdev);
} }
static const struct hid_device_id d5next_table[] = { static const struct hid_device_id aqc_table[] = {
{ HID_USB_DEVICE(0x0c70, 0xf00e) }, /* Aquacomputer D5 Next */ { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) },
{}, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
{ }
}; };
MODULE_DEVICE_TABLE(hid, d5next_table); MODULE_DEVICE_TABLE(hid, aqc_table);
static struct hid_driver d5next_driver = { static struct hid_driver aqc_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.id_table = d5next_table, .id_table = aqc_table,
.probe = d5next_probe, .probe = aqc_probe,
.remove = d5next_remove, .remove = aqc_remove,
.raw_event = d5next_raw_event, .raw_event = aqc_raw_event,
}; };
static int __init d5next_init(void) static int __init aqc_init(void)
{ {
return hid_register_driver(&d5next_driver); return hid_register_driver(&aqc_driver);
} }
static void __exit d5next_exit(void) static void __exit aqc_exit(void)
{ {
hid_unregister_driver(&d5next_driver); hid_unregister_driver(&aqc_driver);
} }
/* Request to initialize after the HID bus to ensure it's not being loaded before */ /* Request to initialize after the HID bus to ensure it's not being loaded before */
late_initcall(aqc_init);
late_initcall(d5next_init); module_exit(aqc_exit);
module_exit(d5next_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
MODULE_DESCRIPTION("Hwmon driver for Aquacomputer D5 Next pump"); MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment