On Fri, Feb 03, 2023 at 01:03:24PM +0100, Aleksa Savic wrote: > Extend aquacomputer_d5next driver to expose various hardware sensors of the > Aquacomputer Aquastream Ultimate watercooling pump, which communicates > through a proprietary USB HID protocol. > > Coolant temp and external temp sensor readings are available, along with > speed, power, voltage and current of both the pump and optionally connected > fan. It also exposes pressure and flow speed readings. > > Additionally, serial number and firmware version are exposed through > debugfs. > > Tested by a user on Github [1]. > > [1] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/issues/50 > > Signed-off-by: Aleksa Savic <savicaleksa83@xxxxxxxxx> Applied. Thanks, Guenter > --- > Documentation/hwmon/aquacomputer_d5next.rst | 5 + > drivers/hwmon/aquacomputer_d5next.c | 126 +++++++++++++++++++- > 2 files changed, 125 insertions(+), 6 deletions(-) > > diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst > index 527bcd3edda9..7d0d015b1a52 100644 > --- a/Documentation/hwmon/aquacomputer_d5next.rst > +++ b/Documentation/hwmon/aquacomputer_d5next.rst > @@ -12,6 +12,7 @@ Supported devices: > * Aquacomputer Octo fan controller > * Aquacomputer Quadro fan controller > * Aquacomputer High Flow Next sensor > +* Aquacomputer Aquastream Ultimate watercooling pump > * Aquacomputer Poweradjust 3 fan controller > > Author: Aleksa Savic > @@ -54,6 +55,10 @@ The High Flow Next exposes +5V voltages, water quality, conductivity and flow re > A temperature sensor can be connected to it, in which case it provides its reading > and an estimation of the dissipated/absorbed power in the liquid cooling loop. > > +The Aquastream Ultimate pump exposes coolant temp and an external temp sensor, along > +with speed, power, voltage and current of both the pump and optionally connected fan. > +It also exposes pressure and flow speed readings. > + > The Poweradjust 3 controller exposes a single external temperature sensor. > > Depending on the device, not all sysfs and debugfs entries will be available. > diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c > index 2945b630b4a0..12682a610ce7 100644 > --- a/drivers/hwmon/aquacomputer_d5next.c > +++ b/drivers/hwmon/aquacomputer_d5next.c > @@ -1,7 +1,7 @@ > // SPDX-License-Identifier: GPL-2.0+ > /* > * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, > - * Quadro, High Flow Next, Aquaero) > + * Quadro, High Flow Next, Aquaero, Aquastream Ultimate) > * > * Aquacomputer devices send HID reports (with ID 0x01) every second to report > * sensor values, except for devices that communicate through the > @@ -29,9 +29,13 @@ > #define USB_PRODUCT_ID_FARBWERK360 0xf010 > #define USB_PRODUCT_ID_OCTO 0xf011 > #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 > +#define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b > #define USB_PRODUCT_ID_POWERADJUST3 0xf0bd > > -enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext, aquaero, poweradjust3 }; > +enum kinds { > + d5next, farbwerk, farbwerk360, octo, quadro, > + highflownext, aquaero, poweradjust3, aquastreamult > +}; > > static const char *const aqc_device_names[] = { > [d5next] = "d5next", > @@ -41,6 +45,7 @@ static const char *const aqc_device_names[] = { > [quadro] = "quadro", > [highflownext] = "highflownext", > [aquaero] = "aquaero", > + [aquastreamult] = "aquastreamultimate", > [poweradjust3] = "poweradjust3" > }; > > @@ -117,6 +122,26 @@ static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET > #define D5NEXT_TEMP_CTRL_OFFSET 0x2D /* Temperature sensor offsets location */ > static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */ > > +/* Specs of the Aquastream Ultimate pump */ > +/* Pump does not follow the standard structure, so only consider the fan */ > +#define AQUASTREAMULT_NUM_FANS 1 > +#define AQUASTREAMULT_NUM_SENSORS 2 > + > +/* Sensor report offsets for the Aquastream Ultimate pump */ > +#define AQUASTREAMULT_SENSOR_START 0x2D > +#define AQUASTREAMULT_PUMP_OFFSET 0x51 > +#define AQUASTREAMULT_PUMP_VOLTAGE 0x3D > +#define AQUASTREAMULT_PUMP_CURRENT 0x53 > +#define AQUASTREAMULT_PUMP_POWER 0x55 > +#define AQUASTREAMULT_FAN_OFFSET 0x41 > +#define AQUASTREAMULT_PRESSURE_OFFSET 0x57 > +#define AQUASTREAMULT_FLOW_SENSOR_OFFSET 0x37 > +#define AQUASTREAMULT_FAN_VOLTAGE_OFFSET 0x02 > +#define AQUASTREAMULT_FAN_CURRENT_OFFSET 0x00 > +#define AQUASTREAMULT_FAN_POWER_OFFSET 0x04 > +#define AQUASTREAMULT_FAN_SPEED_OFFSET 0x06 > +static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET }; > + > /* Spec and sensor report offset for the Farbwerk RGB controller */ > #define FARBWERK_NUM_SENSORS 4 > #define FARBWERK_SENSOR_START 0x2f > @@ -339,6 +364,34 @@ static const char *const label_highflownext_voltage[] = { > "+5V USB voltage" > }; > > +/* Labels for Aquastream Ultimate */ > +static const char *const label_aquastreamult_temp[] = { > + "Coolant temp", > + "External temp" > +}; > + > +static const char *const label_aquastreamult_speeds[] = { > + "Fan speed", > + "Pump speed", > + "Pressure [mbar]", > + "Flow speed [dL/h]" > +}; > + > +static const char *const label_aquastreamult_power[] = { > + "Fan power", > + "Pump power" > +}; > + > +static const char *const label_aquastreamult_voltages[] = { > + "Fan voltage", > + "Pump voltage" > +}; > + > +static const char *const label_aquastreamult_current[] = { > + "Fan current", > + "Pump current" > +}; > + > /* Labels for Poweradjust 3 */ > static const char *const label_poweradjust3_temp_sensors[] = { > "External sensor" > @@ -359,7 +412,15 @@ static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = { > .speed = AQUAERO_FAN_SPEED_OFFSET > }; > > -/* Fan structure offsets for all devices except Aquaero */ > +/* Fan structure offsets for Aquastream Ultimate */ > +static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = { > + .voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET, > + .curr = AQUASTREAMULT_FAN_CURRENT_OFFSET, > + .power = AQUASTREAMULT_FAN_POWER_OFFSET, > + .speed = AQUASTREAMULT_FAN_SPEED_OFFSET > +}; > + > +/* Fan structure offsets for all devices except those above */ > static struct aqc_fan_structure_offsets aqc_general_fan_structure = { > .voltage = AQC_FAN_VOLTAGE_OFFSET, > .curr = AQC_FAN_CURRENT_OFFSET, > @@ -565,6 +626,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 > case hwmon_fan_input: > case hwmon_fan_label: > switch (priv->kind) { > + case aquastreamult: > + /* > + * Special case to support pump RPM, fan RPM, > + * pressure and flow sensor > + */ > + if (channel < 4) > + return 0444; > + break; > case highflownext: > /* Special case to support flow sensor, water quality > * and conductivity > @@ -595,6 +664,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 > break; > case hwmon_power: > switch (priv->kind) { > + case aquastreamult: > + /* Special case to support pump and fan power */ > + if (channel < 2) > + return 0444; > + break; > case highflownext: > /* Special case to support one power sensor */ > if (channel == 0) > @@ -607,8 +681,17 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 > } > break; > case hwmon_curr: > - if (channel < priv->num_fans) > - return 0444; > + switch (priv->kind) { > + case aquastreamult: > + /* Special case to support pump and fan current */ > + if (channel < 2) > + return 0444; > + break; > + default: > + if (channel < priv->num_fans) > + return 0444; > + break; > + } > break; > case hwmon_in: > switch (priv->kind) { > @@ -617,6 +700,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 > if (channel < priv->num_fans + 2) > return 0444; > break; > + case aquastreamult: > case highflownext: > /* Special case to support two voltage sensors */ > if (channel < 2) > @@ -1001,6 +1085,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 > i++; > } > break; > + case aquastreamult: > + priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET); > + priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET); > + priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET); > + > + priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000; > + > + priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10; > + > + priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT); > + break; > case d5next: > priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; > priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; > @@ -1273,6 +1368,21 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) > priv->power_label = label_highflownext_power; > priv->voltage_label = label_highflownext_voltage; > break; > + case USB_PRODUCT_ID_AQUASTREAMULT: > + priv->kind = aquastreamult; > + > + priv->num_fans = AQUASTREAMULT_NUM_FANS; > + priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets; > + > + priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS; > + priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START; > + > + priv->temp_label = label_aquastreamult_temp; > + priv->speed_label = label_aquastreamult_speeds; > + priv->power_label = label_aquastreamult_power; > + priv->voltage_label = label_aquastreamult_voltages; > + priv->current_label = label_aquastreamult_current; > + break; > case USB_PRODUCT_ID_POWERADJUST3: > priv->kind = poweradjust3; > > @@ -1302,7 +1412,10 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) > priv->serial_number_start_offset = AQC_SERIAL_START; > priv->firmware_version_offset = AQC_FIRMWARE_VERSION; > > - priv->fan_structure = &aqc_general_fan_structure; > + if (priv->kind == aquastreamult) > + priv->fan_structure = &aqc_aquastreamult_fan_structure; > + else > + priv->fan_structure = &aqc_general_fan_structure; > break; > } > > @@ -1360,6 +1473,7 @@ static const struct hid_device_id aqc_table[] = { > { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, > { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, > { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, > + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) }, > { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) }, > { } > };