hwmon: (aquacomputer_d5next) Add selective 200ms delay after sending ctrl report

commit 56b930dcd8 upstream.

Add a 200ms delay after sending a ctrl report to Quadro,
Octo, D5 Next and Aquaero to give them enough time to
process the request and save the data to memory. Otherwise,
under heavier userspace loads where multiple sysfs entries
are usually set in quick succession, a new ctrl report could
be requested from the device while it's still processing the
previous one and fail with -EPIPE. The delay is only applied
if two ctrl report operations are near each other in time.

Reported by a user on Github [1] and tested by both of us.

[1] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/issues/82

Fixes: 752b927951 ("hwmon: (aquacomputer_d5next) Add support for Aquacomputer Octo")
Signed-off-by: Aleksa Savic <savicaleksa83@gmail.com>
Link: https://lore.kernel.org/r/20230807172004.456968-1-savicaleksa83@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
[ removed Aquaero support as it's not in 6.1 ]
Signed-off-by: Aleksa Savic <savicaleksa83@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Aleksa Savic 2023-08-07 19:20:03 +02:00 committed by Greg Kroah-Hartman
parent d8f9a9cfdc
commit f1fa6e6f85
1 changed files with 35 additions and 1 deletions

View File

@ -12,9 +12,11 @@
#include <linux/crc16.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
@ -49,6 +51,8 @@ static const char *const aqc_device_names[] = {
#define CTRL_REPORT_ID 0x03
#define CTRL_REPORT_DELAY 200 /* ms */
/* The HID report that the official software always sends
* after writing values, currently same for all devices
*/
@ -269,6 +273,9 @@ struct aqc_data {
enum kinds kind;
const char *name;
ktime_t last_ctrl_report_op;
int ctrl_report_delay; /* Delay between two ctrl report operations, in ms */
int buffer_size;
u8 *buffer;
int checksum_start;
@ -325,17 +332,35 @@ static int aqc_pwm_to_percent(long val)
return DIV_ROUND_CLOSEST(val * 100 * 100, 255);
}
static void aqc_delay_ctrl_report(struct aqc_data *priv)
{
/*
* If previous read or write is too close to this one, delay the current operation
* to give the device enough time to process the previous one.
*/
if (priv->ctrl_report_delay) {
s64 delta = ktime_ms_delta(ktime_get(), priv->last_ctrl_report_op);
if (delta < priv->ctrl_report_delay)
msleep(priv->ctrl_report_delay - delta);
}
}
/* Expects the mutex to be locked */
static int aqc_get_ctrl_data(struct aqc_data *priv)
{
int ret;
aqc_delay_ctrl_report(priv);
memset(priv->buffer, 0x00, priv->buffer_size);
ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
ret = -ENODATA;
priv->last_ctrl_report_op = ktime_get();
return ret;
}
@ -345,6 +370,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv)
int ret;
u16 checksum;
aqc_delay_ctrl_report(priv);
/* Init and xorout value for CRC-16/USB is 0xffff */
checksum = crc16(0xffff, priv->buffer + priv->checksum_start, priv->checksum_length);
checksum ^= 0xffff;
@ -356,12 +383,16 @@ static int aqc_send_ctrl_data(struct aqc_data *priv)
ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
return ret;
goto record_access_and_ret;
/* The official software sends this report after every change, so do it here as well */
ret = hid_hw_raw_request(priv->hdev, SECONDARY_CTRL_REPORT_ID, secondary_ctrl_report,
SECONDARY_CTRL_REPORT_SIZE, HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
record_access_and_ret:
priv->last_ctrl_report_op = ktime_get();
return ret;
}
@ -853,6 +884,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
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->ctrl_report_delay = CTRL_REPORT_DELAY;
priv->temp_label = label_d5next_temp;
priv->virtual_temp_label = label_virtual_temp_sensors;
@ -893,6 +925,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
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->ctrl_report_delay = CTRL_REPORT_DELAY;
priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors;
@ -913,6 +946,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
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->ctrl_report_delay = CTRL_REPORT_DELAY;
priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
priv->temp_label = label_temp_sensors;