#include "aio.h" #include "log.h" #include #include static const uint16_t usb_timeout = 1000; static const uint8_t buf_size = 32; // needs 32 bc of overflow issue on these aios? // 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) // posts 2 bytes of cmd to aio, puts n bytes of response inside resp buffer #define POST_AND_GET_RESP2(cmd1, cmd2, n) uint8_t req[2] = {cmd1, cmd2}; uint8_t resp[buf_size]; aio_post(dev, req, 2, resp, n) static void aio_usb_claim(libusb_device_handle* dev) { int ret = libusb_claim_interface(dev, 0); if (ret != 0) { applog(LOG_ERR, "failed to claim AIO device: %s", libusb_error_name(ret)); return; } ret = libusb_control_transfer(dev,(LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE),0x02,0x02,0, NULL, 0, usb_timeout); if (ret < 0) applog(LOG_ERR, "AIO clear-to-send failed: %s", libusb_error_name(ret)); } static void aio_usb_release(libusb_device_handle* dev) { libusb_release_interface(dev, 0); } static void aio_post(libusb_device_handle *dev, uint8_t *req, const int req_len, uint8_t *resp, const int resp_len) { libusb_bulk_transfer(dev, 0x01, req, req_len, NULL, usb_timeout); libusb_bulk_transfer(dev, 0x81, resp, resp_len, NULL, usb_timeout); } static float aio_get_water_temp(libusb_device_handle* dev) { POST_AND_GET_RESP(0xa9, 6); return (float)resp[3] + (float)resp[4] / 10.0f; } static uint16_t aio_get_pump_speed(libusb_device_handle* dev) { POST_AND_GET_RESP(0x31, 5); return resp[3] << 8 | resp[4]; } static enum aio_pump_mode_t aio_get_pump_mode(libusb_device_handle* dev) { POST_AND_GET_RESP(0x33, 4); return (enum aio_pump_mode_t)resp[3]; } static void aio_get_fan_speed(libusb_device_handle* dev, uint16_t *speeds) { for (uint8_t i = 0; i < 2; i++) { POST_AND_GET_RESP2(0x41, i, 6); speeds[i] = resp[4] << 8 | resp[5]; } } static char* aio_get_firmware_version(libusb_device_handle* dev, char* buf) { POST_AND_GET_RESP(0xaa, buf_size); sprintf(buf, "%d.%d.%d.%d", resp[3], resp[4], resp[5], resp[6]); return buf; } static void aio_set_pump_mode(libusb_device_handle* dev, const enum aio_pump_mode_t mode) { POST_AND_GET_RESP2(0x32, mode, 6); } void aio_detect(struct aio_t *aio) { libusb_init(NULL); aio->handle = libusb_open_device_with_vid_pid(NULL, AIO_USB_VID, AIO_USB_PID); if (aio->handle == NULL) { applog(LOG_ERR, "Can't open AIO USB device %x:%x", AIO_USB_VID, AIO_USB_PID); return; } /* detaches other software that might take the device? */ if (libusb_kernel_driver_active(aio->handle, 0)) libusb_detach_kernel_driver(aio->handle, 0); aio_usb_claim(aio->handle); aio_set_pump_mode(aio->handle, QUIET); // needed for init ig? aio_usb_release(aio->handle); applog(LOG_INF, "found Corsair AIO device at %04x:%04x, fw %s", AIO_USB_VID, AIO_USB_PID, aio_get_firmware_version(aio->handle, (char[16]){0})); } void aio_update(struct aio_t *aio) { if (aio->handle == NULL) return; aio_usb_claim(aio->handle); aio->water_temp = aio_get_water_temp(aio->handle); aio->pump_speed = aio_get_pump_speed(aio->handle); aio->pump_mode = aio_get_pump_mode(aio->handle); aio_get_fan_speed(aio->handle, aio->fan_speed); if (aio->water_temp > 36.0f && aio->pump_mode != BALANCED) aio_set_pump_mode(aio->handle, BALANCED); if (aio->water_temp < 34.0f && aio->pump_mode != QUIET) aio_set_pump_mode(aio->handle, QUIET); aio_usb_release(aio->handle); } void aio_close(const struct aio_t *aio) { libusb_attach_kernel_driver(aio->handle, 0); // reattach so liquidctl still works libusb_close(aio->handle); libusb_exit(NULL); applog(LOG_INF, "AIO device closed"); }