Re: [PATCH] hwmon: (aquacomputer_d5next) Add support for reading virtual temp sensors

From: Guenter Roeck
Date: Wed Aug 17 2022 - 11:38:46 EST


On Wed, Aug 17, 2022 at 02:14:41PM +0200, Aleksa Savic wrote:
> Add support for reading virtual temperature sensors for the D5 Next, Octo,
> Quadro and Farbwerk 360.
>
> Virtual temperature sensors are written to the device by the user, pulling
> from an arbitrary value source. Writing to them is not yet reverse
> engineered, so the only way to set them for now is to use the official
> software.
>
> Signed-off-by: Aleksa Savic <savicaleksa83@xxxxxxxxx>

Applied to hwmon-next.

Thanks,
Guenter

> ---
> Documentation/hwmon/aquacomputer_d5next.rst | 32 +++++---
> drivers/hwmon/aquacomputer_d5next.c | 88 +++++++++++++++++++--
> 2 files changed, 100 insertions(+), 20 deletions(-)
>
> diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst
> index 33649a1e3a05..b63a78d47624 100644
> --- a/Documentation/hwmon/aquacomputer_d5next.rst
> +++ b/Documentation/hwmon/aquacomputer_d5next.rst
> @@ -20,10 +20,11 @@ This driver exposes hardware sensors of listed Aquacomputer devices, which
> communicate through proprietary USB HID protocols.
>
> For the D5 Next pump, available sensors are pump and fan speed, power, voltage
> -and current, as well as coolant temperature. Also available through debugfs are
> -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
> -pump. If it's not connected, the fan-related sensors will report zeroes.
> +and current, as well as coolant temperature and eight virtual temp sensors. Also
> +available through debugfs are 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 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
> interface. Configuring the pump through this driver is not implemented, as it
> @@ -31,14 +32,19 @@ 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
> better suited for userspace tools.
>
> -The Octo exposes four temperature sensors and eight PWM controllable fans, along
> -with their speed (in RPM), power, voltage and current.
> +The Octo exposes four physical and sixteen virtual temperature sensors, as well as
> +eight PWM controllable fans, along with their speed (in RPM), power, voltage and
> +current.
>
> -The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable
> -fans, along with their speed (in RPM), power, voltage and current.
> +The Quadro exposes four physical and sixteen virtual temperature sensors, a flow
> +sensor and four PWM controllable fans, along with their speed (in RPM), power,
> +voltage and current.
>
> -The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
> -not all sysfs and debugfs entries will be available.
> +The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally,
> +sixteen virtual temperature sensors of the Farbwerk 360 are exposed.
> +
> +Depending on the device, not all sysfs and debugfs entries will be available.
> +Writing to virtual temperature sensors is not currently supported.
>
> Usage notes
> -----------
> @@ -49,14 +55,14 @@ the kernel and supports hotswapping.
> Sysfs entries
> -------------
>
> -================ ==============================================
> -temp[1-4]_input Temperature sensors (in millidegrees Celsius)
> +================ ==============================================================
> +temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius)
> fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
> power[1-8]_input Pump/fan power (in micro Watts)
> in[0-7]_input Pump/fan voltage (in milli Volts)
> curr[1-8]_input Pump/fan current (in milli Amperes)
> pwm[1-8] Fan PWM (0 - 255)
> -================ ==============================================
> +================ ==============================================================
>
> Debugfs entries
> ---------------
> diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
> index 66430553cc45..b2b0c4fdfa84 100644
> --- a/drivers/hwmon/aquacomputer_d5next.c
> +++ b/drivers/hwmon/aquacomputer_d5next.c
> @@ -71,6 +71,8 @@ static u8 secondary_ctrl_report[] = {
> #define D5NEXT_COOLANT_TEMP 0x57
> #define D5NEXT_NUM_FANS 2
> #define D5NEXT_NUM_SENSORS 1
> +#define D5NEXT_NUM_VIRTUAL_SENSORS 8
> +#define D5NEXT_VIRTUAL_SENSORS_START 0x3f
> #define D5NEXT_PUMP_OFFSET 0x6c
> #define D5NEXT_FAN_OFFSET 0x5f
> #define D5NEXT_5V_VOLTAGE 0x39
> @@ -86,14 +88,18 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
> #define FARBWERK_SENSOR_START 0x2f
>
> /* Register offsets for the Farbwerk 360 RGB controller */
> -#define FARBWERK360_NUM_SENSORS 4
> -#define FARBWERK360_SENSOR_START 0x32
> +#define FARBWERK360_NUM_SENSORS 4
> +#define FARBWERK360_SENSOR_START 0x32
> +#define FARBWERK360_NUM_VIRTUAL_SENSORS 16
> +#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a
>
> /* Register offsets for the Octo fan controller */
> #define OCTO_POWER_CYCLES 0x18
> #define OCTO_NUM_FANS 8
> #define OCTO_NUM_SENSORS 4
> #define OCTO_SENSOR_START 0x3D
> +#define OCTO_NUM_VIRTUAL_SENSORS 16
> +#define OCTO_VIRTUAL_SENSORS_START 0x45
> #define OCTO_CTRL_REPORT_SIZE 0x65F
> static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
>
> @@ -105,6 +111,8 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0
> #define QUADRO_NUM_FANS 4
> #define QUADRO_NUM_SENSORS 4
> #define QUADRO_SENSOR_START 0x34
> +#define QUADRO_NUM_VIRTUAL_SENSORS 16
> +#define QUADRO_VIRTUAL_SENSORS_START 0x3c
> #define QUADRO_CTRL_REPORT_SIZE 0x3c1
> #define QUADRO_FLOW_SENSOR_OFFSET 0x6e
> static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
> @@ -147,6 +155,25 @@ static const char *const label_temp_sensors[] = {
> "Sensor 4"
> };
>
> +static const char *const label_virtual_temp_sensors[] = {
> + "Virtual sensor 1",
> + "Virtual sensor 2",
> + "Virtual sensor 3",
> + "Virtual sensor 4",
> + "Virtual sensor 5",
> + "Virtual sensor 6",
> + "Virtual sensor 7",
> + "Virtual sensor 8",
> + "Virtual sensor 9",
> + "Virtual sensor 10",
> + "Virtual sensor 11",
> + "Virtual sensor 12",
> + "Virtual sensor 13",
> + "Virtual sensor 14",
> + "Virtual sensor 15",
> + "Virtual sensor 16",
> +};
> +
> /* Labels for Octo and Quadro (except speed) */
> static const char *const label_fan_speed[] = {
> "Fan 1 speed",
> @@ -220,6 +247,8 @@ struct aqc_data {
> u16 *fan_ctrl_offsets;
> int num_temp_sensors;
> int temp_sensor_start_offset;
> + int num_virtual_temp_sensors;
> + int virtual_temp_sensor_start_offset;
> u16 power_cycle_count_offset;
> u8 flow_sensor_offset;
>
> @@ -231,7 +260,7 @@ struct aqc_data {
> u32 power_cycles;
>
> /* Sensor values */
> - s32 temp_input[4];
> + s32 temp_input[20]; /* Max 4 physical and 16 virtual */
> u16 speed_input[8];
> u32 power_input[8];
> u16 voltage_input[8];
> @@ -239,6 +268,7 @@ struct aqc_data {
>
> /* Label values */
> const char *const *temp_label;
> + const char *const *virtual_temp_label;
> const char *const *speed_label;
> const char *const *power_label;
> const char *const *voltage_label;
> @@ -345,7 +375,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
>
> switch (type) {
> case hwmon_temp:
> - if (channel < priv->num_temp_sensors)
> + if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors)
> return 0444;
> break;
> case hwmon_pwm:
> @@ -447,7 +477,10 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32
>
> switch (type) {
> case hwmon_temp:
> - *str = priv->temp_label[channel];
> + if (channel < priv->num_temp_sensors)
> + *str = priv->temp_label[channel];
> + else
> + *str = priv->virtual_temp_label[channel - priv->num_temp_sensors];
> break;
> case hwmon_fan:
> *str = priv->speed_label[channel];
> @@ -509,6 +542,22 @@ static const struct hwmon_ops aqc_hwmon_ops = {
>
> static const struct hwmon_channel_info *aqc_info[] = {
> HWMON_CHANNEL_INFO(temp,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL,
> HWMON_T_INPUT | HWMON_T_LABEL,
> HWMON_T_INPUT | HWMON_T_LABEL,
> HWMON_T_INPUT | HWMON_T_LABEL,
> @@ -568,7 +617,7 @@ static const struct hwmon_chip_info aqc_chip_info = {
>
> static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
> {
> - int i, sensor_value;
> + int i, j, sensor_value;
> struct aqc_data *priv;
>
> if (report->id != STATUS_REPORT_ID)
> @@ -581,7 +630,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
> priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
> priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
>
> - /* Temperature sensor readings */
> + /* Physical temperature sensor readings */
> for (i = 0; i < priv->num_temp_sensors; i++) {
> sensor_value = get_unaligned_be16(data +
> priv->temp_sensor_start_offset +
> @@ -592,6 +641,18 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
> priv->temp_input[i] = sensor_value * 10;
> }
>
> + /* Virtual temperature sensor readings */
> + for (j = 0; j < priv->num_virtual_temp_sensors; j++) {
> + sensor_value = get_unaligned_be16(data +
> + priv->virtual_temp_sensor_start_offset +
> + j * AQC_TEMP_SENSOR_SIZE);
> + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
> + priv->temp_input[i] = -ENODATA;
> + else
> + priv->temp_input[i] = sensor_value * 10;
> + i++;
> + }
> +
> /* Fan speed and related readings */
> for (i = 0; i < priv->num_fans; i++) {
> priv->speed_input[i] =
> @@ -717,10 +778,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
> priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
> priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
> + priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS;
> + priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START;
> priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
> priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
>
> priv->temp_label = label_d5next_temp;
> + priv->virtual_temp_label = label_virtual_temp_sensors;
> priv->speed_label = label_d5next_speeds;
> priv->power_label = label_d5next_power;
> priv->voltage_label = label_d5next_voltages;
> @@ -740,7 +804,11 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->num_fans = 0;
> priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
> priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
> + priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS;
> + priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START;
> +
> priv->temp_label = label_temp_sensors;
> + priv->virtual_temp_label = label_virtual_temp_sensors;
> break;
> case USB_PRODUCT_ID_OCTO:
> priv->kind = octo;
> @@ -750,10 +818,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
> priv->num_temp_sensors = OCTO_NUM_SENSORS;
> priv->temp_sensor_start_offset = OCTO_SENSOR_START;
> + priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS;
> + priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START;
> priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
> priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
>
> priv->temp_label = label_temp_sensors;
> + priv->virtual_temp_label = label_virtual_temp_sensors;
> priv->speed_label = label_fan_speed;
> priv->power_label = label_fan_power;
> priv->voltage_label = label_fan_voltage;
> @@ -767,11 +838,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
> priv->num_temp_sensors = QUADRO_NUM_SENSORS;
> priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
> + priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
> + priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START;
> priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
> priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
> priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
>
> priv->temp_label = label_temp_sensors;
> + priv->virtual_temp_label = label_virtual_temp_sensors;
> priv->speed_label = label_quadro_speeds;
> priv->power_label = label_fan_power;
> priv->voltage_label = label_fan_voltage;