From f74d9bf539cd7a645655c3135d4203e60d9f6b62 Mon Sep 17 00:00:00 2001 From: Drath Date: Sun, 7 Jun 2026 17:50:46 +0200 Subject: [PATCH] set fan curve functionality --- include/aio.h | 13 ++++++++++++- src/aio.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/include/aio.h b/include/aio.h index e342c0e..6740b27 100644 --- a/include/aio.h +++ b/include/aio.h @@ -10,6 +10,12 @@ #define AIO_COLOR_WHITE { 255, 255, 255 } #define AIO_COLOR_OFF { 0, 0, 0 } +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +// roughly converts fan speed to duty +#define SPD2DTY(spd) MAX(0, MIN(spd * 100 / max_fan_speed, 100)) + enum aio_pump_mode_t : uint8_t { QUIET, BALANCED, @@ -29,7 +35,12 @@ struct rgb_t { uint8_t b; }; -struct aio_t{ +struct curve_point_t { + uint8_t temp; + uint8_t duty; +}; + +struct aio_t { libusb_device_handle* handle; enum aio_pump_mode_t pump_mode; float water_temp; diff --git a/src/aio.c b/src/aio.c index 1d45f5c..aa2b116 100644 --- a/src/aio.c +++ b/src/aio.c @@ -8,6 +8,9 @@ static const uint16_t usb_timeout = 250; static const uint8_t buf_size = 32; // needs 32 bc of overflow issue on these aios? +static const uint16_t max_fan_speed = 1200; +static const uint8_t max_curve_points = 7; + // posts cmd to the aio, puts n bytes of response inside resp buffer #define POST_AND_GET_RESP(cmd, n) uint8_t req = cmd; uint8_t resp[buf_size]; aio_post(dev, &req, 1, resp, n) @@ -99,6 +102,33 @@ static void aio_set_color(libusb_device_handle* dev, const enum aio_color_mode_t aio_post(dev, req, 2, resp, buf_size); } +static void aio_set_fan_curve(libusb_device_handle* dev, const struct curve_point_t *curve, const uint8_t count) { + if (count < 1 || count > max_curve_points) { + applog(LOG_ERR, "aio: curve must have 1-%d points", max_curve_points); + return; + } + + struct curve_point_t padded[max_curve_points]; + for (int i = 0; i < max_curve_points; i++) + padded[i] = (i < count) ? curve[i] : curve[count - 1]; + + uint8_t req[16], resp[32]; + req[0] = 0x40; + + // setup request + for (int i = 0; i < max_curve_points; i++) + req[2 + i] = padded[i].temp; + for (int i = 0; i < max_curve_points; i++) + req[9 + i] = padded[i].duty; + + // set curve for both fans + for (int i = 0; i < 2; i++) { + req[1] = i; + aio_post(dev, req, sizeof(req), resp, sizeof(resp)); + } + applog(LOG_INF, "set up AIO fan curve"); +} + void aio_detect(struct aio_t *aio, const float t_quiet, const float t_balanced) { libusb_init(NULL); aio->handle = libusb_open_device_with_vid_pid(NULL, AIO_USB_VID, AIO_USB_PID); @@ -110,6 +140,13 @@ void aio_detect(struct aio_t *aio, const float t_quiet, const float t_balanced) aio->t_q = t_quiet; aio->t_b = t_balanced; + const struct curve_point_t fan_curve[] = { + { 35, SPD2DTY(480) }, + { 39, SPD2DTY(750) }, + { 45, SPD2DTY(1000) }, + { 48, SPD2DTY(max_fan_speed) } + }; + /* detaches other software that might take the device? */ if (libusb_kernel_driver_active(aio->handle, 0)) libusb_detach_kernel_driver(aio->handle, 0); @@ -117,6 +154,8 @@ void aio_detect(struct aio_t *aio, const float t_quiet, const float t_balanced) aio_usb_claim(aio->handle); applog(LOG_INF, "found Corsair AIO device at %04x:%04x, fw ver: %s, B mode at %.1fC, Q mode at %.1fC", AIO_USB_VID, AIO_USB_PID, aio_get_firmware_version(aio->handle, (char[16]){0}), aio->t_b, aio->t_q); aio_set_pump_mode(aio->handle, QUIET); // needed for init ig? + aio_set_color(aio->handle, AIO_COLOR_FIXED, 0, NULL, 0); // so it doenst complain the func is unused + aio_set_fan_curve(aio->handle, fan_curve, sizeof(fan_curve) / sizeof(fan_curve[0])); aio_usb_release(aio->handle); }