diff --git a/aquacomputer_d5next.c b/aquacomputer_d5next.c index a563b40..d16cd4e 100644 --- a/aquacomputer_d5next.c +++ b/aquacomputer_d5next.c @@ -79,6 +79,7 @@ static u8 secondary_ctrl_report[] = { #define D5NEXT_5V_VOLTAGE 0x39 #define D5NEXT_12V_VOLTAGE 0x37 #define D5NEXT_CTRL_REPORT_SIZE 0x329 +#define D5NEXT_TEMP_CTRL_OFFSET 0x2D static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; /* Pump and fan speed registers in D5 Next control report (from 0-100%) */ @@ -89,8 +90,10 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x96, 0x41 }; #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_CTRL_REPORT_SIZE 0x682 +#define FARBWERK360_TEMP_CTRL_OFFSET 0x8 /* Register offsets for the Octo fan controller */ #define OCTO_POWER_CYCLES 0x18 @@ -98,6 +101,7 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x96, 0x41 }; #define OCTO_NUM_SENSORS 4 #define OCTO_SENSOR_START 0x3D #define OCTO_CTRL_REPORT_SIZE 0x65F +#define OCTO_TEMP_CTRL_OFFSET 0xa static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; /* Fan speed registers in Octo control report (from 0-100%) */ @@ -110,6 +114,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5A, 0xAF, 0x104, 0x159, 0x1AE, 0x203, 0 #define QUADRO_SENSOR_START 0x34 #define QUADRO_CTRL_REPORT_SIZE 0x3c1 #define QUADRO_FLOW_SENSOR_OFFSET 0x6e +#define QUADRO_TEMP_CTRL_OFFSET 0xa static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; /* Fan speed registers in Quadro control report (from 0-100%) */ @@ -223,6 +228,7 @@ struct aqc_data { u16 *fan_ctrl_offsets; int num_temp_sensors; int temp_sensor_start_offset; + u16 temp_ctrl_offset; u16 power_cycle_count_offset; u8 flow_sensor_offset; @@ -305,8 +311,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv) return ret; } -/* Refreshes the control buffer and returns value at offset */ -static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, size_t size) +/* Refreshes the control buffer and stores value at offset in val */ +static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, size_t size) { int ret; @@ -318,10 +324,10 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, size_t size) switch (size) { case 16: - ret = get_unaligned_be16(priv->buffer + offset); + *val = (s16) get_unaligned_be16(priv->buffer + offset); break; case 8: - ret = priv->buffer[offset]; + *val = priv->buffer[offset]; break; default: ret = -EINVAL; @@ -369,8 +375,18 @@ 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) - return 0444; + if (channel < priv->num_temp_sensors) { + switch (attr) { + case hwmon_temp_label: + case hwmon_temp_input: + return 0444; + case hwmon_temp_offset: + if (priv->temp_ctrl_offset > 0) // TODO: remove when implemented for all devices + return 0644; + default: + break; + } + } break; case hwmon_pwm: if (priv->fan_ctrl_offsets && channel < priv->num_fans) { @@ -435,10 +451,21 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, switch (type) { case hwmon_temp: - if (priv->temp_input[channel] == -ENODATA) - return -ENODATA; + switch (attr) { + case hwmon_temp_input: + if (priv->temp_input[channel] == -ENODATA) + return -ENODATA; - *val = priv->temp_input[channel]; + *val = priv->temp_input[channel]; + break; + case hwmon_temp_offset: + ret = aqc_get_ctrl_val(priv, priv->temp_ctrl_offset + channel * AQC_TEMP_SENSOR_SIZE, val, 16); + if (ret < 0) + return ret; + *val *= 10; + default: + break; + } break; case hwmon_fan: *val = priv->speed_input[channel]; @@ -449,31 +476,31 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, case hwmon_pwm: switch (attr) { case hwmon_pwm_enable: - ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], 8); + ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], val, 8); if (ret < 0) return ret; - *val = ret + 1; /* Incrementing to satisfy hwmon rules */ + *val = *val + 1; /* Incrementing to satisfy hwmon rules */ break; case hwmon_pwm_input: ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel] + - AQC_FAN_CTRL_PWM_OFFSET, 16); + AQC_FAN_CTRL_PWM_OFFSET, val, 16); if (ret < 0) return ret; - *val = aqc_percent_to_pwm(ret); + *val = aqc_percent_to_pwm(*val); break; case hwmon_pwm_auto_channels_temp: ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel] + - AQC_FAN_CTRL_TEMP_SELECT_OFFSET, 16); + AQC_FAN_CTRL_TEMP_SELECT_OFFSET, val, 16); if (ret < 0) return ret; - *val = 1 << ret; + *val = 1 << *val; default: break; } @@ -523,9 +550,24 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, long val) { int ret, pwm_value, temp_sensor; + long ctrl_mode; struct aqc_data *priv = dev_get_drvdata(dev); switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_offset: + /* Limit temp offset to +/- 15K as in the official software */ + val = clamp_val(val, -15000, 15000) / 10; + ret = aqc_set_ctrl_val(priv, priv->temp_ctrl_offset + channel * AQC_TEMP_SENSOR_SIZE, + val, 16); + if (ret < 0) + return ret; + break; + default: + return -EOPNOTSUPP; + } + break; case hwmon_pwm: switch (attr) { case hwmon_pwm_enable: @@ -550,12 +592,12 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val > 3) { ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[val - 4], - 8); + &ctrl_mode, 8); if (ret < 0) return ret; /* The fan is indeed following another one */ - if (ret > 2) + if (ctrl_mode > 2) return -EINVAL; } break; @@ -627,10 +669,10 @@ 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_OFFSET, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL, @@ -686,7 +728,8 @@ 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; + s16 sensor_value; struct aqc_data *priv; if (report->id != STATUS_REPORT_ID) @@ -837,6 +880,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; + priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET; priv->temp_label = label_d5next_temp; priv->speed_label = label_d5next_speeds; @@ -850,6 +894,8 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->num_fans = 0; priv->num_temp_sensors = FARBWERK_NUM_SENSORS; priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; + priv->temp_ctrl_offset = 0; + priv->temp_label = label_temp_sensors; break; case USB_PRODUCT_ID_FARBWERK360: @@ -858,6 +904,9 @@ 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->buffer_size = FARBWERK360_CTRL_REPORT_SIZE; + priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET; + priv->temp_label = label_temp_sensors; break; case USB_PRODUCT_ID_OCTO: @@ -870,6 +919,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_sensor_start_offset = OCTO_SENSOR_START; priv->power_cycle_count_offset = OCTO_POWER_CYCLES; priv->buffer_size = OCTO_CTRL_REPORT_SIZE; + priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET; priv->temp_label = label_temp_sensors; priv->speed_label = label_fan_speed; @@ -888,6 +938,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) 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_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; priv->temp_label = label_temp_sensors; priv->speed_label = label_quadro_speeds; diff --git a/docs/aquacomputer_d5next.rst b/docs/aquacomputer_d5next.rst index 465d35b..e006bd4 100644 --- a/docs/aquacomputer_d5next.rst +++ b/docs/aquacomputer_d5next.rst @@ -77,8 +77,9 @@ the kernel and supports hotswapping. Sysfs entries ------------- -=========================== ============================================= +=========================== ============================================================== temp[1-4]_input Temperature sensors (in millidegrees Celsius) +temp[1-4]_offset Temperature sensor correction offset (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) @@ -86,7 +87,7 @@ curr[1-8]_input Pump/fan current (in milli Amperes) pwm[1-8] Fan PWM (0 - 255) pwm[1-8]_enable Fan control mode pwm[1-8]_auto_channels_temp Fan control temperature sensors select -=========================== ============================================= +=========================== =============================================================== Debugfs entries ---------------