From 0da484bffc1fa93bac2447290abd30c2e97d5d08 Mon Sep 17 00:00:00 2001 From: Luca Lovisa Date: Sun, 21 Feb 2021 13:15:03 +0100 Subject: [PATCH] Add support for the Lian Li Uni Hub Commits squashed and amended for code style by Adam Honse --- 60-openrgb.rules | 5 + .../LianLiControllerDetect.cpp | 77 ++ .../LianLiUniHubController.cpp | 700 ++++++++++++++++++ .../LianLiController/LianLiUniHubController.h | 263 +++++++ .../RGBController_LianLiUniHub.cpp | 475 ++++++++++++ .../RGBController_LianLiUniHub.h | 45 ++ OpenRGB.pro | 6 + 7 files changed, 1571 insertions(+) create mode 100644 Controllers/LianLiController/LianLiControllerDetect.cpp create mode 100644 Controllers/LianLiController/LianLiUniHubController.cpp create mode 100644 Controllers/LianLiController/LianLiUniHubController.h create mode 100644 Controllers/LianLiController/RGBController_LianLiUniHub.cpp create mode 100644 Controllers/LianLiController/RGBController_LianLiUniHub.h diff --git a/60-openrgb.rules b/60-openrgb.rules index 83e9a931..02d1db88 100644 --- a/60-openrgb.rules +++ b/60-openrgb.rules @@ -172,6 +172,11 @@ SUBSYSTEMS=="usb", ATTR{idVendor}=="0951", ATTR{idProduct}=="16be", TAG+="uacces SUBSYSTEMS=="usb", ATTR{idVendor}=="0951", ATTR{idProduct}=="16d3", TAG+="uaccess" SUBSYSTEMS=="usb", ATTR{idVendor}=="0951", ATTR{idProduct}=="1705", TAG+="uaccess" +#---------------------------------------------------------------# +# Lian Li Uni Hub # +#---------------------------------------------------------------# +SUBSYSTEMS=="usb", ATTR{idVendor}=="0cf2", ATTR{idProduct}=="7750", TAG+="uaccess" + #---------------------------------------------------------------# # Logitech Peripheral Devices # # # diff --git a/Controllers/LianLiController/LianLiControllerDetect.cpp b/Controllers/LianLiController/LianLiControllerDetect.cpp new file mode 100644 index 00000000..a8426373 --- /dev/null +++ b/Controllers/LianLiController/LianLiControllerDetect.cpp @@ -0,0 +1,77 @@ +/*-----------------------------------------*\ +| LianLiControllerDetect.cpp | +| | +| Detector for Lian Li Uni Hub USB | +| lighting controller | +| | +| Luca Lovisa 2/20/2021 | +\*-----------------------------------------*/ + +#include +#include + +#include "Detector.h" +#include "LianLiUniHubController.h" +#include "RGBController_LianLiUniHub.h" +#include "ResourceManager.h" + +#include + +#define UNI_HUB_VID 0x0CF2 +#define UNI_HUB_PID 0x7750 + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is controlled by sending control transfers to various wIndex | +| addresses, allthough it announces some kind of hid interface. Hence it | +| requires libusb as hidapi provides no wIndex customization. | +\*----------------------------------------------------------------------------*/ + +void DetectLianLiUniHub(std::vector&) +{ + libusb_context * ctx; + + libusb_device** devices = nullptr; + + int ret; + + ret = libusb_init(&ctx); + if(ret < 0) + { + return; + } + + ret = libusb_get_device_list(ctx, &devices); + if(ret < 0) + { + return; + } + + int deviceCount = ret; + + for(int i = 0; i < deviceCount; i++) + { + libusb_device* device = devices[i]; + libusb_device_descriptor descriptor; + ret = libusb_get_device_descriptor(device, &descriptor); + + if (ret < 0) + { + continue; + } + + if( descriptor.idVendor == UNI_HUB_VID + && descriptor.idProduct == UNI_HUB_PID) + { + LianLiUniHubController* controller = new LianLiUniHubController(device, &descriptor); + RGBController_LianLiUniHub* rgb_controller = new RGBController_LianLiUniHub(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + if(devices != nullptr) + { + libusb_free_device_list(devices, 1); + } +} + +REGISTER_DETECTOR("Lian Li Uni Hub", DetectLianLiUniHub); diff --git a/Controllers/LianLiController/LianLiUniHubController.cpp b/Controllers/LianLiController/LianLiUniHubController.cpp new file mode 100644 index 00000000..d9b2abb3 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController.cpp @@ -0,0 +1,700 @@ +/*-----------------------------------------*\ +| LianLiUniHubController.cpp | +| | +| Driver for Lian Li Uni Hub USB | +| lighting controller | +| | +| Luca Lovisa 2/20/2021 | +\*-----------------------------------------*/ + +#include "LianLiUniHubController.h" + +#include + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is controlled by sending control transfers to various wIndex | +| addresses, allthough it announces some kind of hid interface. Hence it | +| requires libusb as hidapi provides no wIndex customization. | +\*----------------------------------------------------------------------------*/ + +LianLiUniHubController::LianLiUniHubController + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ) +{ + int ret; + + /*--------------------------------------------------------------------*\ + | Open the libusb device. | + \*--------------------------------------------------------------------*/ + ret = libusb_open(device, &handle); + + if(ret < 0) + { + return; + } + + /*--------------------------------------------------------------------*\ + | Fill in the location string from USB port numbers. | + \*--------------------------------------------------------------------*/ + uint8_t ports[7]; + + ret = libusb_get_port_numbers(device, ports, sizeof(ports)); + + if(ret > 0) + { + location = "USB: "; + + for (int i = 0; i < ret; i ++) + { + location += std::to_string(ports[i]); + location.push_back(':'); + } + + location.pop_back(); + } + + /*--------------------------------------------------------------------*\ + | Fill in the serial string from the string descriptor | + \*--------------------------------------------------------------------*/ + char serialStr[64]; + + ret = libusb_get_string_descriptor_ascii(handle, descriptor->iSerialNumber, reinterpret_cast(serialStr), sizeof(serialStr)); + + if(ret > 0) + { + serial = std::string(serialStr, ret); + } + + /*--------------------------------------------------------------------*\ + | Fill in the version string by reading version from device. | + \*--------------------------------------------------------------------*/ + version = ReadVersion(); + + /*--------------------------------------------------------------------*\ + | Create channels with their static configuration and "sane" defaults. | + \*--------------------------------------------------------------------*/ + Channel channel1; + channel1.index = 0; + channel1.anyFanCountOffset = UNIHUB_ANY_C1_FAN_COUNT_OFFSET; + channel1.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel1.ledActionAddress = UNIHUB_LED_C1_ACTION_ADDRESS; + channel1.ledCommitAddress = UNIHUB_LED_C1_COMMIT_ADDRESS; + channel1.ledModeAddress = UNIHUB_LED_C1_MODE_ADDRESS; + channel1.ledSpeedAddress = UNIHUB_LED_C1_SPEED_ADDRESS; + channel1.ledDirectionAddress = UNIHUB_LED_C1_DIRECTION_ADDRESS; + channel1.ledBrightnessAddress = UNIHUB_LED_C1_BRIGHTNESS_ADDRESS; + channel1.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel1.ledSpeed = UNIHUB_LED_SPEED_100; + channel1.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel1.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel1.fanHubActionAddress = UNIHUB_FAN_C1_HUB_ACTION_ADDRESS; + channel1.fanHubCommitAddress = UNIHUB_FAN_C1_HUB_COMMIT_ADDRESS; + channel1.fanPwmActionAddress = UNIHUB_FAN_C1_PWM_ACTION_ADDRESS; + channel1.fanPwmCommitAddress = UNIHUB_FAN_C1_PWM_COMMIT_ADDRESS; + channel1.fanRpmActionAddress = UNIHUB_FAN_C1_RPM_ACTION_ADDRESS; + channel1.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[0] = channel1; + + Channel channel2; + channel2.index = 1; + channel2.anyFanCountOffset = UNIHUB_ANY_C2_FAN_COUNT_OFFSET; + channel2.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel2.ledActionAddress = UNIHUB_LED_C2_ACTION_ADDRESS; + channel2.ledCommitAddress = UNIHUB_LED_C2_COMMIT_ADDRESS; + channel2.ledModeAddress = UNIHUB_LED_C2_MODE_ADDRESS; + channel2.ledSpeedAddress = UNIHUB_LED_C2_SPEED_ADDRESS; + channel2.ledDirectionAddress = UNIHUB_LED_C2_DIRECTION_ADDRESS; + channel2.ledBrightnessAddress = UNIHUB_LED_C2_BRIGHTNESS_ADDRESS; + channel2.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel2.ledSpeed = UNIHUB_LED_SPEED_100; + channel2.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel2.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel2.fanHubActionAddress = UNIHUB_FAN_C2_HUB_ACTION_ADDRESS; + channel2.fanHubCommitAddress = UNIHUB_FAN_C2_HUB_COMMIT_ADDRESS; + channel2.fanPwmActionAddress = UNIHUB_FAN_C2_PWM_ACTION_ADDRESS; + channel2.fanPwmCommitAddress = UNIHUB_FAN_C2_PWM_COMMIT_ADDRESS; + channel2.fanRpmActionAddress = UNIHUB_FAN_C2_RPM_ACTION_ADDRESS; + channel2.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[1] = channel2; + + Channel channel3; + channel3.index = 2; + channel3.anyFanCountOffset = UNIHUB_ANY_C3_FAN_COUNT_OFFSET; + channel3.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel3.ledActionAddress = UNIHUB_LED_C3_ACTION_ADDRESS; + channel3.ledCommitAddress = UNIHUB_LED_C3_COMMIT_ADDRESS; + channel3.ledModeAddress = UNIHUB_LED_C3_MODE_ADDRESS; + channel3.ledSpeedAddress = UNIHUB_LED_C3_SPEED_ADDRESS; + channel3.ledDirectionAddress = UNIHUB_LED_C3_DIRECTION_ADDRESS; + channel3.ledBrightnessAddress = UNIHUB_LED_C3_BRIGHTNESS_ADDRESS; + channel3.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel3.ledSpeed = UNIHUB_LED_SPEED_100; + channel3.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel3.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel3.fanHubActionAddress = UNIHUB_FAN_C3_HUB_ACTION_ADDRESS; + channel3.fanHubCommitAddress = UNIHUB_FAN_C3_HUB_COMMIT_ADDRESS; + channel3.fanPwmActionAddress = UNIHUB_FAN_C3_PWM_ACTION_ADDRESS; + channel3.fanPwmCommitAddress = UNIHUB_FAN_C3_PWM_COMMIT_ADDRESS; + channel3.fanRpmActionAddress = UNIHUB_FAN_C3_RPM_ACTION_ADDRESS; + channel3.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[2] = channel3; + + Channel channel4; + channel4.index = 3; + channel4.anyFanCountOffset = UNIHUB_ANY_C4_FAN_COUNT_OFFSET; + channel4.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel4.ledActionAddress = UNIHUB_LED_C4_ACTION_ADDRESS; + channel4.ledCommitAddress = UNIHUB_LED_C4_COMMIT_ADDRESS; + channel4.ledModeAddress = UNIHUB_LED_C4_MODE_ADDRESS; + channel4.ledSpeedAddress = UNIHUB_LED_C4_SPEED_ADDRESS; + channel4.ledDirectionAddress = UNIHUB_LED_C4_DIRECTION_ADDRESS; + channel4.ledBrightnessAddress = UNIHUB_LED_C4_BRIGHTNESS_ADDRESS; + channel4.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel4.ledSpeed = UNIHUB_LED_SPEED_100; + channel4.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel4.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel4.fanHubActionAddress = UNIHUB_FAN_C4_HUB_ACTION_ADDRESS; + channel4.fanHubCommitAddress = UNIHUB_FAN_C4_HUB_COMMIT_ADDRESS; + channel4.fanPwmActionAddress = UNIHUB_FAN_C4_PWM_ACTION_ADDRESS; + channel4.fanPwmCommitAddress = UNIHUB_FAN_C4_PWM_COMMIT_ADDRESS; + channel4.fanRpmActionAddress = UNIHUB_FAN_C4_RPM_ACTION_ADDRESS; + channel4.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[3] = channel4; +} + +LianLiUniHubController::~LianLiUniHubController() +{ + CloseLibusb(); +} + +std::string LianLiUniHubController::GetVersion() +{ + return version; +} + +std::string LianLiUniHubController::GetLocation() +{ + return location; +} + +std::string LianLiUniHubController::GetSerial() +{ + return serial; +} + +void LianLiUniHubController::SetAnyFanCount(size_t channel, uint8_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].anyFanCount = count; +} + +void LianLiUniHubController::SetLedColors(size_t channel, RGBColor* colors, size_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + /*-------------------------------------*\ + | Check for invalid count | + \*-------------------------------------*/ + if(count > UNIHUB_CHANLED_COUNT) + { + count = UNIHUB_CHANLED_COUNT; + } + + size_t i = 0; + for(; i < count; i++) + { + channels[channel].colors[i].r = RGBGetRValue(colors[i]); + channels[channel].colors[i].b = RGBGetBValue(colors[i]); + channels[channel].colors[i].g = RGBGetGValue(colors[i]); + } + + /* Set all remaining leds to black */ + for(; i < UNIHUB_CHANLED_COUNT; i++) + { + channels[channel].colors[i].r = 0x00; + channels[channel].colors[i].b = 0x00; + channels[channel].colors[i].g = 0x00; + } +} + +void LianLiUniHubController::SetLedMode(size_t channel, uint8_t mode) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledMode = mode; +} + +void LianLiUniHubController::SetLedSpeed(size_t channel, uint8_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledSpeed = speed; +} + +void LianLiUniHubController::SetLedDirection(size_t channel, uint8_t direction) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledDirection = direction; +} + +void LianLiUniHubController::SetLedBrightness(size_t channel, uint8_t brightness) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledBrightness = brightness; +} + +uint16_t LianLiUniHubController::GetFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return 0; + } + + return channels[channel].fanSpeed; +} + +void LianLiUniHubController::SetFanSpeed(size_t channel, uint16_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].fanSpeed = speed; +} + +void LianLiUniHubController::EnableRgbhMode() +{ + rgbhModeEnabled = true; +} + +void LianLiUniHubController::DisableRgbhMode() +{ + rgbhModeEnabled = false; +} + +void LianLiUniHubController::EnableSyncMode() +{ + syncModeEnabled = true; +} + +void LianLiUniHubController::DisableSyncMode() +{ + syncModeEnabled = false; +} + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is a PWM and LED controller designed specifically for the Lian | +| Li Uni Fans. It can control them by itself using the built-in effect engine | +| can also be connected to the mainboard via 4-pin PWM and 3-pin RGB cables | +| and forward these signals. The protocol implementation below was build as | +| close a possible to the Lian Li L-Connect software. | +| | +| The commands to control the fan speeds and to switch between controller and | +| mainboard control is already included, but currently deactivated as OpenRGB | +| had no fan control module or controller specific configuration at the time | +| of writing. | +\*----------------------------------------------------------------------------*/ +void LianLiUniHubController::Synchronize() +{ + /*---------------------------------------------------------------------*\ + | Configure common settings. | + \*---------------------------------------------------------------------*/ + + /*---------------------------------------------------------------------*\ + | Still unsure about this. Probably some sort of configuration | + | initialization | + \*---------------------------------------------------------------------*/ + uint8_t config_initialization[1] = { 0x34 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_initialization, sizeof(config_initialization)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + + for(const Channel& channel : channels) + { + /*-------------------------------------*\ + | The Uni Hub doesn't know zero fans | + \*-------------------------------------*/ + uint8_t anyFanCount = channel.anyFanCount; + + if(anyFanCount == UNIHUB_ANY_FAN_COUNT_000) + { + anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + } + + /*-------------------------------------*\ + | Configure the physical fan count | + \*-------------------------------------*/ + uint8_t config_fan_count[2] = { 0x32, (uint8_t)(channel.anyFanCountOffset | anyFanCount) }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_fan_count, sizeof(config_fan_count)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + + /*--------------------------------------------------------------------*\ + | Configure channels for sync effects | + \*--------------------------------------------------------------------*/ + if(syncModeEnabled) + { + uint8_t config_sync[6]; + uint8_t config_sync_index = 0; + + config_sync[config_sync_index++] = 0x33; + + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_ANY_FAN_COUNT_000) + { + config_sync[config_sync_index++] = channel.index; + } + } + + config_sync[config_sync_index++] = 0x08; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_sync, config_sync_index); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_ANY_FAN_COUNT_000) + { + /*-----------------------------*\ + | Configure led colors | + \*-----------------------------*/ + uint8_t config_colors[192]; + memcpy(config_colors, channel.colors, sizeof(config_colors)); + + /*-----------------------------*\ + | No idea what this does ... | + \*-----------------------------*/ + if (syncModeEnabled) + { + config_colors[0x06] = 0x66; + config_colors[0x07] = 0x33; + config_colors[0x08] = 0xCC; + + memset(config_colors + 0x09, 0x00, sizeof(config_colors) - 0x09); + } + + SendConfig(channel.ledActionAddress, config_colors, sizeof(config_colors)); + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t config_mode[1] = { channel.ledMode }; + + SendConfig(channel.ledModeAddress, config_mode, sizeof(config_mode)); + } + /*-----------------------------------------------------------------*\ + | The Uni Hub doesn't know zero fans so we set them to black | + \*-----------------------------------------------------------------*/ + else + { + /*-----------------------------*\ + | Configure led colors | + \*-----------------------------*/ + uint8_t config_colors[192]; + memset(config_colors, 0x00, sizeof(config_colors)); + + SendConfig(channel.ledActionAddress, config_colors, sizeof(config_colors)); + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t config_mode[1] = { UNIHUB_LED_MODE_STATIC_COLOR }; + + SendConfig(channel.ledModeAddress, config_mode, sizeof(config_mode)); + } + + /*---------------------------------*\ + | Configure led speed | + \*---------------------------------*/ + uint8_t config_speed[1] = { channel.ledSpeed }; + + SendConfig(channel.ledSpeedAddress, config_speed, sizeof(config_speed)); + + /*---------------------------------*\ + | Configure led direction | + \*---------------------------------*/ + uint8_t config_direction[1] = { channel.ledDirection }; + + SendConfig(channel.ledDirectionAddress, config_direction, sizeof(config_direction)); + + /*---------------------------------*\ + | Configure led brightness | + \*---------------------------------*/ + uint8_t config_brightness[1] = { channel.ledBrightness }; + + SendConfig(channel.ledBrightnessAddress, config_brightness, sizeof(config_brightness)); + + /*---------------------------------*\ + | Commit only once for all led | + | settings | + \*---------------------------------*/ + SendCommit(channel.ledCommitAddress); + } + + /*--------------------------------------------------------------------*\ + | Configure fan settings. Comment out until enabling fan control | + \*--------------------------------------------------------------------*/ +// uint8_t control = 0; + +// /*-------------------------------------*\ +// | Configure fan settings | +// \*-------------------------------------*/ +// for(const Channel& channel : channels) +// { +// if(channel.fanSpeed == UNIHUB_FAN_SPEED_PWM) +// { +// /*-----------------------------*\ +// | Configure the fan to pwm | +// | control | +// \*-----------------------------*/ +// uint8_t config_pwm[1] = { 0x00 }; + +// control |= (0x01 << channel.index); + +// SendConfig(channel.fanPwmActionAddress, config_pwm, sizeof(config_pwm)); +// SendCommit(channel.fanPwmCommitAddress); +// } +// else +// { +// /*-----------------------------*\ +// | Configure the fan to hub | +// | control and set speed | +// \*-----------------------------*/ +// uint8_t config_hub[2] = { (uint8_t)(channel.fanSpeed >> 0x08), (uint8_t)(channel.fanSpeed & 0xFF) }; + +// SendConfig(channel.fanHubActionAddress, config_hub, sizeof(config_hub)); +// SendCommit(channel.fanHubCommitAddress); +// } +// } + +// /*-------------------------------------*\ +// | Configure fan control modes | +// \*-------------------------------------*/ +// uint8_t config_fan_mode[2] = { 0x31, (uint8_t)(0xF0 | control) }; + +// SendConfig(UNIHUB_ACTION_ADDRESS, config_fan_mode, sizeof(config_fan_mode)); +// SendCommit(UNIHUB_COMMIT_ADDRESS); + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ + if(rgbhModeEnabled) + { + /*-------------------------------------*\ + | Configure the leds to hdr control. | + \*-------------------------------------*/ + uint8_t config_hdr[2] = { 0x30, 0x01 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_hdr, sizeof(config_hdr)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + else + { + /*-------------------------------------*\ + | Configure the leds to hub control | + \*-------------------------------------*/ + uint8_t config_hub[2] = { 0x30, 0x00 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_hub, sizeof(config_hub)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } +} + +uint16_t LianLiUniHubController::ReadFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(0); + } + + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel > UNIHUB_CHANNEL_COUNT) + { + return(0); + } + + uint8_t buffer[2]; + uint8_t length = sizeof(buffer); + + uint16_t wIndex = channels[channel].fanRpmActionAddress; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(0); + } + + return(*(uint16_t*)buffer); +} + +void LianLiUniHubController::CloseLibusb() +{ + if (handle != nullptr) + { + libusb_close(handle); + handle = nullptr; + } +} + +std::string LianLiUniHubController::ReadVersion() +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(""); + } + + uint8_t buffer[5]; + uint8_t length = sizeof(buffer); + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + 0xB500, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(""); + } + + /*-------------------------------------*\ + | Format version string | + \*-------------------------------------*/ + char version[14]; + int vlength = std::snprintf(version, sizeof(version), "%x.%x.%x.%x.%x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + + return(std::string(version, vlength)); +} + +void LianLiUniHubController::SendConfig(uint16_t wIndex, uint8_t *config, size_t length) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return; + } + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0x40, /* bmRequestType */ + 0x80, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + config, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return; + } +} + +void LianLiUniHubController::SendCommit(uint16_t wIndex) +{ + /*-------------------------------------*\ + | Set up config packet | + \*-------------------------------------*/ + uint8_t config[1] = { 0x01 }; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + SendConfig(wIndex, config, sizeof(config)); + + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHubController.h b/Controllers/LianLiController/LianLiUniHubController.h new file mode 100644 index 00000000..74de8c74 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController.h @@ -0,0 +1,263 @@ +/*-----------------------------------------*\ +| LianLiUniHubController.h | +| | +| Definitions and types for Lian Li Uni | +| Hub USB RGB lighting controller | +| | +| Luca Lovisa 2/20/2021 | +\*-----------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "RGBController.h" + +#include + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_CHANNEL_COUNT = 0x04, /* Channel count */ + UNIHUB_CHANLED_COUNT = 0x40, /* Max-LED per channel count */ +}; + +enum +{ + UNIHUB_ACTION_ADDRESS = 0xe021, /* Global action address */ + UNIHUB_COMMIT_ADDRESS = 0xe02f, /* Global commit address */ +}; + +enum +{ + UNIHUB_ANY_C1_FAN_COUNT_OFFSET = 0x00, /* Channel 1 fan count offset */ + UNIHUB_ANY_C2_FAN_COUNT_OFFSET = 0x10, /* Channel 2 fan count offset */ + UNIHUB_ANY_C3_FAN_COUNT_OFFSET = 0x20, /* Channel 3 fan count offset */ + UNIHUB_ANY_C4_FAN_COUNT_OFFSET = 0x30, /* Channel 4 fan count offset */ +}; + +enum +{ + UNIHUB_ANY_FAN_COUNT_000 = 0xFF, /* Fan count for 0 fans (dummy value) */ + UNIHUB_ANY_FAN_COUNT_001 = 0x00, /* Fan count for 1 fan */ + UNIHUB_ANY_FAN_COUNT_002 = 0x01, /* Fan count for 2 fans */ + UNIHUB_ANY_FAN_COUNT_003 = 0x02, /* Fan count for 3 fans */ + UNIHUB_ANY_FAN_COUNT_004 = 0x03, /* Fan count for 4 fans */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to led configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_LED_C1_ACTION_ADDRESS = 0xe300, /* Channel 1 led action address */ + UNIHUB_LED_C1_COMMIT_ADDRESS = 0xe02f, /* Channel 1 led commit address */ + UNIHUB_LED_C1_MODE_ADDRESS = 0xe021, /* Channel 1 led mode address */ + UNIHUB_LED_C1_SPEED_ADDRESS = 0xe022, /* Channel 1 led speed address */ + UNIHUB_LED_C1_DIRECTION_ADDRESS = 0xe023, /* Channel 1 led direction address */ + UNIHUB_LED_C1_BRIGHTNESS_ADDRESS = 0xe029, /* Channel 1 led brightness address */ + + UNIHUB_LED_C2_ACTION_ADDRESS = 0xe3c0, /* Channel 2 led action address */ + UNIHUB_LED_C2_COMMIT_ADDRESS = 0xe03f, /* Channel 2 led commit address */ + UNIHUB_LED_C2_MODE_ADDRESS = 0xe031, /* Channel 2 led mode address */ + UNIHUB_LED_C2_SPEED_ADDRESS = 0xe032, /* Channel 2 led speed address */ + UNIHUB_LED_C2_DIRECTION_ADDRESS = 0xe033, /* Channel 2 led direction address */ + UNIHUB_LED_C2_BRIGHTNESS_ADDRESS = 0xe039, /* Channel 2 led brightness address */ + + UNIHUB_LED_C3_ACTION_ADDRESS = 0xe480, /* Channel 3 led action address */ + UNIHUB_LED_C3_COMMIT_ADDRESS = 0xe04f, /* Channel 3 led commit address */ + UNIHUB_LED_C3_MODE_ADDRESS = 0xe041, /* Channel 3 led mode address */ + UNIHUB_LED_C3_SPEED_ADDRESS = 0xe042, /* Channel 3 led speed address */ + UNIHUB_LED_C3_DIRECTION_ADDRESS = 0xe043, /* Channel 3 led direction address */ + UNIHUB_LED_C3_BRIGHTNESS_ADDRESS = 0xe049, /* Channel 3 led brightness address */ + + UNIHUB_LED_C4_ACTION_ADDRESS = 0xe540, /* Channel 4 led action address */ + UNIHUB_LED_C4_COMMIT_ADDRESS = 0xe05f, /* Channel 4 led commit address */ + UNIHUB_LED_C4_MODE_ADDRESS = 0xe051, /* Channel 4 led mode address */ + UNIHUB_LED_C4_SPEED_ADDRESS = 0xe052, /* Channel 4 led speed address */ + UNIHUB_LED_C4_DIRECTION_ADDRESS = 0xe053, /* Channel 4 led direction address */ + UNIHUB_LED_C4_BRIGHTNESS_ADDRESS = 0xe059, /* Channel 4 led brightness address */ +}; + +enum +{ + UNIHUB_LED_MODE_RAINBOW = 0x05, /* Rainbow mode */ + UNIHUB_LED_MODE_STATIC_COLOR = 0x01, /* Static Color mode */ + UNIHUB_LED_MODE_BREATHING = 0x02, /* Breathing mode */ + UNIHUB_LED_MODE_COLOR_CYCLE = 0x04, /* Color Cycle mode */ + UNIHUB_LED_MODE_RUNWAY = 0x1c, /* Runway mode */ + UNIHUB_LED_MODE_RUNWAY_SYNC = 0x1c, /* Runway Sync mode */ + UNIHUB_LED_MODE_STAGGGERED = 0x18, /* Stagggered mode */ + UNIHUB_LED_MODE_MIXING = 0x1a, /* Mixing mode */ + UNIHUB_LED_MODE_METEOR = 0x07, /* Meteor mode */ + UNIHUB_LED_MODE_METEOR_SYNC = 0x07, /* Meteor Sync mode */ + UNIHUB_LED_MODE_FIREWORK = 0x1f, /* Firework mode */ + UNIHUB_LED_MODE_STACK = 0x21, /* Stack mode */ + UNIHUB_LED_MODE_STACK_MULTI_COLOR = 0x22, /* Stack Multi Color mode */ + UNIHUB_LED_MODE_NEON = 0x23, /* Neon mode */ +}; + +enum +{ + UNIHUB_LED_SPEED_000 = 0x04, /* Very slow speed */ + UNIHUB_LED_SPEED_025 = 0x03, /* Rather slow speed */ + UNIHUB_LED_SPEED_050 = 0x02, /* Medium speed */ + UNIHUB_LED_SPEED_075 = 0x01, /* Rather fast speed */ + UNIHUB_LED_SPEED_100 = 0x00, /* Very fast speed */ +}; + +enum +{ + UNIHUB_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to fan configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_FAN_C1_HUB_ACTION_ADDRESS = 0xe8a0, /* Channel 1 fan action address for hub control */ + UNIHUB_FAN_C1_HUB_COMMIT_ADDRESS = 0xe890, /* Channel 1 fan commit address for hub control */ + UNIHUB_FAN_C1_PWM_ACTION_ADDRESS = 0xe890, /* Channel 1 fan action address for pwm control */ + UNIHUB_FAN_C1_PWM_COMMIT_ADDRESS = 0xe818, /* Channel 1 fan commit address for pwm control */ + UNIHUB_FAN_C1_RPM_ACTION_ADDRESS = 0xe800, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C2_HUB_ACTION_ADDRESS = 0xe8a2, /* Channel 2 fan action address for hub control */ + UNIHUB_FAN_C2_HUB_COMMIT_ADDRESS = 0xe891, /* Channel 2 fan commit address for hub control */ + UNIHUB_FAN_C2_PWM_ACTION_ADDRESS = 0xe891, /* Channel 2 fan action address for pwm control */ + UNIHUB_FAN_C2_PWM_COMMIT_ADDRESS = 0xe81a, /* Channel 2 fan commit address for pwm control */ + UNIHUB_FAN_C2_RPM_ACTION_ADDRESS = 0xe802, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C3_HUB_ACTION_ADDRESS = 0xe8a4, /* Channel 3 fan action address for hub control */ + UNIHUB_FAN_C3_HUB_COMMIT_ADDRESS = 0xe892, /* Channel 3 fan commit address for hub control */ + UNIHUB_FAN_C3_PWM_ACTION_ADDRESS = 0xe892, /* Channel 3 fan action address for pwm control */ + UNIHUB_FAN_C3_PWM_COMMIT_ADDRESS = 0xe81c, /* Channel 3 fan commit address for pwm control */ + UNIHUB_FAN_C3_RPM_ACTION_ADDRESS = 0xe804, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C4_HUB_ACTION_ADDRESS = 0xe8a6, /* Channel 4 fan action address for hub control */ + UNIHUB_FAN_C4_HUB_COMMIT_ADDRESS = 0xe893, /* Channel 4 fan commit address for hub control */ + UNIHUB_FAN_C4_PWM_ACTION_ADDRESS = 0xe893, /* Channel 4 fan action address for pwm control */ + UNIHUB_FAN_C4_PWM_COMMIT_ADDRESS = 0xe81e, /* Channel 4 fan commit address for pwm control */ + UNIHUB_FAN_C4_RPM_ACTION_ADDRESS = 0xe806, /* Channel 1 fan pwm read address */ +}; + +enum +{ + UNIHUB_FAN_SPEED_QUIET = 0x2003, /* Rather slow */ + UNIHUB_FAN_SPEED_HIGH_SPEED = 0x2206, /* Rather fast */ + UNIHUB_FAN_SPEED_FULL_SPEED = 0x6c07, /* BRRRRRRRRRR */ + UNIHUB_FAN_SPEED_PWM = 0xffff, /* PWM Control */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHubController +{ +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_CHANLED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +public: + LianLiUniHubController + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ); + ~LianLiUniHubController(); + + std::string GetVersion(); + std::string GetLocation(); + std::string GetSerial(); + + void SetAnyFanCount(size_t channel, uint8_t count); + void SetLedColors(size_t channel, RGBColor* colors, size_t count); + void SetLedMode(size_t channel, uint8_t mode); + void SetLedSpeed(size_t channel, uint8_t speed); + void SetLedDirection(size_t channel, uint8_t direction); + void SetLedBrightness(size_t channel, uint8_t brightness); + uint16_t GetFanSpeed(size_t channel); + void SetFanSpeed(size_t channel, uint16_t speed); + void EnableRgbhMode(); + void DisableRgbhMode(); + void EnableSyncMode(); + void DisableSyncMode(); + uint16_t ReadFanSpeed(size_t channel); + + /*-----------------------------------------------------*\ + | Synchronize the current configuration to the Uni Hub. | + \*-----------------------------------------------------*/ + void Synchronize(); + +private: + libusb_device_handle* handle = nullptr; + + std::string version; + std::string location; + std::string serial; + + bool rgbhModeEnabled = false; + bool syncModeEnabled = false; + + Channel channels[UNIHUB_CHANNEL_COUNT]; + + void CloseLibusb(); + std::string ReadVersion(); + void SendConfig(uint16_t wIndex, uint8_t *config, size_t length); + void SendCommit(uint16_t wIndex); +}; diff --git a/Controllers/LianLiController/RGBController_LianLiUniHub.cpp b/Controllers/LianLiController/RGBController_LianLiUniHub.cpp new file mode 100644 index 00000000..587c6f3d --- /dev/null +++ b/Controllers/LianLiController/RGBController_LianLiUniHub.cpp @@ -0,0 +1,475 @@ +/*-----------------------------------------*\ +| RGBController_LianLiUniHub.cpp | +| | +| Generic RGB Interface for Lian Li Uni | +| Hub USB controller driver | +| | +| Luca Lovisa 2/20/2021 | +\*-----------------------------------------*/ + +#include "RGBController_LianLiUniHub.h" + +#include + +mode makeMode() +{ + mode Mode; + + Mode.value = 0; + Mode.flags = 0; + Mode.speed_min = 0; + Mode.speed_max = 0; + Mode.colors_min = 0; + Mode.colors_max = 0; + Mode.speed = 0; + Mode.direction = 0; + Mode.color_mode = 0; + + return Mode; +} + +RGBController_LianLiUniHub::RGBController_LianLiUniHub(LianLiUniHubController* uniHub_ptr) +{ + uniHub = uniHub_ptr; + + name = "Lian Li Uni Hub"; + vendor = "Lian Li"; + version = uniHub->GetVersion(); + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub"; + location = uniHub->GetLocation(); + serial = uniHub->GetSerial(); + + initializedMode = false; + + mode StaticColor = makeMode(); + StaticColor.name = "Custom"; + StaticColor.value = UNIHUB_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + StaticColor.color_mode = MODE_COLORS_PER_LED; + modes.push_back(StaticColor); + + mode Rainbow = makeMode(); + Rainbow.name = "Rainbow Wave"; + Rainbow.value = UNIHUB_LED_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.speed_min = 1; + Rainbow.speed_max = 5; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Breathing = makeMode(); + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = 1; + Breathing.speed_max = 5; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle = makeMode(); + ColorCycle.name = "Color Cycle"; + ColorCycle.value = UNIHUB_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ColorCycle.speed_min = 1; + ColorCycle.speed_max = 5; + ColorCycle.colors_min = 3; + ColorCycle.colors_max = 3; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(3); + modes.push_back(ColorCycle); + + mode Runway = makeMode(); + Runway.name = "Runway"; + Runway.value = UNIHUB_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 1; + Runway.speed_max = 5; + Runway.colors_min = 2; + Runway.colors_max = 2; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + + mode RunwaySync = makeMode(); + RunwaySync.name = "Runway Sync"; + RunwaySync.value = UNIHUB_LED_MODE_RUNWAY_SYNC | 0x0100; + RunwaySync.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RunwaySync.speed_min = 1; + RunwaySync.speed_max = 5; + RunwaySync.colors_min = 2; + RunwaySync.colors_max = 2; + RunwaySync.color_mode = MODE_COLORS_MODE_SPECIFIC; + RunwaySync.colors.resize(2); + modes.push_back(RunwaySync); + + mode Staggered = makeMode(); + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_LED_MODE_STAGGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 1; + Staggered.speed_max = 5; + Staggered.colors_min = 2; + Staggered.colors_max = 2; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(2); + modes.push_back(Staggered); + + mode Mixing = makeMode(); + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 1; + Mixing.speed_max = 5; + Mixing.colors_min = 2; + Mixing.colors_max = 2; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + mode Meteor = makeMode(); + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Meteor.speed_min = 1; + Meteor.speed_max = 5; + Meteor.colors_min = 2; + Meteor.colors_max = 2; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(2); + modes.push_back(Meteor); + + mode MeteorSync = makeMode(); + MeteorSync.name = "Meteor Sync"; + MeteorSync.value = UNIHUB_LED_MODE_METEOR_SYNC | 0x0100; + MeteorSync.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + MeteorSync.speed_min = 1; + MeteorSync.speed_max = 5; + MeteorSync.colors_min = 2; + MeteorSync.colors_max = 2; + MeteorSync.color_mode = MODE_COLORS_MODE_SPECIFIC; + MeteorSync.colors.resize(2); + modes.push_back(MeteorSync); + + mode Firework = makeMode(); + Firework.name = "Firework"; + Firework.value = UNIHUB_LED_MODE_FIREWORK; + Firework.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Firework.speed_min = 1; + Firework.speed_max = 5; + Firework.colors_min = 2; + Firework.colors_max = 2; + Firework.color_mode = MODE_COLORS_MODE_SPECIFIC; + Firework.colors.resize(2); + modes.push_back(Firework); + + mode Stack = makeMode(); + Stack.name = "Stack"; + Stack.value = UNIHUB_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Stack.speed_min = 1; + Stack.speed_max = 5; + Stack.colors_min = 1; + Stack.colors_max = 1; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(1); + modes.push_back(Stack); + + mode StackMultiColor = makeMode(); + StackMultiColor.name = "Stack Multi Color"; + StackMultiColor.value = UNIHUB_LED_MODE_STACK_MULTI_COLOR; + StackMultiColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + StackMultiColor.speed_min = 1; + StackMultiColor.speed_max = 5; + StackMultiColor.color_mode = MODE_COLORS_NONE; + modes.push_back(StackMultiColor); + + mode Neon = makeMode(); + Neon.name = "Neon"; + Neon.value = UNIHUB_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = 1; + Neon.speed_max = 5; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode Rgbh = makeMode(); + Rgbh.name = "RGB Header"; + Rgbh.value = UNIHUB_LED_MODE_STATIC_COLOR | 0x0200; + Rgbh.flags = 0; + Rgbh.color_mode = MODE_COLORS_NONE; + modes.push_back(Rgbh); + + RGBController_LianLiUniHub::SetupZones(); +} + +void RGBController_LianLiUniHub::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(UNIHUB_CHANNEL_COUNT); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + int addressableCounter = 1; + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(addressableCounter)); + + addressableCounter++; + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_CHANLED_COUNT; + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + zones[channel_idx].matrix_map = NULL; + } + + SetupColors(); +} + +void RGBController_LianLiUniHub::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHub::DeviceUpdateLEDs() +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + for(size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + uniHub->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + uniHub->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + } + + uniHub->Synchronize(); +} + +void RGBController_LianLiUniHub::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = zone; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + uniHub->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + uniHub->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + uniHub->Synchronize(); +} + +void RGBController_LianLiUniHub::UpdateSingleLED(int led) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = leds[led].value; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + uniHub->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + uniHub->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + uniHub->Synchronize(); +} + +void RGBController_LianLiUniHub::DeviceUpdateMode() +{ + initializedMode = true; + + for (size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + uniHub->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + + switch (modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + uniHub->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + break; + + case MODE_COLORS_MODE_SPECIFIC: + uniHub->SetLedColors(channel, modes[active_mode].colors.data(), modes[active_mode].colors.size()); + break; + + default: + uniHub->SetLedColors(channel, nullptr, 0); + break; + } + + uniHub->SetLedMode(channel, modes[active_mode].value); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + uniHub->SetLedSpeed(channel, convertLedSpeed(modes[active_mode].speed)); + } + else + { + uniHub->SetLedSpeed(channel, UNIHUB_LED_SPEED_000); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + uniHub->SetLedDirection(channel, convertLedDirection(modes[active_mode].direction)); + } + else + { + uniHub->SetLedDirection(channel, UNIHUB_LED_DIRECTION_LTR); + } + } + + if(modes[active_mode].value & 0x0200) + { + uniHub->EnableRgbhMode(); + uniHub->DisableSyncMode(); + } + else if (modes[active_mode].value & 0x0100) + { + uniHub->DisableRgbhMode(); + uniHub->EnableSyncMode(); + } + else + { + uniHub->DisableRgbhMode(); + uniHub->DisableSyncMode(); + } + + uniHub->Synchronize(); +} + +void RGBController_LianLiUniHub::SetCustomMode() +{ + /*-------------------------------------------------*\ + | Set mode to Static Color | + \*-------------------------------------------------*/ + active_mode = 0; +} + +uint8_t RGBController_LianLiUniHub::convertAnyFanCount(uint8_t count) +{ + switch (count) + { + case 0: + return UNIHUB_ANY_FAN_COUNT_000; + + case 1: + return UNIHUB_ANY_FAN_COUNT_001; + + case 2: + return UNIHUB_ANY_FAN_COUNT_002; + + case 3: + return UNIHUB_ANY_FAN_COUNT_003; + + case 4: + return UNIHUB_ANY_FAN_COUNT_004; + + default: + return UNIHUB_ANY_FAN_COUNT_001; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedSpeed(uint8_t speed) +{ + switch (speed) + { + case 1: + return UNIHUB_LED_SPEED_000; + + case 2: + return UNIHUB_LED_SPEED_025; + + case 3: + return UNIHUB_LED_SPEED_050; + + case 4: + return UNIHUB_LED_SPEED_075; + + case 5: + return UNIHUB_LED_SPEED_100; + + default: + return UNIHUB_LED_SPEED_050; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedDirection(uint8_t direction) +{ + switch (direction) + { + case 0: + return UNIHUB_LED_DIRECTION_LTR; + + case 1: + return UNIHUB_LED_DIRECTION_RTL; + + default: + return UNIHUB_LED_DIRECTION_LTR; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedCountToFanCount(uint8_t count) +{ + /*-------------------------------------------------*\ + | Converts 0 to 0, 1-16 to 1, 17-32 to 2, 33-48 to | + | 3 and 49-64+ to 4 | + \*-------------------------------------------------*/ + if (count == 0x00) + { + return 0x00; + } + if (count >= 0x40) + { + count = 0x40; + } + + return((count -1) / 16 + 1); +} diff --git a/Controllers/LianLiController/RGBController_LianLiUniHub.h b/Controllers/LianLiController/RGBController_LianLiUniHub.h new file mode 100644 index 00000000..8627bc74 --- /dev/null +++ b/Controllers/LianLiController/RGBController_LianLiUniHub.h @@ -0,0 +1,45 @@ +/*-----------------------------------------*\ +| RGBController_LianLiUniHub.h | +| | +| Generic RGB Interface for Lian Li Uni | +| Hub USB controller driver | +| | +| Luca Lovisa 2/20/2021 | +\*-----------------------------------------*/ + +#pragma once + +#include +#include + +#include "LianLiUniHubController.h" +#include "RGBController.h" + +class RGBController_LianLiUniHub : public RGBController +{ +public: + RGBController_LianLiUniHub(LianLiUniHubController* uniHub_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void SetCustomMode(); + +private: + uint8_t convertAnyFanCount(uint8_t count); + uint8_t convertLedSpeed(uint8_t speed); + uint8_t convertLedDirection(uint8_t direction); + + uint8_t convertLedCountToFanCount(uint8_t count); + +private: + LianLiUniHubController* uniHub; + bool initializedMode; +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index 4b89c26c..01244a4d 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -91,6 +91,7 @@ INCLUDEPATH += Controllers/HyperXMouseController/ \ Controllers/HyperXMousematController/ \ Controllers/LEDStripController/ \ + Controllers/LianLiController/ \ Controllers/LogitechController/ \ Controllers/MSI3ZoneController/ \ Controllers/MSIGPUController/ \ @@ -255,6 +256,8 @@ HEADERS += Controllers/HyperXMousematController/RGBController_HyperXMousemat.h \ Controllers/LEDStripController/LEDStripController.h \ Controllers/LEDStripController/RGBController_LEDStrip.h \ + Controllers/LianLiController/LianLiUniHubController.h \ + Controllers/LianLiController/RGBController_LianLiUniHub.h \ Controllers/LogitechController/LogitechG203Controller.h \ Controllers/LogitechController/LogitechG203LController.h \ Controllers/LogitechController/LogitechG213Controller.h \ @@ -523,6 +526,9 @@ SOURCES += Controllers/LEDStripController/LEDStripController.cpp \ Controllers/LEDStripController/LEDStripControllerDetect.cpp \ Controllers/LEDStripController/RGBController_LEDStrip.cpp \ + Controllers/LianLiController/LianLiControllerDetect.cpp \ + Controllers/LianLiController/LianLiUniHubController.cpp \ + Controllers/LianLiController/RGBController_LianLiUniHub.cpp \ Controllers/LogitechController/LogitechControllerDetect.cpp \ Controllers/LogitechController/LogitechG203Controller.cpp \ Controllers/LogitechController/LogitechG203LController.cpp \