Compare commits

...
Author SHA1 Message Date
Adam Honse 253f74f5bd Add PowerShell implementation of build-msi for building MSI on Windows, add additional information about PawnIO and the OpenRGB system service to the installer. 2026-06-02 21:45:39 -05:00
Adam Honse b03cd94ae8 Fix incorrect firmware packet format and add timeouts to HID reads in ThermaltakeRiingController 2026-06-02 01:07:33 -05:00
Jefferson González bbf348d273 Add HyperX Eve 1800 keyboard support 2026-06-02 00:15:19 -05:00
David Glushkov dce48eb415 Add MSI Raider A18 HX A9WJG SteelSeries RGB 2026-06-02 00:05:18 -05:00
Ox HaK decf14635d Add Fnatic Streak65 support 2026-06-01 08:27:17 -05:00
Richard Harris db4f49bbba Implement Direct mode support for Roccat Vulcan TKL (non-Pro) 2026-05-31 12:38:57 -05:00
HorrorTroll 26824fcb6e Added support for Colorful iGame GeForce RTX 5060 Ultra W OC 2026-05-31 15:12:36 +07:00
Jerry Fan 8afad91b33 Add support for RealtekBridgeController 2026-05-29 07:46:22 -05:00
Adam Honse 69de0b13c4 Update PawnIO modules to 0.2.6 and enable Intel Skylake IMC SMBus module 2026-05-28 23:48:42 -05:00
alstruit 967153bc10 Add support for ASUS ROG Gladius III Core 2026-05-25 12:08:43 -05:00
JAO1988 971d9be1bd Added Colorful iGame RTX 5060TI 16GB Ultra W DUO Variant 2026-05-23 23:10:36 -05:00
Mark Willis 7a3faef886 Initial commit for ASUS TUF GeForce RTX 5070 Ti Gaming White OC 2026-05-22 12:25:59 -05:00
Purrple Kitten f67030fcc7 Add support for the Gigabyte AORUS RTX 5080 MASTER ICE 2026-05-18 07:30:37 -05:00
Alex Jawhari 7353e99449 Fix AMD GPU i2c bus PCI ID detection on Linux (sysfs path traversal) 2026-05-17 23:58:21 -05:00
JAO1988 91d9597c89 Added Support for ASUS ROG ASTRAL GeForce RTX 5080 WHITE 2026-05-17 23:47:55 -05:00
Adam Honse 2eb569912b XPGSummonerKeyboardController - Remove fake modes 2026-05-13 11:49:05 -05:00
Ken Sanislo 9410d9c066 Add support for Thrustmaster Sol series joystick RGB LEDs 2026-05-09 00:21:40 -05:00
Adam Honse 29d8788e25 Allow value 0x27 at address 0x00 for Galax GPU controllers at address 0x23 2026-05-08 20:05:40 -05:00
Elvis Pranskevichus 0304c50bc6 Add support for Gigabyte AORUS RX 7900 XTX ELITE 24G 2026-05-08 00:12:43 -05:00
Adam Honse dd22150cfe Add vertical spacer to OpenRGBDeviceInfoPage 2026-05-07 16:17:46 -05:00
Barry H 07e7feca56 Nollie 2.1 firmware for Master 2026-05-04 14:16:29 -05:00
Sha_425 a93eec8f50 Add ASUS ROG STRIX RTX 3060 Ti V2 LHR (subsystem 1043:8835) 2026-05-03 12:15:13 -05:00
w43322 88bad762e5 add MSI MEG Z890 UNIFY-X (MS-7E20) support 2026-05-02 19:19:10 -05:00
w43322 18dfe6ca8b add ASUS TUF GeForce RTX 5060 Gaming OC support 2026-05-02 19:15:16 -05:00
mangobiche 96dfa7009f Add support for Corsair Vengeance RGB DDR5 CMH32GX5M2E6000C36 2026-05-02 15:06:09 -05:00
NaifuKishi 92a9d8ad11 Initial commit for ASUS TUF Radeon RX 7900 GRE Gaming OC 2026-05-01 11:09:31 -05:00
Francesco P 2ab9094b41 Add support for MSI MAG B850 TOMAHAWK WiFi7 2026-04-29 21:54:51 -05:00
M1dDler Official f6154ccbc4 Add msi rtx 5070 gaming trio 2026-04-28 20:04:53 -05:00
Illustrious-Net-8110 0be01dde19 Add Asus Ryujin AIO Gen1 support 2026-04-24 07:35:53 -05:00
Adam Honse e113e91423 Deploy QOpenGL and QtCore5Compat frameworks used by the OpenRGB Effects Plugin 2026-04-23 23:42:36 -05:00
Adam Honse 28c63b8b9d Call macdeployqt a second time on Qt6 builds to ensure frameworks depended on by default plugins are deployed 2026-04-23 12:37:49 -05:00
Adam Honse c5aac4c44f Fix libsharpyuv macdeployqt error for Qt5 builds 2026-04-23 10:52:54 -05:00
Adam Honse 42e7f2a5c3 Ignore SDK version in Mac build 2026-04-23 03:56:07 -05:00
Adam Honse ae9ae18d31 Add build-macos.sh script for easier MacOS builds 2026-04-23 00:37:45 -05:00
ricarim f89cdac198 Initial commit for HyperX Origins 2 65 2026-04-20 23:32:49 -05:00
acehighdan e8f345ff77 Sync autostart system configuration with OpenRGB settings on startup
If the autostart system configuration is deleted (e.g. by a reinstall or
package manager) but the JSON config still has enabled=true, OpenRGB
would silently fail to start at login. Fix by calling ConfigureAutoStart()
in the settings page constructor so the system configuration is always
reconciled with the stored JSON state every time OpenRGB starts.

Commit amended to make wording more generic by Adam Honse <[email protected]>
2026-04-20 23:32:49 -05:00
Dmitry Kychanov aff0610df9 Fix default language load on start 2026-04-20 23:32:49 -05:00
JAO1988 9199d062d1 Add Support for MSI GeForce RTX 5080 SUPRIM LIQUID SOC 2026-04-20 23:32:49 -05:00
Dmitry Kychanov 0ec4aaa629 Fix losing software info upon changing language 2026-04-20 23:32:49 -05:00
Adam Honse 9fca99b4ef Reduce i2c_smbus_linux PCI log messages from warning to info, they are expected in many cases 2026-04-20 23:32:31 -05:00
Vasily Galkin 14853895f3 Add support for Gigabyte GTX 1050 G1 Gaming 2GB (1458:372B) 2026-04-20 23:32:31 -05:00
Adam Honse d79a400c8c Fix corrupted text in TabLabels when translation occurs after the constructor 2026-04-20 23:32:31 -05:00
Adam Honse f741385e43 Fix bug introduced in MSI controller merge 2026-04-20 23:32:31 -05:00
Adam Honse 840bd92afb Add AI guidelines section to CONTRIBUTING.md 2026-04-20 23:32:31 -05:00
Nameless Monarch 96e1fb0b10 Add support for MSI PRO B850M-VC WIFI6E 2026-04-20 23:32:31 -05:00
Adam Honse 388557e5f9 Ensure device view is initialized if there are no LEDs to avoid accessing out of bounds 2026-04-20 23:32:17 -05:00
Slim Amamou 3f32817272 Added support for PNY GeForce RTX 5060Ti ARGB Epic-X OC 2026-04-20 23:32:10 -05:00
Adam Honse 056b383536 Add SMBus Sleep Mode settings page 2026-04-15 00:29:54 -05:00
Adam Honse 0691f40588 Update PawnIO modules to release 0.2.4 and implement SMBus sleep mode setting 2026-04-15 00:03:38 -05:00
Adam Honse 800ca0aa7c Fix same heap buffer overflow in RGBController_MSIMysticLight761.cpp 2026-04-13 11:42:03 -05:00
kuroki-kael a91679609b Fix heap-buffer-overflow in RGBController_MSIMysticLight185::GetDeviceConfig() 2026-04-13 11:40:46 -05:00
Adam Honse 19f9408990 Fix remaining warning 2026-04-13 11:34:28 -05:00
Adam Honse 0eea0c9aca Fix warnings 2026-04-13 10:54:34 -05:00
Stella 9f95aa8271 Add support for colorful igame rtx 5070 ultraw ocv 2 2026-04-11 02:24:15 -05:00
Adam Honse 0ee04c497b Revert "Change test in ENE SMBus controller to fix a conflict where it locks up Corsair DDR5 Light Enhancement Kit sticks"
This reverts commit c4e0164479.
2026-04-11 01:29:05 -05:00
Adam Honse 2847270c97 Remove out-of-range addresses from ENE RAM address list 2026-04-10 11:52:50 -05:00
Adam Honse b98174afba Fix off-by-one error in CorsairDRAMController 2026-04-10 11:03:34 -05:00
MissingDLL 3416980849 Add MSI MPG B850 EDGE TI WIFI (MS-7E62) to 761 controller 2026-04-09 20:49:35 -05:00
Joseph E c171bfbf3b Apex Pro Gen 3 TKL wired, add media light and regional layout support 2026-04-09 18:11:52 -05:00
Eder Sánchez 583a737cb5 Fix Windows build in ZOTAC Blackwell GPU controller 2026-04-09 15:03:21 -05:00
rjc862003 d2af6289dd Roccat vulcan ii max support 2026-04-09 13:28:33 -05:00
Moh'd Khier Abualruz 44e6f455e9 Fix Apex Pro TKL 2023 Wireless (PIDs 0x1630/0x1632) RGB control 2026-04-09 13:18:38 -05:00
Eder Sánchez b3a50d669e Add support for ZOTAC GAMING GeForce RTX 5080 AMP Extreme INFINITY 2026-04-09 12:21:52 -05:00
ricarim 0092ea22e2 Fix name assignment for HyperXAlloyOrigins60and65 2026-04-09 10:14:06 -05:00
Jérôme Bauer 58fb35a9bb Fix MSI Mystic Light 185 for Common PID boards (0x0DB0:0x0076) 2026-04-09 10:09:55 -05:00
Adam Honse 9a6ff77545 Combine Corsair RAM controllers, Corsair RAM identification packet testing 2026-04-09 00:47:06 -05:00
Adam Honse c4e0164479 Change test in ENE SMBus controller to fix a conflict where it locks up Corsair DDR5 Light Enhancement Kit sticks 2026-04-08 22:24:56 -05:00
Adam Honse f872f1c591 Ignore modes out of range in VialRGB controller 2026-04-08 18:50:47 -05:00
kuroki-kael 03e64da845 Fix heap corruption in i2c_smbus_linux when sysfs files are empty 2026-04-08 15:54:38 -05:00
Frans Meulenbroeks 3150f64895 Mountain Everest 60: fix led index 2026-04-08 09:33:56 -05:00
Hugo Chabert 8b36e9c317 Add razer blackwidow v4 tenkeyless hyperspeed 2026-04-07 07:30:59 -05:00
Stella c5c5fc6c02 Add support for Colorful iGAME RTX 5060Ti 16GB Ultra W DUO V 2026-04-05 03:46:51 -05:00
Artemis Tomaras c7d9992a3d Fix SL V2 SendStartAction/SendCommitAction packet size 2026-04-05 01:52:56 -05:00
InterStella0 0d8f2c6b14 add colorful rtx 5070 ultra w ocv support 2026-04-03 18:22:49 -05:00
vitutu1 c1800f2a6f Add support for MSI RTX 4070 SUPER Gaming X Slim MLG 2026-04-03 16:46:23 +00:00
Micael Pereira d41a115a07 Add support for MSI PRO A620M-B (MS-7E28) 2026-04-03 01:31:28 -05:00
Elijah Mock 2640a3b93f Update docstring detector to match registered detector 2026-03-26 21:50:02 -05:00
Adam Honse 984c18cb11 Add Gigabyte 9070XT GAMING OC ICE 2026-03-23 16:44:30 -05:00
Adam Honse 23db15dfb9 Add support for E6K5-1114 ENE device 2026-03-23 11:46:04 -05:00
Org van Rensburg 9426f954b0 Add support for MSI MAG Z890 Tomahawk WIFI (MS-7E32) - Resolves #5438 2026-03-23 07:24:22 -05:00
asheriif 133b64778b Add support for Apex Pro TKL Gen 3 Wireless 2026-03-22 22:19:37 +01:00
Aakrisht Sharma Paudel a8a97fa38d Legion 5 gen10 2026-03-17 07:50:23 -05:00
Roy Biggins da6061bbde Add X570 AORUS XTREME zone mapping for IT8297BX 2026-03-16 10:29:38 -05:00
124 changed files with 7750 additions and 2006 deletions
+4 -16
View File
@@ -516,10 +516,7 @@ before_script:
- macos
stage: build
script:
- eval $(/opt/homebrew/bin/brew shellenv)
- /opt/homebrew/opt/qt@5/bin/qmake OpenRGB.pro
- make -j16
- /opt/homebrew/opt/qt@5/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
- ./scripts/build-macos.sh qt5 arm
artifacts:
name: "${CI_PROJECT_NAME}_MacOS_ARM64_${CI_COMMIT_SHORT_SHA}"
@@ -535,10 +532,7 @@ before_script:
- macos
stage: build
script:
- eval $(/opt/homebrew/bin/brew shellenv)
- qmake OpenRGB.pro
- make -j16
- macdeployqt OpenRGB.app -codesign=OpenRGB
- ./scripts/build-macos.sh qt6 arm
artifacts:
name: "${CI_PROJECT_NAME}_MacOS_ARM64_Qt6_${CI_COMMIT_SHORT_SHA}"
@@ -554,10 +548,7 @@ before_script:
- macos
stage: build
script:
- eval $(/usr/local/bin/brew shellenv)
- arch -x86_64 /usr/local/opt/qt@5/bin/qmake OpenRGB.pro
- arch -x86_64 make -j16
- arch -x86_64 /usr/local/opt/qt@5/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
- ./scripts/build-macos.sh qt5 intel
artifacts:
name: "${CI_PROJECT_NAME}_MacOS_Intel_${CI_COMMIT_SHORT_SHA}"
@@ -573,10 +564,7 @@ before_script:
- macos
stage: build
script:
- eval $(/usr/local/bin/brew shellenv)
- arch -x86_64 /usr/local/bin/qmake OpenRGB.pro
- arch -x86_64 make -j16
- arch -x86_64 /usr/local/bin/macdeployqt OpenRGB.app -codesign=OpenRGB
- ./scripts/build-macos.sh qt6 intel
artifacts:
name: "${CI_PROJECT_NAME}_MacOS_Intel_Qt6_${CI_COMMIT_SHORT_SHA}"
+1 -2
View File
@@ -22,13 +22,12 @@ CODEOWNERS @Calcprogrammer1
/Controllers/BlinkyTapeController/
/Controllers/CoolerMasterController/ @Dr_No
/Controllers/CorsairCommanderCoreController/
/Controllers/CorsairDominatorPlatinumController/
/Controllers/CorsairDRAMController/
/Controllers/CorsairHydroController/
/Controllers/CorsairHydroPlatinumController/
/Controllers/CorsairLightingNodeController/
/Controllers/CorsairPeripheralController/
/Controllers/CorsairVengeanceController/
/Controllers/CorsairVengeanceProController/
/Controllers/CorsairWirelessController/
/Controllers/CreativeController/
/Controllers/CrucialController/
+4
View File
@@ -69,3 +69,7 @@ OpenRGB is written in C++, uses the Qt framework for UI, and uses the QMake buil
Translation files are located in [`OpenRGB/qt/i18n/`](https://gitlab.com/CalcProgrammer1/OpenRGB/-/tree/master/qt/i18n), where languages are formatted using ISO 639-1 format: `OpenRGB_xx_XX.ts` — `xx_XX` representing the language code.
In order to translate a file, you need to [fork](https://gitlab.com/CalcProgrammer1/OpenRGB/-/forks/new) the project, create a new file for your language with `lupdate` (or edit an exisiting one), edit the file with `qtlinguist`, commit, push, and create a merge request.
## AI Guidelines
OpenRGB is an open source project developed by humans for humans. As a general rule, AI generated submissions are not permitted. The licensing behind AI generated code is problematic. If you choose to use AI for assistance in your development process, we can't stop you, but do not credit the AI in commits (no AI authorship/co-authorship). Ultimately, you as a human developer are responsible for the code you submit and it is expected that any code you submit you fully understand and have manually vetted before submission. Merge requests that appear to be straight from an AI output, commits with AI tool authorship or co-authorship tags, or otherwise AI generated submissions are subject to closure.
@@ -22,6 +22,7 @@
#define AURA_ROG_GLADIUS_II_WIRELESS_1_PID 0x189E
#define AURA_ROG_GLADIUS_II_WIRELESS_2_PID 0x18A0
#define AURA_ROG_GLADIUS_III_PID 0x197B
#define AURA_ROG_GLADIUS_III_CORE_PID 0x1C8D
#define AURA_ROG_GLADIUS_III_WIRELESS_USB_PID 0x197D
#define AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID 0x197F
#define AURA_ROG_GLADIUS_III_WIRELESS_BT_PID 0x1981
@@ -217,6 +218,20 @@ static std::map<int,mouse_type> aura_mouse_devices =
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY }
}
},
{
AURA_ROG_GLADIUS_III_CORE_PID, // ROG Gladius III Core
{
0,
0,
0,
100,
false,
1,
false,
{ AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL },
{ AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE }
}
},
{
AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, // ROG Gladius III Wireless USB
{
@@ -19,7 +19,10 @@ AsusAuraRyuoAIOController::AsusAuraRyuoAIOController(hid_device* dev_handle, con
| Manually adding device info for now |
| TODO: Implement config table accurately |
\*-----------------------------------------------------*/
device_info.push_back({0x00, 0x00, 12, 0, AuraDeviceType::FIXED});
uint8_t leds = (dev_name.find("Ryujin") != std::string::npos) ? 5 : 12;
device_info.push_back({0x00, 0x00, leds, 0, AuraDeviceType::FIXED});
}
AsusAuraRyuoAIOController::~AsusAuraRyuoAIOController()
@@ -120,6 +120,7 @@
#define AURA_TERMINAL_PID 0x1889
#define ROG_STRIX_LC120_PID 0x879E
#define AURA_RYUO_AIO_PID 0x1887
#define AURA_RYUJIN_AIO_PID 0x18AE
#define ASUS_ROG_ALLY_PID 0x1ABE
#define ASUS_ROG_ALLY_X_PID 0x1B4C
@@ -393,6 +394,7 @@ REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin COD",
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_1_PID, 1, 0xFF13);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_2_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_CORE_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID, 0, 0xFF01);
REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless Bluetooth", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_BT_PID, 0, 0xFF01);
@@ -453,6 +455,7 @@ REGISTER_HID_DETECTOR_PU("ASUS ROG PG32UQ", DetectAs
REGISTER_HID_DETECTOR ("ASUS ROG AURA Terminal", DetectAsusAuraUSBTerminal, AURA_USB_VID, AURA_TERMINAL_PID);
REGISTER_HID_DETECTOR_PU ("ASUS ROG Strix LC", DetectAsusAuraUSBROGStrixLC, AURA_USB_VID, ROG_STRIX_LC120_PID, 0x00FF, 1);
REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryuo AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUO_AIO_PID, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryujin AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUJIN_AIO_PID, 0xFF72, 0x00A1);
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_PID, 0);
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_PID, 0);
REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI GUNDAM", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_GUNDAM_PID, 0);
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| ColorfulGPUControllerDetect.cpp |
| |
| Detector for Colorful GPU |
@@ -48,7 +48,7 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Advanced OC 12G L-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060TI_ADVANCED_OC, 0x61);
@@ -72,3 +72,8 @@ REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", Detec
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC_2, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV, 0x61);
REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2, 0x61);
@@ -0,0 +1,422 @@
/*---------------------------------------------------------*\
| CorsairDRAMController.cpp |
| |
| Driver for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "CRC.h"
#include "CorsairDRAMController.h"
#include "LogManager.h"
#define CORSAIR_DRAM_NAME "Corsair DRAM"
using namespace std::chrono_literals;
CorsairDRAMController::CorsairDRAMController(i2c_smbus_interface *bus, corsair_dev_id dev)
{
/*-----------------------------------------------------*\
| Initialize class variables |
\*-----------------------------------------------------*/
this->bus = bus;
this->dev = dev;
device_index = 0;
pid = 0;
vid = 0;
protocol_version = 0;
/*-----------------------------------------------------*\
| Read device information |
\*-----------------------------------------------------*/
ReadDeviceInfo();
}
CorsairDRAMController::~CorsairDRAMController()
{
}
unsigned int CorsairDRAMController::GetLEDCount()
{
return(corsair_dram_device_list[device_index]->led_count);
}
unsigned char CorsairDRAMController::GetProtocolVersion()
{
return(protocol_version);
}
std::string CorsairDRAMController::GetDeviceLocation()
{
std::string return_string(bus->device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
std::string CorsairDRAMController::GetDeviceName()
{
return(corsair_dram_device_list[device_index]->name);
}
std::string CorsairDRAMController::GetDeviceVersion()
{
return(firmware_version);
}
void CorsairDRAMController::SetColorsPerLED(RGBColor* colors)
{
/*-----------------------------------------------------*\
| Get LED count from device list |
\*-----------------------------------------------------*/
unsigned int led_count = corsair_dram_device_list[device_index]->led_count;
if(direct_mode)
{
/*-------------------------------------------------*\
| Sanity check - Direct mode can only be used on |
| protocol 4+ |
\*-------------------------------------------------*/
if(protocol_version < 4)
{
LOG_ERROR("[%s] Protocol version %d tried to use direct mode, ignoring", CORSAIR_DRAM_NAME, protocol_version);
return;
}
/*-------------------------------------------------*\
| Packet format: |
| Size n is (LED count * 3) + 2 |
| 0: Command byte (0x0A or 0x0C) |
| 1 to (n-2): LED color data in R/G/B order |
| (n-1): CRC8 of bytes 0 to (n-2) |
\*-------------------------------------------------*/
unsigned int direct_packet_size = (led_count * 3) + 2;
unsigned char* direct_packet = new unsigned char[direct_packet_size];
/*-------------------------------------------------*\
| First byte in packet is LED count |
\*-------------------------------------------------*/
direct_packet[0] = led_count;
/*-------------------------------------------------*\
| Fill in LED data |
\*-------------------------------------------------*/
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
unsigned int color_index = led_idx;
unsigned int offset = (led_idx * 3) + 1;
if(corsair_dram_device_list[device_index]->reverse)
{
color_index = (led_count -1) - led_idx;
}
direct_packet[offset + 0] = RGBGetRValue(colors[color_index]);
direct_packet[offset + 1] = RGBGetGValue(colors[color_index]);
direct_packet[offset + 2] = RGBGetBValue(colors[color_index]);
}
/*-------------------------------------------------*\
| Last byte in packet is CRC of all data up to it |
\*-------------------------------------------------*/
direct_packet[direct_packet_size - 1] = CRCPP::CRC::Calculate(direct_packet, (direct_packet_size - 1), CRCPP::CRC::CRC_8());
/*-------------------------------------------------*\
| Write using block writes, if packet exceeds 32 |
| bytes, use a second block write to the second |
| block write address for the remaining data |
\*-------------------------------------------------*/
bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1, 32, direct_packet);
if(direct_packet_size > 32)
{
bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_2, direct_packet_size - 32, direct_packet + 32);
}
/*-------------------------------------------------*\
| Remember to delete the data buffer |
\*-------------------------------------------------*/
delete[] direct_packet;
}
else
{
/*-------------------------------------------------*\
| Local variables |
\*-------------------------------------------------*/
unsigned char device_crc;
unsigned char calc_crc;
/*-------------------------------------------------*\
| Packet format: |
| Size n is (LED count * 4) |
| Format is 0xRR, 0xGG, 0xBB, 0xFF for each LED |
\*-------------------------------------------------*/
unsigned int color_data_size = (led_count * 4);
unsigned char* color_data_packet = new unsigned char[color_data_size];
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
unsigned int color_index = led_idx;
if(corsair_dram_device_list[device_index]->reverse)
{
color_index = (led_count -1) - led_idx;
}
color_data_packet[(led_idx * 4) + 0] = RGBGetRValue(colors[color_index]);
color_data_packet[(led_idx * 4) + 1] = RGBGetGValue(colors[color_index]);
color_data_packet[(led_idx * 4) + 2] = RGBGetBValue(colors[color_index]);
color_data_packet[(led_idx * 4) + 3] = 0xFF;
}
/*-------------------------------------------------*\
| Write LED color data packet |
\*-------------------------------------------------*/
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_RESET_BUFFER, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00);
for(unsigned int i = 0; i < color_data_size; i++)
{
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_SET_BINARY_DATA, color_data_packet[i]);
}
/*-------------------------------------------------*\
| Calculate CRC and read CRC reported by device |
\*-------------------------------------------------*/
calc_crc = CRCPP::CRC::Calculate(color_data_packet, color_data_size, CRCPP::CRC::CRC_8());
device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM);
/*-------------------------------------------------*\
| Write effect configuration only if CRCs match |
\*-------------------------------------------------*/
if(calc_crc == device_crc)
{
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_WRITE_CONFIGURATION, CORSAIR_DRAM_ID_COLOR_DATA);
WaitReady();
}
/*-------------------------------------------------*\
| Remember to delete the data buffer |
\*-------------------------------------------------*/
delete[] color_data_packet;
}
}
void CorsairDRAMController::SetDirect(bool direct)
{
direct_mode = direct;
}
void CorsairDRAMController::SetEffect
(
unsigned char mode,
unsigned char speed,
unsigned char direction,
bool random,
unsigned char brightness,
unsigned char red1,
unsigned char grn1,
unsigned char blu1,
unsigned char red2,
unsigned char grn2,
unsigned char blu2
)
{
/*-----------------------------------------------------*\
| Local variables |
\*-----------------------------------------------------*/
unsigned char effect_data[20];
unsigned char device_crc;
unsigned char calc_crc;
unsigned char random_byte;
/*-----------------------------------------------------*\
| If mode is direct (which is a dummy value not |
| understood by the hardware), return. Direct mode is |
| not set in the effect configuration. |
\*-----------------------------------------------------*/
direct_mode = (mode == CORSAIR_DRAM_MODE_DIRECT);
if(direct_mode)
{
return;
}
/*-----------------------------------------------------*\
| Determine random byte |
\*-----------------------------------------------------*/
if(random)
{
random_byte = CORSAIR_DRAM_EFFECT_RANDOM_COLORS;
}
else
{
random_byte = CORSAIR_DRAM_EFFECT_CUSTOM_COLORS;
}
/*-----------------------------------------------------*\
| Fill in effect packet |
\*-----------------------------------------------------*/
effect_data[0] = mode; // Mode
effect_data[1] = speed; // Speed
effect_data[2] = random_byte; // Custom color
effect_data[3] = direction; // Direction
effect_data[4] = red1; // Custom color 1 red
effect_data[5] = grn1; // Custom color 1 green
effect_data[6] = blu1; // Custom color 1 blue
effect_data[7] = brightness;
effect_data[8] = red2; // Custom color 2 red
effect_data[9] = grn2; // Custom color 2 green
effect_data[10] = blu2; // Custom color 2 blue
effect_data[11] = brightness;
effect_data[12] = 0x00;
effect_data[13] = 0x00;
effect_data[14] = 0x00;
effect_data[15] = 0x00;
effect_data[16] = 0x00;
effect_data[17] = 0x00;
effect_data[18] = 0x00;
effect_data[19] = 0x00;
/*-----------------------------------------------------*\
| Write effect packet |
\*-----------------------------------------------------*/
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_RESET_BUFFER, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00);
for(unsigned int i = 0; i < 20; i++)
{
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_SET_BINARY_DATA, effect_data[i]);
}
/*-----------------------------------------------------*\
| Calculate CRC and read CRC reported by device |
\*-----------------------------------------------------*/
calc_crc = CRCPP::CRC::Calculate(effect_data, sizeof(effect_data), CRCPP::CRC::CRC_8());
device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM);
/*-----------------------------------------------------*\
| Write effect configuration only if CRCs match |
\*-----------------------------------------------------*/
if(calc_crc == device_crc)
{
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_WRITE_CONFIGURATION, CORSAIR_DRAM_ID_EFFECT_CONFIGURATION);
WaitReady();
}
}
bool CorsairDRAMController::WaitReady()
{
/*-----------------------------------------------------*\
| Poll status register 0x30; bit 3 (0x08) = busy. |
| Device is ready when bit 3 is clear. |
\*-----------------------------------------------------*/
for(int retry = 0; retry < 5; retry++)
{
int status = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_STATUS);
if(status >= 0 && (status & 0x08) == 0)
{
return true;
}
std::this_thread::sleep_for(10ms);
}
return false;
}
void CorsairDRAMController::ReadDeviceInfo()
{
unsigned char device_information_data[32];
unsigned char device_crc;
unsigned char calc_crc;
/*-----------------------------------------------------*\
| Request Device Information Data |
\*-----------------------------------------------------*/
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_GET_DEVICE_INFO, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00);
/*-----------------------------------------------------*\
| Read Device Information Data |
\*-----------------------------------------------------*/
for(unsigned int i = 0; i < 32; i++)
{
device_information_data[i] = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_BINARY_DATA);
}
/*-----------------------------------------------------*\
| Compare CRC |
\*-----------------------------------------------------*/
calc_crc = CRCPP::CRC::Calculate(device_information_data, sizeof(device_information_data), CRCPP::CRC::CRC_8());
device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM);
if(calc_crc != device_crc)
{
LOG_ERROR("[%s] ReadDeviceInfo CRC Mismatch", CORSAIR_DRAM_NAME);
}
/*-----------------------------------------------------*\
| Log Device Information Data |
\*-----------------------------------------------------*/
if(LogManager::get()->getLoglevel() >= LL_TRACE)
{
char device_info_buf[256];
unsigned int pos;
pos = snprintf(device_info_buf, sizeof(device_info_buf), "%02X: ", dev);
for(unsigned int i = 0; i < 32; i++)
{
pos += snprintf(&device_info_buf[pos], sizeof(device_info_buf) - pos, "%02X ", device_information_data[i]);
}
LOG_TRACE("[%s] Device Info: %s", CORSAIR_DRAM_NAME, device_info_buf);
}
/*-----------------------------------------------------*\
| Read VID |
\*-----------------------------------------------------*/
vid = (device_information_data[1] << 8) | device_information_data[0];
/*-----------------------------------------------------*\
| Read PID |
\*-----------------------------------------------------*/
pid = (device_information_data[3] << 8) | device_information_data[2];
/*-----------------------------------------------------*\
| Format Firwmare Version |
\*-----------------------------------------------------*/
firmware_version = std::to_string(device_information_data[9]) + "." + std::to_string(device_information_data[8]) + "." + std::to_string((device_information_data[11] << 8) | device_information_data[10]);
/*-----------------------------------------------------*\
| Read Protocol Version |
\*-----------------------------------------------------*/
protocol_version = device_information_data[28];
/*-----------------------------------------------------*\
| Loop through all known devices to look for a PID |
| match |
\*-----------------------------------------------------*/
for(unsigned int i = 0; i < CORSAIR_DRAM_NUM_DEVICES; i++)
{
for(unsigned int j = 0; j < CORSAIR_DRAM_MAX_PIDS; j++)
{
if(corsair_dram_device_list[i]->pids[j] == pid)
{
/*-----------------------------------------*\
| Set device ID |
\*-----------------------------------------*/
device_index = i;
break;
}
}
}
}
@@ -0,0 +1,149 @@
/*---------------------------------------------------------*\
| CorsairDRAMController.h |
| |
| Driver for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
#include "CorsairDRAMDevices.h"
#include "RGBController.h"
typedef unsigned char corsair_dev_id;
enum
{ /* (*) indicates deprecated registers no longer used by iCue */
CORSAIR_DRAM_REG_RESET_BUFFER = 0x0B, /* Reset buffer by writing 0x00 */
CORSAIR_DRAM_REG_SET_BINARY_DATA = 0x20, /* Write byte to active binary data buffer */
CORSAIR_DRAM_REG_BINARY_START = 0x21, /* Start binary data transfer by writing 0x00 */
CORSAIR_DRAM_REG_SWITCH_MODE = 0x23, /* Switch between Bootloader(0x00) and Normal(0x01) */
CORSAIR_DRAM_REG_SET_BUFFER = 0x26, /* Select configuration buffer to write by ID (*) */
CORSAIR_DRAM_REG_STATUS = 0x30, /* Status register */
CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1 = 0x31, /* Direct color buffer block register 1 */
CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_2 = 0x32, /* Direct color buffer block register 2 */
CORSAIR_DRAM_REG_GET_BINARY_DATA = 0x40, /* Read byte from active binary data buffer */
CORSAIR_DRAM_REG_BUSY_STATUS = 0x41, /* Reads nonzero while busy, zero when ready(*) */
CORSAIR_DRAM_REG_GET_CHECKSUM = 0x42, /* Get checksum (CRC8) of active binary data buffer */
CORSAIR_DRAM_REG_GET_DEVICE_INFO = 0x61, /* Select device info buffer */
CORSAIR_DRAM_REG_GET_CONFIGURATION = 0x63, /* Select configuration buffer to read by ID */
CORSAIR_DRAM_REG_WRITE_CONFIGURATION = 0x82, /* Write/Apply configuration by writing buffer ID */
};
enum
{
CORSAIR_DRAM_ID_COMMAND_LIST = 0, /* Command list */
CORSAIR_DRAM_ID_EFFECT_CONFIGURATION = 1, /* Effect configuration */
CORSAIR_DRAM_ID_COLOR_DATA = 2, /* Color data */
};
enum
{
CORSAIR_DRAM_MODE_DIRECT = 0xDD, /* Arbitrary value to compare against later. Not the actual packet */
CORSAIR_DRAM_MODE_COLOR_SHIFT = 0x00, /* Color Shift mode */
CORSAIR_DRAM_MODE_COLOR_PULSE = 0x01, /* Color Pulse mode */
CORSAIR_DRAM_MODE_RAINBOW_WAVE = 0x03, /* Rainbow Wave mode */
CORSAIR_DRAM_MODE_COLOR_WAVE = 0x04, /* Color Wave mode */
CORSAIR_DRAM_MODE_VISOR = 0x05, /* Visor mode */
CORSAIR_DRAM_MODE_RAIN = 0x06, /* Rain mode */
CORSAIR_DRAM_MODE_MARQUEE = 0x07, /* Marquee mode */
CORSAIR_DRAM_MODE_RAINBOW = 0x08, /* Rainbow mode */
CORSAIR_DRAM_MODE_SEQUENTIAL = 0x09, /* Sequential mode */
CORSAIR_DRAM_MODE_STATIC = 0x10, /* Static mode */
CORSAIR_DRAM_NUMBER_MODES = 10, /* Number of Corsair Pro modes */
};
enum
{
CORSAIR_DRAM_SPEED_SLOW = 0x00, /* Slow speed */
CORSAIR_DRAM_SPEED_MEDIUM = 0x01, /* Medium speed */
CORSAIR_DRAM_SPEED_FAST = 0x02, /* Fast speed */
};
enum
{
CORSAIR_DRAM_EFFECT_RANDOM_COLORS = 0x00, /* Random colors */
CORSAIR_DRAM_EFFECT_CUSTOM_COLORS = 0x01, /* Custom colors */
};
enum
{
CORSAIR_DRAM_DIRECTION_UP = 0x00, /* Up direction */
CORSAIR_DRAM_DIRECTION_DOWN = 0x01, /* Down direction */
CORSAIR_DRAM_DIRECTION_LEFT = 0x02, /* Left direction */
CORSAIR_DRAM_DIRECTION_RIGHT = 0x03, /* Right direction */
CORSAIR_DRAM_DIRECTION_VERTICAL = 0x01, /* Vertical direction */
CORSAIR_DRAM_DIRECTION_HORIZONTAL = 0x03, /* Horizontal direction */
};
enum
{
CORSAIR_DRAM_BRIGHTNESS_MIN = 0, /* Minimum brightness */
CORSAIR_DRAM_BRIGHTNESS_MAX = 255, /* Maximum brightness */
CORSAIR_DRAM_BRIGHTNESS_DEFAULT = 255, /* Default brightness */
};
class CorsairDRAMController
{
public:
CorsairDRAMController(i2c_smbus_interface *bus, corsair_dev_id dev);
~CorsairDRAMController();
std::string GetDeviceLocation();
std::string GetDeviceName();
std::string GetDeviceVersion();
unsigned int GetLEDCount();
unsigned char GetProtocolVersion();
void SetColorsPerLED(RGBColor* colors);
void SetDirect(bool direct);
void SetEffect(unsigned char mode,
unsigned char speed,
unsigned char direction,
bool random,
unsigned char brightness,
unsigned char red1,
unsigned char grn1,
unsigned char blu1,
unsigned char red2,
unsigned char grn2,
unsigned char blu2);
bool WaitReady();
private:
/*-----------------------------------------------------*\
| I2C |
\*-----------------------------------------------------*/
i2c_smbus_interface* bus;
corsair_dev_id dev;
/*-----------------------------------------------------*\
| State tracking |
\*-----------------------------------------------------*/
bool direct_mode;
/*-----------------------------------------------------*\
| Corsair DRAM information |
\*-----------------------------------------------------*/
unsigned short vid;
unsigned short pid;
std::string firmware_version;
unsigned char protocol_version;
unsigned int device_index;
/*-----------------------------------------------------*\
| Private functions |
\*-----------------------------------------------------*/
void ReadDeviceInfo();
};
@@ -0,0 +1,92 @@
/*---------------------------------------------------------*\
| CorsairDRAMControllerDetect.cpp |
| |
| Detector for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "Detector.h"
#include "CorsairDRAMController.h"
#include "RGBController_CorsairDRAM.h"
#include "LogManager.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
using namespace std::chrono_literals;
#define CORSAIR_DRAM_NAME "Corsair DRAM"
bool TestForCorsairDRAMController(i2c_smbus_interface *bus, unsigned char address)
{
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
LOG_DEBUG("[%s] Trying address %02X", CORSAIR_DRAM_NAME, address);
if(res < 0)
{
LOG_DEBUG("[%s] Failed: res was %04X", CORSAIR_DRAM_NAME, res);
return false;
}
res = bus->i2c_smbus_read_byte_data(address, 0x43);
if(!(res == 0x1A || res == 0x1B || res == 0x1C))
{
LOG_DEBUG("[%s] Failed: expected 0x1A, 0x1B, or 0x1C, got %04X", CORSAIR_DRAM_NAME, res);
return false;
}
res = bus->i2c_smbus_read_byte_data(address, 0x44);
if(!(res == 0x01 || res == 0x03 || res == 0x04))
{
LOG_DEBUG("[%s] Failed: expected 0x01, 0x03, or 0x04, got %04X", CORSAIR_DRAM_NAME, res);
return false;
}
return true;
}
void DetectCorsairDRAMControllers(std::vector<i2c_smbus_interface *> &busses)
{
for(unsigned int bus = 0; bus < busses.size(); bus++)
{
IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device)
{
LOG_DEBUG("[%s] Testing bus %d", CORSAIR_DRAM_NAME, bus);
std::vector<unsigned char> addresses;
for(unsigned char addr = 0x58; addr <= 0x5F; addr++)
{
addresses.push_back(addr);
}
for(unsigned char addr = 0x18; addr <= 0x1F; addr++)
{
addresses.push_back(addr);
}
for(unsigned char addr : addresses)
{
if(TestForCorsairDRAMController(busses[bus], addr))
{
CorsairDRAMController* controller = new CorsairDRAMController(busses[bus], addr);
RGBController_CorsairDRAM* rgb_controller = new RGBController_CorsairDRAM(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
std::this_thread::sleep_for(10ms);
}
}
}
}
REGISTER_I2C_DETECTOR(CORSAIR_DRAM_NAME, DetectCorsairDRAMControllers);
@@ -0,0 +1,163 @@
/*---------------------------------------------------------*\
| CorsairDRAMDevices.cpp |
| |
| Device list for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 07 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "CorsairDRAMDevices.h"
static const corsair_dram_device corsair_vengeance_pro_ddr4_device =
{
"Corsair Vengeance RGB Pro DDR4",
{
CORSAIR_VENGEANCE_PRO_DDR4_PID_1,
CORSAIR_VENGEANCE_PRO_DDR4_PID_2,
0,
0,
0,
0,
},
10,
false
};
static const corsair_dram_device corsair_dominator_platinum_ddr4_device =
{
"Corsair Dominator Platinum RGB DDR4",
{
CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_1,
CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_2,
0,
0,
0,
0,
},
12,
true
};
static const corsair_dram_device corsair_vengeance_pro_sl_ddr4_device =
{
"Corsair Vengeance RGB Pro SL DDR4",
{
CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_1,
CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_2,
0,
0,
0,
0,
},
10,
false
};
static const corsair_dram_device corsair_vengeance_rs_ddr4_device =
{
"Corsair Vengeance RGB RS DDR4",
{
CORSAIR_VENGEANCE_RS_DDR4_PID_1,
CORSAIR_VENGEANCE_RS_DDR4_PID_2,
0,
0,
0,
0,
},
6,
false
};
static const corsair_dram_device corsair_dominator_platinum_ddr5_device =
{
"Corsair Dominator Platinum RGB DDR5",
{
CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_1,
CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_2,
0,
0,
0,
0,
},
12,
true
};
static const corsair_dram_device corsair_dominator_titanium_ddr5_device =
{
"Corsair Dominator Titanium RGB DDR5",
{
CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_1,
CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_2,
CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_3,
CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_4,
0,
0,
},
12,
true
};
static const corsair_dram_device corsair_vengeance_ddr5_device =
{
"Corsair Vengeance RGB DDR5",
{
CORSAIR_VENGEANCE_DDR5_PID_1,
CORSAIR_VENGEANCE_DDR5_PID_2,
CORSAIR_VENGEANCE_DDR5_PID_3,
CORSAIR_VENGEANCE_DDR5_PID_4,
CORSAIR_VENGEANCE_DDR5_PID_5,
CORSAIR_VENGEANCE_DDR5_PID_6,
},
10,
false
};
static const corsair_dram_device corsair_vengeance_shugo_series_ddr5_device =
{
"Corsair Vengeance Shugo Series DDR5",
{
CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_1,
CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_2,
CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_3,
CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_4,
0,
0,
},
10,
false
};
static const corsair_dram_device corsair_vengeance_rs_ddr5_device =
{
"Corsair Vengeance RGB RS DDR5",
{
CORSAIR_VENGEANCE_RS_DDR5_PID_1,
CORSAIR_VENGEANCE_RS_DDR5_PID_2,
0,
0,
0,
0,
},
6,
false
};
static const corsair_dram_device* device_list[] =
{
&corsair_vengeance_pro_ddr4_device,
&corsair_dominator_platinum_ddr4_device,
&corsair_vengeance_pro_sl_ddr4_device,
&corsair_vengeance_rs_ddr4_device,
&corsair_dominator_platinum_ddr5_device,
&corsair_dominator_titanium_ddr5_device,
&corsair_vengeance_ddr5_device,
&corsair_vengeance_shugo_series_ddr5_device,
&corsair_vengeance_rs_ddr5_device,
};
const unsigned int CORSAIR_DRAM_NUM_DEVICES = (sizeof(device_list) / sizeof(device_list[ 0 ]));
const corsair_dram_device** corsair_dram_device_list = device_list;
@@ -0,0 +1,68 @@
/*---------------------------------------------------------*\
| CorsairDRAMDevices.h |
| |
| Device list for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 07 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
/*---------------------------------------------------------*\
| Maximum number of PIDs for a given DRAM model |
\*---------------------------------------------------------*/
#define CORSAIR_DRAM_MAX_PIDS 6
/*---------------------------------------------------------*\
| Corsair DRAM vendor ID |
\*---------------------------------------------------------*/
#define CORSAIR_DRAM_VID 0x1B1C
/*---------------------------------------------------------*\
| Corsair DRAM product IDs |
\*---------------------------------------------------------*/
#define CORSAIR_VENGEANCE_PRO_DDR4_PID_1 0x0100
#define CORSAIR_VENGEANCE_PRO_DDR4_PID_2 0x0101
#define CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_1 0x0200
#define CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_2 0x0201
#define CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_1 0x0300
#define CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_2 0x0301
#define CORSAIR_VENGEANCE_RS_DDR4_PID_1 0x0400
#define CORSAIR_VENGEANCE_RS_DDR4_PID_2 0x0401
#define CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_1 0x0600
#define CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_2 0x0601
#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_1 0x0800
#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_2 0x0801
#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_3 0x0810
#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_4 0x0811
#define CORSAIR_VENGEANCE_DDR5_PID_1 0x0700
#define CORSAIR_VENGEANCE_DDR5_PID_2 0x0701
#define CORSAIR_VENGEANCE_DDR5_PID_3 0x0900
#define CORSAIR_VENGEANCE_DDR5_PID_4 0x0901
#define CORSAIR_VENGEANCE_DDR5_PID_5 0x0910
#define CORSAIR_VENGEANCE_DDR5_PID_6 0x0911
#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_1 0x0A00
#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_2 0x0A01
#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_3 0x0A10
#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_4 0x0A11
#define CORSAIR_VENGEANCE_RS_DDR5_PID_1 0x0B00
#define CORSAIR_VENGEANCE_RS_DDR5_PID_2 0x0B01
typedef struct
{
std::string name;
unsigned short pids[CORSAIR_DRAM_MAX_PIDS];
unsigned int led_count;
bool reverse;
} corsair_dram_device;
/*-----------------------------------------------------*\
| These constant values are defined in RazerDevices.cpp |
\*-----------------------------------------------------*/
extern const unsigned int CORSAIR_DRAM_NUM_DEVICES;
extern const corsair_dram_device** corsair_dram_device_list;
@@ -0,0 +1,330 @@
/*---------------------------------------------------------*\
| RGBController_CorsairDRAM.cpp |
| |
| RGBController for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_CorsairDRAM.h"
/**------------------------------------------------------------------*\
@name Corsair DRAM
@category RAM
@type SMBus
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectCorsairDRAMControllers
@comment
The Corsair DRAM RGB controller chip can be found on several
Corsair memory sticks which have different LED counts. This can be controlled
by editing the Part Number in OpenRGB.json with values in the below table.
| Part Number | LED Count |
| :---------: | --------: |
| CMG | 6 |
| CMH | 10 |
| CMN | 10 |
| CMT | 12 |
\*-------------------------------------------------------------------*/
RGBController_CorsairDRAM::RGBController_CorsairDRAM(CorsairDRAMController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Corsair";
type = DEVICE_TYPE_DRAM;
description = "Corsair DRAM RGB Device";
location = controller->GetDeviceLocation();
version = controller->GetDeviceVersion();
if(controller->GetProtocolVersion() >= 4)
{
mode Direct;
Direct.name = "Direct";
Direct.value = CORSAIR_DRAM_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
}
mode Custom;
Custom.name = "Custom";
Custom.value = CORSAIR_DRAM_MODE_STATIC;
Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE;
Custom.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Custom);
mode ColorShift;
ColorShift.name = "Color Shift";
ColorShift.value = CORSAIR_DRAM_MODE_COLOR_SHIFT;
ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorShift.speed_min = CORSAIR_DRAM_SPEED_SLOW;
ColorShift.speed_max = CORSAIR_DRAM_SPEED_FAST;
ColorShift.speed = CORSAIR_DRAM_SPEED_SLOW;
ColorShift.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
ColorShift.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
ColorShift.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
ColorShift.colors_min = 2;
ColorShift.colors_max = 2;
ColorShift.colors.resize(2);
modes.push_back(ColorShift);
mode ColorPulse;
ColorPulse.name = "Color Pulse";
ColorPulse.value = CORSAIR_DRAM_MODE_COLOR_PULSE;
ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorPulse.speed_min = CORSAIR_DRAM_SPEED_SLOW;
ColorPulse.speed_max = CORSAIR_DRAM_SPEED_FAST;
ColorPulse.speed = CORSAIR_DRAM_SPEED_SLOW;
ColorPulse.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
ColorPulse.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
ColorPulse.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
ColorPulse.colors_min = 2;
ColorPulse.colors_max = 2;
ColorPulse.colors.resize(2);
modes.push_back(ColorPulse);
mode RainbowWave;
RainbowWave.name = "Rainbow Wave";
RainbowWave.value = CORSAIR_DRAM_MODE_RAINBOW_WAVE;
RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE;
RainbowWave.color_mode = MODE_COLORS_NONE;
RainbowWave.speed_min = CORSAIR_DRAM_SPEED_SLOW;
RainbowWave.speed_max = CORSAIR_DRAM_SPEED_FAST;
RainbowWave.speed = CORSAIR_DRAM_SPEED_SLOW;
RainbowWave.direction = MODE_DIRECTION_DOWN;
modes.push_back(RainbowWave);
mode ColorWave;
ColorWave.name = "Color Wave";
ColorWave.value = CORSAIR_DRAM_MODE_COLOR_WAVE;
ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorWave.speed_min = CORSAIR_DRAM_SPEED_SLOW;
ColorWave.speed_max = CORSAIR_DRAM_SPEED_FAST;
ColorWave.speed = CORSAIR_DRAM_SPEED_SLOW;
ColorWave.direction = MODE_DIRECTION_DOWN;
ColorWave.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
ColorWave.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
ColorWave.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
ColorWave.colors_min = 2;
ColorWave.colors_max = 2;
ColorWave.colors.resize(2);
modes.push_back(ColorWave);
mode Visor;
Visor.name = "Visor";
Visor.value = CORSAIR_DRAM_MODE_VISOR;
Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Visor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Visor.speed_min = CORSAIR_DRAM_SPEED_SLOW;
Visor.speed_max = CORSAIR_DRAM_SPEED_FAST;
Visor.speed = CORSAIR_DRAM_SPEED_SLOW;
Visor.direction = MODE_DIRECTION_VERTICAL;
Visor.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
Visor.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
Visor.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
Visor.colors_min = 2;
Visor.colors_max = 2;
Visor.colors.resize(2);
modes.push_back(Visor);
mode Rain;
Rain.name = "Rain";
Rain.value = CORSAIR_DRAM_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Rain.color_mode = MODE_COLORS_MODE_SPECIFIC;
Rain.speed_min = CORSAIR_DRAM_SPEED_SLOW;
Rain.speed_max = CORSAIR_DRAM_SPEED_FAST;
Rain.speed = CORSAIR_DRAM_SPEED_SLOW;
Rain.direction = MODE_DIRECTION_DOWN;
Rain.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
Rain.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
Rain.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
Rain.colors_min = 2;
Rain.colors_max = 2;
Rain.colors.resize(2);
modes.push_back(Rain);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CORSAIR_DRAM_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
Marquee.speed_min = CORSAIR_DRAM_SPEED_SLOW;
Marquee.speed_max = CORSAIR_DRAM_SPEED_FAST;
Marquee.speed = CORSAIR_DRAM_SPEED_SLOW;
Marquee.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
Marquee.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
Marquee.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.colors.resize(1);
modes.push_back(Marquee);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CORSAIR_DRAM_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE;
Rainbow.color_mode = MODE_COLORS_NONE;
Rainbow.speed_min = CORSAIR_DRAM_SPEED_SLOW;
Rainbow.speed_max = CORSAIR_DRAM_SPEED_FAST;
Rainbow.speed = CORSAIR_DRAM_SPEED_SLOW;
modes.push_back(Rainbow);
mode Sequential;
Sequential.name = "Sequential";
Sequential.value = CORSAIR_DRAM_MODE_SEQUENTIAL;
Sequential.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE;
Sequential.color_mode = MODE_COLORS_MODE_SPECIFIC;
Sequential.speed_min = CORSAIR_DRAM_SPEED_SLOW;
Sequential.speed_max = CORSAIR_DRAM_SPEED_FAST;
Sequential.speed = CORSAIR_DRAM_SPEED_SLOW;
Sequential.direction = MODE_DIRECTION_DOWN;
Sequential.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN;
Sequential.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX;
Sequential.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT;
Sequential.colors_min = 1;
Sequential.colors_max = 1;
Sequential.colors.resize(1);
modes.push_back(Sequential);
SetupZones();
}
RGBController_CorsairDRAM::~RGBController_CorsairDRAM()
{
delete controller;
}
void RGBController_CorsairDRAM::SetupZones()
{
/*-----------------------------------------------------*\
| Set up zone |
\*-----------------------------------------------------*/
zone new_zone;
new_zone.name = "Corsair DRAM";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = controller->GetLEDCount();
new_zone.leds_max = controller->GetLEDCount();
new_zone.leds_count = controller->GetLEDCount();
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
/*-----------------------------------------------------*\
| Set up LEDs |
\*-----------------------------------------------------*/
for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led new_led;
new_led.name = "Corsair DRAM LED ";
new_led.name.append(std::to_string(led_idx));
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_CorsairDRAM::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*-----------------------------------------------------*\
| This device does not support resizing zones |
\*-----------------------------------------------------*/
}
void RGBController_CorsairDRAM::DeviceUpdateLEDs()
{
controller->SetColorsPerLED(colors.data());
}
void RGBController_CorsairDRAM::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_CorsairDRAM::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_CorsairDRAM::DeviceUpdateMode()
{
unsigned int corsair_direction = 0;
bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
unsigned char mode_colors[6];
switch(modes[active_mode].direction)
{
case MODE_DIRECTION_LEFT:
corsair_direction = CORSAIR_DRAM_DIRECTION_LEFT;
break;
case MODE_DIRECTION_RIGHT:
corsair_direction = CORSAIR_DRAM_DIRECTION_RIGHT;
break;
case MODE_DIRECTION_UP:
corsair_direction = CORSAIR_DRAM_DIRECTION_UP;
break;
case MODE_DIRECTION_DOWN:
corsair_direction = CORSAIR_DRAM_DIRECTION_DOWN;
break;
case MODE_DIRECTION_HORIZONTAL:
corsair_direction = CORSAIR_DRAM_DIRECTION_HORIZONTAL;
break;
case MODE_DIRECTION_VERTICAL:
corsair_direction = CORSAIR_DRAM_DIRECTION_VERTICAL;
break;
}
mode_colors[0] = 0;
mode_colors[1] = 0;
mode_colors[2] = 0;
mode_colors[3] = 0;
mode_colors[4] = 0;
mode_colors[5] = 0;
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
mode_colors[0] = RGBGetRValue(modes[active_mode].colors[0]);
mode_colors[1] = RGBGetGValue(modes[active_mode].colors[0]);
mode_colors[2] = RGBGetBValue(modes[active_mode].colors[0]);
if(modes[active_mode].colors.size() == 2)
{
mode_colors[3] = RGBGetRValue(modes[active_mode].colors[1]);
mode_colors[4] = RGBGetGValue(modes[active_mode].colors[1]);
mode_colors[5] = RGBGetBValue(modes[active_mode].colors[1]);
}
}
if(modes[active_mode].name == "Direct")
{
controller->SetDirect(true);
}
else
{
controller->SetDirect(false);
}
controller->SetEffect(modes[active_mode].value,
modes[active_mode].speed,
corsair_direction,
random,
(unsigned char)modes[active_mode].brightness,
mode_colors[0],
mode_colors[1],
mode_colors[2],
mode_colors[3],
mode_colors[4],
mode_colors[5]);
std::this_thread::sleep_for(std::chrono::milliseconds(15));
}
@@ -1,8 +1,9 @@
/*---------------------------------------------------------*\
| RGBController_CorsairDominatorPlatinum.h |
| RGBController_CorsairDRAM.h |
| |
| RGBController for Corsair Dominator Platinum RAM |
| RGBController for Corsair DRAM RGB controllers |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
@@ -12,13 +13,13 @@
#pragma once
#include "RGBController.h"
#include "CorsairDominatorPlatinumController.h"
#include "CorsairDRAMController.h"
class RGBController_CorsairDominatorPlatinum : public RGBController
class RGBController_CorsairDRAM : public RGBController
{
public:
RGBController_CorsairDominatorPlatinum(CorsairDominatorPlatinumController* controller_ptr);
~RGBController_CorsairDominatorPlatinum();
RGBController_CorsairDRAM(CorsairDRAMController* controller_ptr);
~RGBController_CorsairDRAM();
void SetupZones();
@@ -31,5 +32,5 @@ public:
void DeviceUpdateMode();
private:
CorsairDominatorPlatinumController* controller;
CorsairDRAMController* controller;
};
@@ -1,134 +0,0 @@
/*---------------------------------------------------------*\
| CorsairDominatorPlatinumController.cpp |
| |
| Driver for Corsair Dominator Platinum RAM |
| |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <chrono>
#include "CorsairDominatorPlatinumController.h"
using namespace std::chrono_literals;
CorsairDominatorPlatinumController::CorsairDominatorPlatinumController(i2c_smbus_interface *bus, corsair_dev_id dev, unsigned int leds_count, std::string dev_name)
{
this->bus = bus;
this->dev = dev;
this->leds_count = leds_count;
this->name = dev_name;
led_data[0] = 0xc;
}
CorsairDominatorPlatinumController::~CorsairDominatorPlatinumController()
{
}
unsigned int CorsairDominatorPlatinumController::GetLEDCount()
{
return leds_count;
}
std::string CorsairDominatorPlatinumController::GetDeviceLocation()
{
std::string return_string(bus->device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
std::string CorsairDominatorPlatinumController::GetDeviceName()
{
return(name);
}
void CorsairDominatorPlatinumController::SetAllColors
(
unsigned char red,
unsigned char green,
unsigned char blue
)
{
for(unsigned int led = 0; led < leds_count; led++)
{
SetLEDColor(led, red, green, blue);
}
}
void CorsairDominatorPlatinumController::SetLEDColor
(
unsigned int led,
unsigned char red,
unsigned char green,
unsigned char blue
)
{
if(led >= leds_count)
{
return;
}
unsigned int offset = (led * 3) + 1;
led_data[offset] = red;
led_data[offset + 1] = green;
led_data[offset + 2] = blue;
}
unsigned char CorsairDominatorPlatinumController::crc8
(
unsigned char init,
unsigned char poly,
unsigned char* data,
unsigned char len
)
{
unsigned char crc = init;
for(unsigned int i = 0; i < len; i++)
{
unsigned char val = data[i];
for(unsigned char mask = 0x80; mask != 0; mask >>= 1)
{
unsigned char bit;
if ((val & mask) != 0)
{
bit = (crc & 0x80) ^ 0x80;
}
else
{
bit = crc & 0x80;
}
if (bit == 0)
{
crc <<= 1;
}
else
{
crc = (crc << 1) ^ poly;
}
}
}
return crc;
}
void CorsairDominatorPlatinumController::ApplyColors()
{
unsigned char data[sizeof(led_data)];
memcpy(data, led_data, sizeof(led_data));
unsigned char crc = crc8(0x0, 0x7, data, sizeof(data) - 1);
data[sizeof(data) - 1] = crc;
bus->i2c_smbus_write_block_data(dev, 0x31, 0x20, data);
std::this_thread::sleep_for(800us);
bus->i2c_smbus_write_block_data(dev, 0x32, sizeof(data) - 0x20, data + 0x20);
std::this_thread::sleep_for(200us);
}
@@ -1,44 +0,0 @@
/*---------------------------------------------------------*\
| CorsairDominatorPlatinumController.h |
| |
| Driver for Corsair Dominator Platinum RAM |
| |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
#define CORSAIR_DOMINATOR_PLATINUM_DATA_SIZE 38
typedef unsigned char corsair_dev_id;
class CorsairDominatorPlatinumController
{
public:
CorsairDominatorPlatinumController(i2c_smbus_interface *bus, corsair_dev_id dev, unsigned int leds_count, std::string dev_name);
~CorsairDominatorPlatinumController();
std::string GetDeviceLocation();
std::string GetDeviceName();
unsigned int GetLEDCount();
void SetAllColors(unsigned char red, unsigned char green, unsigned char blue);
void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void ApplyColors();
bool WaitReady();
private:
unsigned char led_data[CORSAIR_DOMINATOR_PLATINUM_DATA_SIZE];
i2c_smbus_interface* bus;
corsair_dev_id dev;
unsigned int leds_count;
std::string name;
static unsigned char crc8(unsigned char init, unsigned char poly, unsigned char *data, unsigned char len);
};
@@ -1,176 +0,0 @@
/*---------------------------------------------------------*\
| CorsairDominatorPlatinumControllerDetect.cpp |
| |
| Detector for Corsair Dominator Platinum RAM |
| |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "Detector.h"
#include "CorsairDominatorPlatinumController.h"
#include "RGBController_CorsairDominatorPlatinum.h"
#include "SettingsManager.h"
#include "LogManager.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
using namespace std::chrono_literals;
json corsair_dominator_models =
{
{
"CMT",
{
{"name", "Corsair Dominator Platinum"},
{"leds", 12}
}
},
{
"CMH",
{
{"name", "Corsair Vengeance Pro SL"},
{"leds", 10}
}
},
{
"CMN",
{
{"name", "Corsair Vengeance RGB RT"},
{"leds", 10}
}
},
{
"CMG",
{
{"name", "Corsair Vengeance RGB RS"},
{"leds", 6}
}
},
{
"CMP",
{
{"name", "Corsair Dominator Titanium"},
{"leds", 11}
}
}
};
#define CORSAIR_DOMINATOR_PLATINUM_NAME "Corsair Dominator Platinum"
bool TestForCorsairDominatorPlatinumController(i2c_smbus_interface *bus, unsigned char address)
{
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
LOG_DEBUG("[%s] Trying address %02X", CORSAIR_DOMINATOR_PLATINUM_NAME, address);
if(res < 0)
{
LOG_DEBUG("[%s] Failed: res was %04X", CORSAIR_DOMINATOR_PLATINUM_NAME, res);
return false;
}
res = bus->i2c_smbus_read_byte_data(address, 0x43);
if(!(res == 0x1A || res == 0x1B))
{
LOG_DEBUG("[%s] Failed: expected 0x1a or 0x1b, got %04X", CORSAIR_DOMINATOR_PLATINUM_NAME, res);
return false;
}
res = bus->i2c_smbus_read_byte_data(address, 0x44);
if(res != 0x04)
{
LOG_DEBUG("[%s] Failed: expected 0x04, got %04X", CORSAIR_DOMINATOR_PLATINUM_NAME, res);
return false;
}
return true;
}
/******************************************************************************************\
* *
* DetectCorsairDominatorPlatinumControllers *
* *
* Detect Corsair Dominator Platinum controllers on the enumerated I2C busses. *
* *
* bus - pointer to i2c_smbus_interface where Aura device is connected *
* dev - I2C address of Aura device *
* *
\******************************************************************************************/
void DetectCorsairDominatorPlatinumControllers(std::vector<i2c_smbus_interface *> &busses)
{
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
json corsair_dominator_settings = settings_manager->GetSettings("CorsairDominatorSettings");
if(!corsair_dominator_settings.contains("model"))
{
// Set default value
corsair_dominator_settings["model"] = "CMT";
settings_manager->SetSettings("CorsairDominatorSettings", corsair_dominator_settings);
settings_manager->SaveSettings();
}
std::string model = corsair_dominator_settings["model"];
for(unsigned int bus = 0; bus < busses.size(); bus++)
{
IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device)
{
LOG_DEBUG("[%s] Testing bus %d", CORSAIR_DOMINATOR_PLATINUM_NAME, bus);
std::vector<unsigned char> addresses;
for(unsigned char addr = 0x58; addr <= 0x5F; addr++)
{
addresses.push_back(addr);
}
for(unsigned char addr = 0x18; addr <= 0x1F; addr++)
{
addresses.push_back(addr);
}
for(unsigned char addr : addresses)
{
if(TestForCorsairDominatorPlatinumController(busses[bus], addr))
{
unsigned int leds;
std::string name;
if(corsair_dominator_models.contains(model))
{
leds = corsair_dominator_models[model]["leds"];
name = corsair_dominator_models[model]["name"];
}
else
{
leds = corsair_dominator_models["CMT"]["leds"];
name = corsair_dominator_models["CMT"]["name"];
}
LOG_DEBUG("[%s] Model: %s, Leds: %d", CORSAIR_DOMINATOR_PLATINUM_NAME, name.c_str(), leds);
CorsairDominatorPlatinumController* controller = new CorsairDominatorPlatinumController(busses[bus], addr, leds, name);
RGBController_CorsairDominatorPlatinum* rgbcontroller = new RGBController_CorsairDominatorPlatinum(controller);
ResourceManager::get()->RegisterRGBController(rgbcontroller);
}
std::this_thread::sleep_for(10ms);
}
}
else
{
LOG_DEBUG("[%s] Bus %d is not a DRAM bus", CORSAIR_DOMINATOR_PLATINUM_NAME, bus);
}
}
} /* DetectCorsairDominatorPlatinumControllers() */
REGISTER_I2C_DETECTOR(CORSAIR_DOMINATOR_PLATINUM_NAME, DetectCorsairDominatorPlatinumControllers);
@@ -1,134 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_CorsairDominatorPlatinum.cpp |
| |
| RGBController for Corsair Dominator Platinum RAM |
| |
| Erik Gilling (konkers) 25 Sep 2020 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_CorsairDominatorPlatinum.h"
/**------------------------------------------------------------------*\
@name Corsair Dominator Platinum
@category RAM
@type SMBus
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectCorsairDominatorPlatinumControllers
@comment
The Corsair Dominator controller chip can be found on several
Corsair memory sticks which have different LED counts. This can be controlled
by editing the Part Number in OpenRGB.json with values in the below table.
| Part Number | LED Count |
| :---------: | --------: |
| CMG | 6 |
| CMH | 10 |
| CMN | 10 |
| CMT | 12 |
\*-------------------------------------------------------------------*/
RGBController_CorsairDominatorPlatinum::RGBController_CorsairDominatorPlatinum(CorsairDominatorPlatinumController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Corsair";
type = DEVICE_TYPE_DRAM;
description = "Corsair RAM RGB Device";
location = controller->GetDeviceLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.speed_min = 0;
Direct.speed_max = 0;
Direct.speed = 0;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
active_mode = 0;
}
RGBController_CorsairDominatorPlatinum::~RGBController_CorsairDominatorPlatinum()
{
delete controller;
}
void RGBController_CorsairDominatorPlatinum::SetupZones()
{
/*---------------------------------------------------------*\
| Set up zone |
\*---------------------------------------------------------*/
zone new_zone;
new_zone.name = "Corsair RAM Zone";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = controller->GetLEDCount();
new_zone.leds_max = controller->GetLEDCount();
new_zone.leds_count = controller->GetLEDCount();
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
/*---------------------------------------------------------*\
| Set up LEDs |
\*---------------------------------------------------------*/
for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led new_led;
new_led.name = "Corsair RAM LED ";
new_led.name.append(std::to_string(led_idx));
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_CorsairDominatorPlatinum::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_CorsairDominatorPlatinum::DeviceUpdateLEDs()
{
for(unsigned int led = 0; led < (unsigned int)colors.size(); led++)
{
RGBColor color = colors[led];
unsigned char red = RGBGetRValue(color);
unsigned char grn = RGBGetGValue(color);
unsigned char blu = RGBGetBValue(color);
controller->SetLEDColor(led, red, grn, blu);
}
controller->ApplyColors();
}
void RGBController_CorsairDominatorPlatinum::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_CorsairDominatorPlatinum::UpdateSingleLED(int led)
{
RGBColor color = colors[led];
unsigned char red = RGBGetRValue(color);
unsigned char grn = RGBGetGValue(color);
unsigned char blu = RGBGetBValue(color);
controller->SetLEDColor(led, red, grn, blu);
controller->ApplyColors();
}
void RGBController_CorsairDominatorPlatinum::DeviceUpdateMode()
{
}
@@ -1,213 +0,0 @@
/*---------------------------------------------------------*\
| CorsairVengeanceProController.cpp |
| |
| Driver for Corsair Vengeance Pro RGB RAM |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <chrono>
#include "CorsairVengeanceProController.h"
using namespace std::chrono_literals;
CorsairVengeanceProController::CorsairVengeanceProController(i2c_smbus_interface* bus, corsair_dev_id dev)
{
this->bus = bus;
this->dev = dev;
strcpy(device_name, "Corsair Vengeance Pro RGB");
led_count = CORSAIR_PRO_LED_COUNT;
direct_mode = false;
effect_mode = CORSAIR_PRO_MODE_STATIC;
for (unsigned int i = 0; i < led_count; i++)
{
led_red[i] = 0;
led_green[i] = 0;
led_blue[i] = 0;
}
}
CorsairVengeanceProController::~CorsairVengeanceProController()
{
}
std::string CorsairVengeanceProController::GetDeviceName()
{
return(device_name);
}
std::string CorsairVengeanceProController::GetDeviceLocation()
{
std::string return_string(bus->device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
unsigned int CorsairVengeanceProController::GetLEDCount()
{
return(led_count);
}
unsigned char CorsairVengeanceProController::GetEffect()
{
return(effect_mode);
}
void CorsairVengeanceProController::SetAllColors(unsigned char red, unsigned char green, unsigned char blue)
{
for (unsigned int i = 0; i < led_count; i++)
{
led_red[i] = red;
led_green[i] = green;
led_blue[i] = blue;
}
}
void CorsairVengeanceProController::SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue)
{
led_red[led] = red;
led_green[led] = green;
led_blue[led] = blue;
}
void CorsairVengeanceProController::ApplyColors()
{
if(direct_mode)
{
unsigned char full_packet[32];
unsigned char crc_packet[31];
full_packet[0] = 0x0A;
crc_packet[0] = 0x0A;
for (int i = 0; i < 10; i++)
{
crc_packet[(i * 3) + 1] = led_red[i];
full_packet[(i * 3) + 1] = led_red[i];
crc_packet[(i * 3) + 2] = led_green[i];
full_packet[(i * 3) + 2] = led_green[i];
crc_packet[(i * 3) + 3] = led_blue[i];
full_packet[(i * 3) + 3] = led_blue[i];
}
uint8_t footer = CRCPP::CRC::Calculate(crc_packet, 31, CRCPP::CRC::CRC_8());
full_packet[31] = footer;
bus->i2c_smbus_write_block_data(dev, CORSAIR_PRO_DIRECT_COMMAND, 32, full_packet);
}
else
{
bus->i2c_smbus_write_byte_data(dev, 0x26, 0x02);
std::this_thread::sleep_for(1ms);
bus->i2c_smbus_write_byte_data(dev, 0x21, 0x00);
std::this_thread::sleep_for(1ms);
for (int i = 0; i < 10; i++)
{
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, led_red[i]);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, led_green[i]);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, led_blue[i]);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0xFF);
}
bus->i2c_smbus_write_byte_data(dev, 0x82, 0x02);
}
}
void CorsairVengeanceProController::SetEffect(unsigned char mode,
unsigned char speed,
unsigned char direction,
bool random,
unsigned char red1,
unsigned char grn1,
unsigned char blu1,
unsigned char red2,
unsigned char grn2,
unsigned char blu2
)
{
if(mode == effect_mode)
{
return;
}
effect_mode = mode;
direct_mode = (effect_mode == CORSAIR_PRO_MODE_DIRECT);
if(direct_mode)
{
return;
}
bus->i2c_smbus_write_byte_data(dev, 0x26, 0x01);
std::this_thread::sleep_for(1ms);
bus->i2c_smbus_write_byte_data(dev, 0x21, 0x00);
std::this_thread::sleep_for(1ms);
unsigned char random_byte;
if(random)
{
random_byte = CORSAIR_PRO_EFFECT_RANDOM_COLORS;
}
else
{
random_byte = CORSAIR_PRO_EFFECT_CUSTOM_COLORS;
}
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, effect_mode); // Mode
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, speed); // Speed
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, random_byte); // Custom color
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, direction); // Direction
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, red1); // Custom color 1 red
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, grn1); // Custom color 1 green
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, blu1); // Custom color 1 blue
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0xFF);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, red2); // Custom color 2 red
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, grn2); // Custom color 2 green
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, blu2); // Custom color 2 blue
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0xFF);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, CORSAIR_PRO_REG_COMMAND, 0x00);
bus->i2c_smbus_write_byte_data(dev, 0x82, 0x01);
WaitReady();
}
bool CorsairVengeanceProController::WaitReady()
{
int i = 0;
while (bus->i2c_smbus_read_byte_data(dev, 0x41) != 0x00)
{
i++;
std::this_thread::sleep_for(1ms);
}
return false;
}
void CorsairVengeanceProController::SetDirect(bool direct)
{
direct_mode = direct;
}
@@ -1,109 +0,0 @@
/*---------------------------------------------------------*\
| CorsairVengeanceProController.h |
| |
| Driver for Corsair Vengeance Pro RGB RAM |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
#include "CRC.h"
typedef unsigned char corsair_dev_id;
#define CORSAIR_PRO_LED_COUNT ( 10 )
#define CORSAIR_VENGEANCE_RGB_PRO_NAME "Corsair Vengeance RGB PRO"
enum
{
CORSAIR_PRO_REG_COMMAND = 0x20, /* Command write register */
CORSAIR_PRO_DIRECT_COMMAND = 0x31, /* Where it writes direct mode colors */
};
enum
{
CORSAIR_PRO_MODE_DIRECT = 0xDD, /* Arbitrary value to compare against later. Not the actual packet */
CORSAIR_PRO_MODE_COLOR_SHIFT = 0x00, /* Color Shift mode */
CORSAIR_PRO_MODE_COLOR_PULSE = 0x01, /* Color Pulse mode */
CORSAIR_PRO_MODE_RAINBOW_WAVE = 0x03, /* Rainbow Wave mode */
CORSAIR_PRO_MODE_COLOR_WAVE = 0x04, /* Color Wave mode */
CORSAIR_PRO_MODE_VISOR = 0x05, /* Visor mode */
CORSAIR_PRO_MODE_RAIN = 0x06, /* Rain mode */
CORSAIR_PRO_MODE_MARQUEE = 0x07, /* Marquee mode */
CORSAIR_PRO_MODE_RAINBOW = 0x08, /* Rainbow mode */
CORSAIR_PRO_MODE_SEQUENTIAL = 0x09, /* Sequential mode */
CORSAIR_PRO_MODE_STATIC = 0x10, /* Static mode */
CORSAIR_PRO_NUMBER_MODES = 10, /* Number of Corsair Pro modes */
};
enum
{
CORSAIR_PRO_SPEED_SLOW = 0x00, /* Slow speed */
CORSAIR_PRO_SPEED_MEDIUM = 0x01, /* Medium speed */
CORSAIR_PRO_SPEED_FAST = 0x02, /* Fast speed */
};
enum
{
CORSAIR_PRO_EFFECT_RANDOM_COLORS = 0x00, /* Random colors */
CORSAIR_PRO_EFFECT_CUSTOM_COLORS = 0x01, /* Custom colors */
};
enum
{
CORSAIR_PRO_DIRECTION_UP = 0x00, /* Up direction */
CORSAIR_PRO_DIRECTION_DOWN = 0x01, /* Down direction */
CORSAIR_PRO_DIRECTION_LEFT = 0x02, /* Left direction */
CORSAIR_PRO_DIRECTION_RIGHT = 0x03, /* Right direction */
CORSAIR_PRO_DIRECTION_VERTICAL = 0x01, /* Vertical direction */
CORSAIR_PRO_DIRECTION_HORIZONTAL = 0x03, /* Horizontal direction */
};
class CorsairVengeanceProController
{
public:
CorsairVengeanceProController(i2c_smbus_interface* bus, corsair_dev_id dev);
~CorsairVengeanceProController();
std::string GetDeviceName();
std::string GetDeviceLocation();
unsigned int GetLEDCount();
unsigned char GetEffect();
void SetEffect(unsigned char mode,
unsigned char speed,
unsigned char direction,
bool random,
unsigned char red1,
unsigned char grn1,
unsigned char blu1,
unsigned char red2,
unsigned char grn2,
unsigned char blu2
);
void SetAllColors(unsigned char red, unsigned char green, unsigned char blue);
void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue);
void ApplyColors();
bool WaitReady();
void SetDirect(bool direct);
private:
char device_name[32];
unsigned int led_count;
bool direct_mode;
unsigned char effect_mode;
unsigned char led_red[CORSAIR_PRO_LED_COUNT];
unsigned char led_green[CORSAIR_PRO_LED_COUNT];
unsigned char led_blue[CORSAIR_PRO_LED_COUNT];
i2c_smbus_interface* bus;
corsair_dev_id dev;
};
@@ -1,107 +0,0 @@
/*---------------------------------------------------------*\
| CorsairVengeanceProControllerDetect.cpp |
| |
| Detector for Corsair Vengeance Pro RGB RAM |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <vector>
#include "Detector.h"
#include "CorsairVengeanceProController.h"
#include "RGBController_CorsairVengeancePro.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
#include "LogManager.h"
using namespace std::chrono_literals;
/******************************************************************************************\
* *
* TestForCorsairVengeanceProController *
* *
* Tests the given address to see if a Corsair Pro controller exists there. *
* *
\******************************************************************************************/
bool TestForCorsairVengeanceProController(i2c_smbus_interface* bus, unsigned char address)
{
bool pass = false;
int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE);
LOG_DEBUG("[%s] Trying address %02X", CORSAIR_VENGEANCE_RGB_PRO_NAME, address);
if(res >= 0)
{
pass = true;
res = bus->i2c_smbus_read_byte_data(address, 0x43);
if (res != 0x1C)
{
pass = false;
LOG_DEBUG("[%s] Failed: was expecting 0x1C got %02X", CORSAIR_VENGEANCE_RGB_PRO_NAME, res);
}
res = bus->i2c_smbus_read_byte_data(address, 0x44);
if(!((res == 0x03) || (res == 0x04)))
{
pass = false;
LOG_DEBUG("[%s] Failed: was expecting 0x03 or 0x04 got %02X", CORSAIR_VENGEANCE_RGB_PRO_NAME, res);
}
}
else
{
LOG_DEBUG("[%s] Failed: res was %04X", CORSAIR_VENGEANCE_RGB_PRO_NAME, res);
}
std::this_thread::sleep_for(10ms);
return(pass);
} /* TestForCorsairVengeanceProController() */
/******************************************************************************************\
* *
* DetectCorsairVengeanceProControllers *
* *
* Detect Corsair Vengeance Pro controllers on the enumerated I2C busses. *
* *
* bus - pointer to i2c_smbus_interface where Aura device is connected *
* dev - I2C address of Aura device *
* *
\******************************************************************************************/
void DetectCorsairVengeanceProControllers(std::vector<i2c_smbus_interface*> &busses)
{
for(unsigned int bus = 0; bus < busses.size(); bus++)
{
LOG_DEBUG("[%s] Testing bus %d", CORSAIR_VENGEANCE_RGB_PRO_NAME, bus);
IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device)
{
for(unsigned char addr = 0x58; addr <= 0x5F; addr++)
{
if(TestForCorsairVengeanceProController(busses[bus], addr))
{
CorsairVengeanceProController* new_controller = new CorsairVengeanceProController(busses[bus], addr);
RGBController_CorsairVengeancePro* new_rgbcontroller = new RGBController_CorsairVengeancePro(new_controller);
ResourceManager::get()->RegisterRGBController(new_rgbcontroller);
}
}
}
else
{
LOG_DEBUG("[%s] Bus %d is not a DRAM bus", CORSAIR_VENGEANCE_RGB_PRO_NAME, bus);
}
}
} /* DetectCorsairVengeanceProControllers() */
REGISTER_I2C_DETECTOR("Corsair Vengeance Pro", DetectCorsairVengeanceProControllers);
@@ -1,316 +0,0 @@
/*---------------------------------------------------------*\
| RGBController_CorsairVengeancePro.cpp |
| |
| RGBController for Corsair Vengeance Pro RGB RAM |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_CorsairVengeancePro.h"
/**------------------------------------------------------------------*\
@name Corsair Vengeance Pro
@category RAM
@type SMBus
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectCorsairVengeanceProControllers
@comment
\*-------------------------------------------------------------------*/
RGBController_CorsairVengeancePro::RGBController_CorsairVengeancePro(CorsairVengeanceProController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Corsair";
type = DEVICE_TYPE_DRAM;
description = "Corsair Vengeance Pro RGB Device";
location = controller->GetDeviceLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = CORSAIR_PRO_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.speed_min = 0;
Direct.speed_max = 0;
Direct.speed = 0;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = CORSAIR_PRO_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Static.speed_min = 0;
Static.speed_max = 0;
Static.speed = 0;
Static.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Static);
mode ColorShift;
ColorShift.name = "Color Shift";
ColorShift.value = CORSAIR_PRO_MODE_COLOR_SHIFT;
ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
ColorShift.speed_min = CORSAIR_PRO_SPEED_SLOW;
ColorShift.speed_max = CORSAIR_PRO_SPEED_FAST;
ColorShift.colors_min = 2;
ColorShift.colors_max = 2;
ColorShift.speed = CORSAIR_PRO_SPEED_SLOW;
ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorShift.colors.resize(2);
modes.push_back(ColorShift);
mode ColorPulse;
ColorPulse.name = "Color Pulse";
ColorPulse.value = CORSAIR_PRO_MODE_COLOR_PULSE;
ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
ColorPulse.speed_min = CORSAIR_PRO_SPEED_SLOW;
ColorPulse.speed_max = CORSAIR_PRO_SPEED_FAST;
ColorPulse.colors_min = 2;
ColorPulse.colors_max = 2;
ColorPulse.speed = CORSAIR_PRO_SPEED_SLOW;
ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorPulse.colors.resize(2);
modes.push_back(ColorPulse);
mode RainbowWave;
RainbowWave.name = "Rainbow Wave";
RainbowWave.value = CORSAIR_PRO_MODE_RAINBOW_WAVE;
RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD;
RainbowWave.speed_min = CORSAIR_PRO_SPEED_SLOW;
RainbowWave.speed_max = CORSAIR_PRO_SPEED_FAST;
RainbowWave.speed = CORSAIR_PRO_SPEED_SLOW;
RainbowWave.direction = MODE_DIRECTION_DOWN;
RainbowWave.color_mode = MODE_COLORS_NONE;
modes.push_back(RainbowWave);
mode ColorWave;
ColorWave.name = "Color Wave";
ColorWave.value = CORSAIR_PRO_MODE_COLOR_WAVE;
ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
ColorWave.speed_min = CORSAIR_PRO_SPEED_SLOW;
ColorWave.speed_max = CORSAIR_PRO_SPEED_FAST;
ColorWave.colors_min = 2;
ColorWave.colors_max = 2;
ColorWave.speed = CORSAIR_PRO_SPEED_SLOW;
ColorWave.direction = MODE_DIRECTION_DOWN;
ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC;
ColorWave.colors.resize(2);
modes.push_back(ColorWave);
mode Visor;
Visor.name = "Visor";
Visor.value = CORSAIR_PRO_MODE_VISOR;
Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Visor.speed_min = CORSAIR_PRO_SPEED_SLOW;
Visor.speed_max = CORSAIR_PRO_SPEED_FAST;
Visor.colors_min = 2;
Visor.colors_max = 2;
Visor.speed = CORSAIR_PRO_SPEED_SLOW;
Visor.direction = MODE_DIRECTION_VERTICAL;
Visor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Visor.colors.resize(2);
modes.push_back(Visor);
mode Rain;
Rain.name = "Rain";
Rain.value = CORSAIR_PRO_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Rain.speed_min = CORSAIR_PRO_SPEED_SLOW;
Rain.speed_max = CORSAIR_PRO_SPEED_FAST;
Rain.colors_min = 2;
Rain.colors_max = 2;
Rain.speed = CORSAIR_PRO_SPEED_SLOW;
Rain.direction = MODE_DIRECTION_DOWN;
Rain.color_mode = MODE_COLORS_MODE_SPECIFIC;
Rain.colors.resize(2);
modes.push_back(Rain);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = CORSAIR_PRO_MODE_MARQUEE;
Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Marquee.speed_min = CORSAIR_PRO_SPEED_SLOW;
Marquee.speed_max = CORSAIR_PRO_SPEED_FAST;
Marquee.colors_min = 1;
Marquee.colors_max = 1;
Marquee.speed = CORSAIR_PRO_SPEED_SLOW;
Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC;
Marquee.colors.resize(1);
modes.push_back(Marquee);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = CORSAIR_PRO_MODE_RAINBOW;
Rainbow.flags = MODE_FLAG_HAS_SPEED;
Rainbow.speed_min = CORSAIR_PRO_SPEED_SLOW;
Rainbow.speed_max = CORSAIR_PRO_SPEED_FAST;
Rainbow.speed = CORSAIR_PRO_SPEED_SLOW;
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Sequential;
Sequential.name = "Sequential";
Sequential.value = CORSAIR_PRO_MODE_SEQUENTIAL;
Sequential.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR;
Sequential.speed_min = CORSAIR_PRO_SPEED_SLOW;
Sequential.speed_max = CORSAIR_PRO_SPEED_FAST;
Sequential.colors_min = 1;
Sequential.colors_max = 1;
Sequential.speed = CORSAIR_PRO_SPEED_SLOW;
Sequential.direction = MODE_DIRECTION_DOWN;
Sequential.color_mode = MODE_COLORS_MODE_SPECIFIC;
Sequential.colors.resize(1);
modes.push_back(Sequential);
SetupZones();
active_mode = 9;
}
RGBController_CorsairVengeancePro::~RGBController_CorsairVengeancePro()
{
delete controller;
}
void RGBController_CorsairVengeancePro::SetupZones()
{
/*---------------------------------------------------------*\
| Set up zone |
\*---------------------------------------------------------*/
zone new_zone;
new_zone.name = "Corsair Pro Zone";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = controller->GetLEDCount();
new_zone.leds_max = controller->GetLEDCount();
new_zone.leds_count = controller->GetLEDCount();
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
/*---------------------------------------------------------*\
| Set up LEDs |
\*---------------------------------------------------------*/
for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led* new_led = new led();
new_led->name = "Corsair Pro LED ";
new_led->name.append(std::to_string(led_idx));
leds.push_back(*new_led);
}
SetupColors();
}
void RGBController_CorsairVengeancePro::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_CorsairVengeancePro::DeviceUpdateLEDs()
{
for(unsigned int led = 0; led < (unsigned int)colors.size(); led++)
{
RGBColor color = colors[led];
unsigned char red = RGBGetRValue(color);
unsigned char grn = RGBGetGValue(color);
unsigned char blu = RGBGetBValue(color);
controller->SetLEDColor(led, red, grn, blu);
}
controller->ApplyColors();
}
void RGBController_CorsairVengeancePro::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_CorsairVengeancePro::UpdateSingleLED(int led)
{
RGBColor color = colors[led];
unsigned char red = RGBGetRValue(color);
unsigned char grn = RGBGetGValue(color);
unsigned char blu = RGBGetBValue(color);
controller->SetLEDColor(led, red, grn, blu);
controller->ApplyColors();
}
void RGBController_CorsairVengeancePro::DeviceUpdateMode()
{
unsigned int corsair_direction = 0;
bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM);
unsigned char mode_colors[6];
switch(modes[active_mode].direction)
{
case MODE_DIRECTION_LEFT:
corsair_direction = CORSAIR_PRO_DIRECTION_LEFT;
break;
case MODE_DIRECTION_RIGHT:
corsair_direction = CORSAIR_PRO_DIRECTION_RIGHT;
break;
case MODE_DIRECTION_UP:
corsair_direction = CORSAIR_PRO_DIRECTION_UP;
break;
case MODE_DIRECTION_DOWN:
corsair_direction = CORSAIR_PRO_DIRECTION_DOWN;
break;
case MODE_DIRECTION_HORIZONTAL:
corsair_direction = CORSAIR_PRO_DIRECTION_HORIZONTAL;
break;
case MODE_DIRECTION_VERTICAL:
corsair_direction = CORSAIR_PRO_DIRECTION_VERTICAL;
break;
}
mode_colors[0] = 0;
mode_colors[1] = 0;
mode_colors[2] = 0;
mode_colors[3] = 0;
mode_colors[4] = 0;
mode_colors[5] = 0;
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
mode_colors[0] = RGBGetRValue(modes[active_mode].colors[0]);
mode_colors[1] = RGBGetGValue(modes[active_mode].colors[0]);
mode_colors[2] = RGBGetBValue(modes[active_mode].colors[0]);
if(modes[active_mode].colors.size() == 2)
{
mode_colors[3] = RGBGetRValue(modes[active_mode].colors[1]);
mode_colors[4] = RGBGetGValue(modes[active_mode].colors[1]);
mode_colors[5] = RGBGetBValue(modes[active_mode].colors[1]);
}
}
if (modes[active_mode].name == "Direct")
{
controller->SetDirect(true);
}
else
{
controller->SetDirect(false);
}
controller->SetEffect(modes[active_mode].value,
modes[active_mode].speed,
corsair_direction,
random,
mode_colors[0],
mode_colors[1],
mode_colors[2],
mode_colors[3],
mode_colors[4],
mode_colors[5]);
std::this_thread::sleep_for(std::chrono::milliseconds(15));
}
@@ -266,6 +266,21 @@ ENESMBusController::ENESMBusController(ENESMBusInterface* interface, ene_dev_id
led_count = config_table[ENE_CONFIG_LED_COUNT_0107];
}
/*-----------------------------------------------------*\
| AUMA0-E6K5-1114 |
| Found on ASUS ROG MATRIX PLATINUM 5090 |
\*-----------------------------------------------------*/
else if(strcmp(device_version, "AUMA0-E6K5-1114") == 0)
{
direct_reg = ENE_REG_COLORS_DIRECT_V2;
effect_reg = ENE_REG_COLORS_EFFECT_V2;
channel_cfg = ENE_CONFIG_CHANNEL_V2;
/*-------------------------------------------------*\
| Read LED count from configuration table |
\*-------------------------------------------------*/
led_count = config_table[ENE_CONFIG_LED_COUNT_0107];
}
/*-----------------------------------------------------*\
| ROG ARION - ASUS ROG Arion external SSD enclosure |
| This device does not support ENE read, so we fake the |
| device name string if the interface is ROG Arion type.|
@@ -30,10 +30,11 @@ using namespace std::chrono_literals;
#undef interface
#endif
/*----------------------------------------------------------------------*\
| This list contains the available SMBus addresses for mapping ENE RAM |
\*----------------------------------------------------------------------*/
#define ENE_RAM_ADDRESS_COUNT 23
/*---------------------------------------------------------*\
| This list contains the available SMBus addresses for |
| mapping ENE RAM |
\*---------------------------------------------------------*/
#define ENE_RAM_ADDRESS_COUNT (sizeof(ene_ram_addresses) / sizeof(ene_ram_addresses[0]))
static const unsigned char ene_ram_addresses[] =
{
@@ -44,14 +45,7 @@ static const unsigned char ene_ram_addresses[] =
0x74,
0x75,
0x76,
0x78,
0x79,
0x7A,
0x7B,
0x7C,
0x7D,
0x7E,
0x7F,
0x77,
0x4F,
0x66,
0x67,
@@ -62,10 +56,11 @@ static const unsigned char ene_ram_addresses[] =
0x3D
};
/*---------------------------------------------------------------------------------*\
| This list contains the available SMBus addresses for mapping Aura motherboards |
\*---------------------------------------------------------------------------------*/
#define AURA_MOBO_ADDRESS_COUNT 3
/*---------------------------------------------------------*\
| This list contains the available SMBus addresses for |
| mapping Aura motherboards |
\*---------------------------------------------------------*/
#define AURA_MOBO_ADDRESS_COUNT (sizeof(aura_mobo_addresses) / sizeof(aura_mobo_addresses[0]))
static const unsigned char aura_mobo_addresses[] =
{
@@ -220,7 +215,7 @@ void DetectENESMBusDRAMControllers(std::vector<i2c_smbus_interface*> &busses)
{
address_list_idx++;
if(address_list_idx < ENE_RAM_ADDRESS_COUNT)
if(address_list_idx < (int)ENE_RAM_ADDRESS_COUNT)
{
LOG_DEBUG("[ENE SMBus DRAM] Testing address %02X to see if there is a device there", ene_ram_addresses[address_list_idx]);
@@ -232,7 +227,7 @@ void DetectENESMBusDRAMControllers(std::vector<i2c_smbus_interface*> &busses)
}
} while (res >= 0);
if(address_list_idx < ENE_RAM_ADDRESS_COUNT)
if(address_list_idx < (int)ENE_RAM_ADDRESS_COUNT)
{
LOG_DEBUG("[ENE SMBus DRAM] Remapping slot %d to address %02X", slot, ene_ram_addresses[address_list_idx]);
@@ -355,6 +350,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti Gaming OC",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_OC, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G_OC_V2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_2_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_OC, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_WHITE, 0x67);
@@ -462,8 +458,10 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060_O8G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_5070TI_O16G_GAMING_OC, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5080_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5090_O32G_GAMING, 0x67);
@@ -471,6 +469,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming",
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE, 0x67);
@@ -502,6 +501,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7700 XT Gaming OC",
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING_0606, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming White OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_WHITE_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 GRE Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900GRE_O16G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XT_020G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XTX Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XTX_O24G_GAMING, 0x67);
REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 9070 Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_9070_016G_GAMING, 0x67);
@@ -44,7 +44,7 @@ FnaticStreakController::~FnaticStreakController()
std::string FnaticStreakController::GetDeviceLocation()
{
return("HID " + location);
return("HID: " + location);
}
std::string FnaticStreakController::GetNameString()
@@ -230,16 +230,41 @@ void FnaticStreakController::SetLEDsDirect(std::vector<led> leds, std::vector<RG
void FnaticStreakController::SendRGBToDevice()
{
unsigned int total_leds = GetLEDCount();
unsigned int data_size = total_leds * 3;
if(keyboard_type == FNATIC_STREAK_TYPE_65)
{
unsigned char data[64];
memset(data, 0x00, sizeof(data));
/*-----------------------------------------------------*\
| For direct/software control, use command 0x0f |
| This bypasses the profile and allows immediate update |
| The 0x03 subcommand indicates per-key color data |
\*-----------------------------------------------------*/
unsigned char prefix[] = { 0x0f, 0x03 };
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
data[0] = 0x0F;
data[1] = 0x15;
data[7] = 0x0F;
data[8] = 0x03;
for(unsigned int i = 9; i < 25; i++)
{
data[i] = 0xFF;
}
data[25] = color_buf[0];
data[26] = color_buf[1];
data[27] = color_buf[2];
hid_write(dev, data, sizeof(data));
}
else
{
unsigned int total_leds = GetLEDCount();
unsigned int data_size = total_leds * 3;
/*-----------------------------------------------------*\
| For direct/software control, use command 0x0f |
| This bypasses the profile and allows immediate update |
| The 0x03 subcommand indicates per-key color data |
\*-----------------------------------------------------*/
unsigned char prefix[] = { 0x0f, 0x03 };
SendRequest(prefix, sizeof(prefix), color_buf, data_size);
}
}
void FnaticStreakController::SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed)
@@ -51,7 +51,8 @@
enum FnaticStreakType
{
FNATIC_STREAK_TYPE_FULL,
FNATIC_STREAK_TYPE_MINI
FNATIC_STREAK_TYPE_MINI,
FNATIC_STREAK_TYPE_65
};
class FnaticStreakController
@@ -26,6 +26,7 @@
#define FNATIC_STREAK_PID 0x0101
#define FNATIC_MINISTREAK_PID 0x0102
#define FNATIC_STREAK65_PID 0x0105
void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
{
@@ -39,6 +40,10 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
{
kb_type = FNATIC_STREAK_TYPE_MINI;
}
else if(info->product_id == FNATIC_STREAK65_PID)
{
kb_type = FNATIC_STREAK_TYPE_65;
}
else
{
kb_type = FNATIC_STREAK_TYPE_FULL;
@@ -53,3 +58,4 @@ void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name)
REGISTER_HID_DETECTOR_I("Fnatic Streak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK_PID, 1);
REGISTER_HID_DETECTOR_I("Fnatic miniStreak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_MINISTREAK_PID, 1);
REGISTER_HID_DETECTOR_I("Fnatic Streak65", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK65_PID, 1);
@@ -37,19 +37,22 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
\*-----------------------------------------------------------------*/
case 0x32:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if(res == 0x27 || res == 0x26) {
if(res == 0x27 || res == 0x26)
{
res = bus->i2c_smbus_read_byte_data(address, 0x01);
if (res == 0x10 || res == 0x20)
if(res == 0x10 || res == 0x20)
{
pass = true;
}
}
break;
/*-----------------------------------------------------------------*\
| V1 Controller - RTX 3080 |
| V1 Controller |
\*-----------------------------------------------------------------*/
case 0x23:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if(res == 0x30)
if(res == 0x27 || res == 0x30)
{
pass = true;
}
@@ -60,8 +63,10 @@ bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address)
\*-----------------------------------------------------------------*/
case 0x51:
res = bus->i2c_smbus_read_byte_data(address, 0x00);
if (res == 0x80)
if(res == 0x80)
{
pass = true;
}
break;
}
@@ -199,6 +199,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Gaming OC",
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_ICE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV1, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV2, 0x75);
@@ -213,6 +214,7 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9060 XT GAMING",
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9060 XT GAMING OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI44_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9060XT_GAMING_OC_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC ICE", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_ICE_16G_SUB_DEV, 0x75);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_16G_SUB_DEV, 0x75);
@@ -194,8 +194,10 @@ REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7800 XT GAMING OC",
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 GRE GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900GRE_GAMING_OC_16G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XT_GAMING_OC_20G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XTX GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XTX_GAMING_OC_24G_SUB_DEV, 0x62);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 7900 XTX ELITE 24G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX7900XTX_ELITE_24G_SUB_DEV, 0x71);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6750 XT ELITE 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX_6750_XT_ELITE_12G_SUB_DEV, 0x70);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6900 XT EXTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_XTREME_WATERFORCE_WB_SUB_DEV, 0x70);
REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x73);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_16G_SUB_DEV, 0x73);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC ICE", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_ICE_16G_SUB_DEV, 0x73);
REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070_GAMING_OC_16G_SUB_DEV, 0x73);
@@ -2065,6 +2065,51 @@ static const gb_fusion2_device x570_aorus_ultra_device =
"X570 AORUS ULTRA",
};
/*-------------------------------------------------------------*\
| X570_AORUS_XTREME 048D:8297 |
| |
| Zone "Digital LED 1" : Linear |
| Zone "Digital LED 2" : Linear |
| Zone "Back I/O" : Single |
| Zone "PCIe" : Single |
| Zone "CPU Header" : Single |
\*-------------------------------------------------------------*/
static const gb_fusion2_zone xtreme_back_io_zone =
{
LED3,
1,
1,
"Back I/O",
};
static const gb_fusion2_zone xtreme_cpu_hdr_zone =
{
LED7,
1,
1,
"CPU Header",
};
static gb_fusion2_layout x570_aorus_xtreme_layout =
{
&common_d_led1_zone,
&common_d_led2_zone,
&xtreme_back_io_zone,
&common_pcie_led4_zone,
&xtreme_cpu_hdr_zone,
nullptr,
nullptr,
nullptr
};
static const gb_fusion2_device x570_aorus_xtreme_device =
{
&x570_aorus_xtreme_layout,
0xCDE4CFFE,
1,
"X570 AORUS XTREME",
};
/*-------------------------------------------------------------------------*\
| Generic Layout (Used when no match found) |
\*-------------------------------------------------------------------------*/
@@ -3831,6 +3876,7 @@ const gb_fusion2_device* gb_fusion2_device_list_data[] =
&x570_aorus_pro_device,
&x570_aorus_pro_wifi_device,
&x570_aorus_ultra_device,
&x570_aorus_xtreme_device,
&x570i_aorus_pro_wifi_device,
&z390_aorus_master_cf_device,
@@ -105,6 +105,7 @@ void DetectGigabyteRGBFusionGPUControllers(i2c_smbus_interface* bus, uint8_t i2c
}
} /* DetectGigabyteRGBFusionGPUControllers() */
REGISTER_I2C_PCI_DETECTOR("Gigabyte GTX 1050 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050_G1_GAMING_SUB_DEV, 0x47);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming Rev A1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x47);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x48);
REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_G1_GAMING_SUB_DEV, 0x48);
@@ -17,7 +17,7 @@ HyperXAlloyOrigins60and65Controller::HyperXAlloyOrigins60and65Controller(hid_dev
{
dev = dev_handle;
location = path;
dev_name = name;
name = dev_name;
}
HyperXAlloyOrigins60and65Controller::~HyperXAlloyOrigins60and65Controller()
@@ -0,0 +1,121 @@
/*---------------------------------------------------------*\
| HyperXEve1800Controller.cpp |
| |
| Driver for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "HyperXEve1800Controller.h"
#include "StringUtils.h"
HyperXEve1800Controller::HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name)
{
dev = dev_handle;
location = path;
name = dev_name;
}
HyperXEve1800Controller::~HyperXEve1800Controller()
{
hid_close(dev);
}
std::string HyperXEve1800Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string HyperXEve1800Controller::GetNameString()
{
return(name);
}
std::string HyperXEve1800Controller::GetSerialString()
{
wchar_t serial_string[128];
int ret = hid_get_serial_number_string(dev, serial_string, 128);
if(ret != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
void HyperXEve1800Controller::SetBrightness(unsigned int brightness)
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x40;
buf[0x01] = 0x01;
buf[0x02] = 0x00;
buf[0x03] = 0x00;
buf[0x04] = brightness & 0xFF;
hid_write(dev, buf, 65);
}
void HyperXEve1800Controller::SetLEDsDirect(std::vector<RGBColor> colors)
{
SendDirectInitialization();
if(colors.empty())
{
RGBColor temp = 0x00000000;
SendDirectColorPacket(&temp, 1);
}
else
{
SendDirectColorPacket(&colors[0], (unsigned int)colors.size());
}
}
void HyperXEve1800Controller::SendDirectInitialization()
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x44;
buf[0x01] = 0x01;
buf[0x02] = 0x04;
buf[0x03] = 0x00;
hid_write(dev, buf, 65);
}
void HyperXEve1800Controller::SendDirectColorPacket(RGBColor* color_data, unsigned int color_count)
{
unsigned char buf[65];
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x44;
buf[0x01] = 0x02;
buf[0x02] = 0x00;
buf[0x03] = 0x00;
/*-----------------------------------------------------*\
| The Eve 1800 exposes 10 lighting zones, but this |
| report format can carry up to 20 RGB triplets. |
\*-----------------------------------------------------*/
if(color_count > 20)
{
color_count = 20;
}
for(unsigned int color_idx = 0; color_idx < color_count; color_idx++)
{
buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]);
}
hid_write(dev, buf, 65);
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| HyperXEve1800Controller.h |
| |
| Driver for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
class HyperXEve1800Controller
{
public:
HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name);
~HyperXEve1800Controller();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
void SetBrightness(unsigned int brightness);
void SetLEDsDirect(std::vector<RGBColor> colors);
private:
hid_device* dev;
std::string location;
std::string name;
void SendDirectInitialization();
void SendDirectColorPacket(RGBColor* color_data, unsigned int color_count);
};
@@ -0,0 +1,127 @@
/*---------------------------------------------------------*\
| RGBController_HyperXEve1800.cpp |
| |
| RGBController for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_HyperXEve1800.h"
using namespace std::chrono_literals;
#define HYPERX_EVE_1800_BRIGHTNESS_MIN 0x00
#define HYPERX_EVE_1800_BRIGHTNESS_MAX 0xFF
#define HYPERX_EVE_1800_ZONE_COUNT 10
/**------------------------------------------------------------------*\
@name HyperX Eve 1800
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectHyperXEve1800
@comment
\*-------------------------------------------------------------------*/
RGBController_HyperXEve1800::RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "HyperX";
type = DEVICE_TYPE_KEYBOARD;
description = "HyperX Eve 1800 Keyboard Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.brightness_min = HYPERX_EVE_1800_BRIGHTNESS_MIN;
Direct.brightness_max = HYPERX_EVE_1800_BRIGHTNESS_MAX;
Direct.brightness = HYPERX_EVE_1800_BRIGHTNESS_MAX;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
keepalive_thread_run = 1;
keepalive_thread = new std::thread(&RGBController_HyperXEve1800::KeepaliveThread, this);
}
RGBController_HyperXEve1800::~RGBController_HyperXEve1800()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
delete controller;
}
void RGBController_HyperXEve1800::SetupZones()
{
zone new_zone;
new_zone.name = "Keyboard";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.leds_max = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.leds_count = HYPERX_EVE_1800_ZONE_COUNT;
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++)
{
led new_led;
new_led.name = "Zone ";
new_led.name.append(std::to_string(led_idx + 1));
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_HyperXEve1800::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_HyperXEve1800::DeviceUpdateLEDs()
{
controller->SetLEDsDirect(colors);
last_update_time = std::chrono::steady_clock::now();
}
void RGBController_HyperXEve1800::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::DeviceUpdateMode()
{
controller->SetBrightness(modes[active_mode].brightness);
DeviceUpdateLEDs();
}
void RGBController_HyperXEve1800::KeepaliveThread()
{
while(keepalive_thread_run.load())
{
if(active_mode == 0)
{
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50))
{
UpdateLEDs();
}
}
std::this_thread::sleep_for(10ms);
}
}
@@ -0,0 +1,39 @@
/*---------------------------------------------------------*\
| RGBController_HyperXEve1800.h |
| |
| RGBController for HyperX Eve 1800 keyboard |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "HyperXEve1800Controller.h"
class RGBController_HyperXEve1800 : public RGBController
{
public:
RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr);
~RGBController_HyperXEve1800();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void KeepaliveThread();
private:
HyperXEve1800Controller* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};
@@ -15,12 +15,16 @@
#include "HyperXAlloyOriginsController.h"
#include "HyperXAlloyOriginsCoreController.h"
#include "HyperXAlloyOrigins60and65Controller.h"
#include "HyperXEve1800Controller.h"
#include "HyperXOrigins2_65Controller.h"
#include "RGBController_HyperXAlloyElite.h"
#include "RGBController_HyperXAlloyElite2.h"
#include "RGBController_HyperXAlloyFPS.h"
#include "RGBController_HyperXAlloyOrigins.h"
#include "RGBController_HyperXAlloyOriginsCore.h"
#include "RGBController_HyperXAlloyOrigins60and65.h"
#include "RGBController_HyperXEve1800.h"
#include "RGBController_HyperXOrigins2_65.h"
/*-----------------------------------------------------*\
| HyperX keyboard vendor and product IDs |
@@ -44,6 +48,8 @@
#define HYPERX_ALLOY_ORIGINS_65_HP_PID 0x038F
#define HYPERX_ALLOY_ORIGINS_CORE_HP_PID 0x098F
#define HYPERX_ALLOY_ORIGINS_HP_PID 0x0591
#define HYPERX_EVE_1800_HP_PID 0x08C2
#define HYPERX_ORIGINS_2_65_HP_PID 0x0CC2
AlloyOrigins60and65MappingLayoutType GetAlloyOrigins60and65MappingLayoutType(int pid)
{
@@ -140,12 +146,41 @@ void DetectHyperXAlloyOrigins60and65(hid_device_info* info, const std::string& n
}
}
void DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
HyperXOrigins2_65Controller* controller = new HyperXOrigins2_65Controller(dev, info->path, name);
RGBController_HyperXOrigins2_65* rgb_controller = new RGBController_HyperXOrigins2_65(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
void DetectHyperXEve1800(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name);
RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXAlloyElite, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXAlloyFPS, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOriginsCore, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 2);
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core (HP)", DetectHyperXAlloyOriginsCore, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_HP_PID, 2);
REGISTER_HID_DETECTOR_I("HyperX Origins 2 65 (HP)", DetectHyperXOrigins2_65, HP_KEYBOARD_VID, HYPERX_ORIGINS_2_65_HP_PID, 3);
REGISTER_HID_DETECTOR_I("HyperX Eve 1800 (HP)", DetectHyperXEve1800, HP_KEYBOARD_VID, HYPERX_EVE_1800_HP_PID, 2);
#ifdef _WIN32
REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3);
REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90);
@@ -0,0 +1,151 @@
/*---------------------------------------------------------*\
| HyperXOrigins2_65Controller.cpp |
| |
| Driver for HyperX Origins 2 65 keyboard |
| |
| Ricardo Amorim 28 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "HyperXOrigins2_65Controller.h"
#include "StringUtils.h"
HyperXOrigins2_65Controller::HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name)
{
dev = dev_handle;
location = path;
name = dev_name;
}
HyperXOrigins2_65Controller::~HyperXOrigins2_65Controller()
{
hid_close(dev);
}
std::string HyperXOrigins2_65Controller::GetDeviceLocation()
{
return("HID " + location);
}
std::string HyperXOrigins2_65Controller::GetNameString()
{
return(name);
}
std::string HyperXOrigins2_65Controller::GetSerialString()
{
wchar_t serial_string[128];
int ret = hid_get_serial_number_string(dev, serial_string, 128);
if(ret != 0)
{
return ("");
}
return(StringUtils::wstring_to_string(serial_string));
}
void HyperXOrigins2_65Controller::SetLEDsDirect(std::vector<RGBColor> colors)
{
/*-----------------------------------------------------*\
| Set up variables to track progress of color transmit |
| Do this after inserting blanks |
\*-----------------------------------------------------*/
int colors_to_send = (int)colors.size();
int colors_sent = 0;
SendDirectInitialization();
for(int pkt_idx = 0; pkt_idx < 4; pkt_idx++)
{
if(colors_to_send > 20)
{
SendDirectColorPacket(&colors[colors_sent], 20, pkt_idx);
colors_sent += 20;
colors_to_send -= 20;
}
else if(colors_to_send > 0)
{
SendDirectColorPacket(&colors[colors_sent], colors_to_send, pkt_idx);
colors_sent += colors_to_send;
colors_to_send -= colors_to_send;
}
else
{
RGBColor temp = 0x00000000;
SendDirectColorPacket(&temp, 1, pkt_idx);
}
}
}
void HyperXOrigins2_65Controller::SendDirectInitialization()
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Direct Initialization packet |
\*-----------------------------------------------------*/
buf[0x00] = 0x44;
buf[0x01] = 0x01;
buf[0x02] = 0x04;
buf[0x03] = 0x00;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, buf, 65);
}
void HyperXOrigins2_65Controller::SendDirectColorPacket
(
RGBColor* color_data,
unsigned int color_count,
unsigned int seq
)
{
unsigned char buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(buf, 0x00, sizeof(buf));
/*-----------------------------------------------------*\
| Set up Direct Initialization packet |
\*-----------------------------------------------------*/
buf[0x00] = 0x44;
buf[0x01] = 0x02;
buf[0x02] = (unsigned char)seq;
buf[0x03] = 0x00;
/*-----------------------------------------------------*\
| The maximum number of colors per packet is 20 |
\*-----------------------------------------------------*/
if(color_count > 20)
{
color_count = 20;
}
/*-----------------------------------------------------*\
| Copy in color data |
\*-----------------------------------------------------*/
for(unsigned int color_idx = 0; color_idx < color_count; color_idx++)
{
buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]);
buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]);
}
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, buf, 65);
}
@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| HyperXOrigins2_65Controller.h |
| |
| Driver for HyperX Origins 2 65 keyboard |
| |
| Ricardo Amorim 28 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
class HyperXOrigins2_65Controller
{
public:
HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name);
~HyperXOrigins2_65Controller();
std::string GetDeviceLocation();
std::string GetNameString();
std::string GetSerialString();
void SetLEDsDirect(std::vector<RGBColor> colors);
private:
hid_device* dev;
std::string location;
std::string name;
void SendDirectInitialization();
void SendDirectColorPacket
(
RGBColor* color_data,
unsigned int color_count,
unsigned int seq
);
};
@@ -0,0 +1,257 @@
/*---------------------------------------------------------*\
| RGBController_HyperXOrigins2_65.cpp |
| |
| RGBController for HyperX Origins 2 65 keyboard |
| |
| Ricardo Amorim 28 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBControllerKeyNames.h"
#include "RGBController_HyperXOrigins2_65.h"
using namespace std::chrono_literals;
//0xFFFFFFFF indicates an unused entry in matrix
#define NA 0xFFFFFFFF
static unsigned int matrix_map[5][15] =
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, NA, 29 },
{ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 },
{ 45, 61, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 59 },
{ 60, 62, 63, 65, NA, NA, 66, NA, NA, 68, 69, 70, 71, 72, 73 } };
static const char* zone_names[] =
{
ZONE_EN_KEYBOARD,
};
static zone_type zone_types[] =
{
ZONE_TYPE_MATRIX,
};
static const unsigned int zone_sizes[] =
{
74,
};
static const char *led_names[] =
{
KEY_EN_ESCAPE,
KEY_EN_1,
KEY_EN_2,
KEY_EN_3,
KEY_EN_4,
KEY_EN_5,
KEY_EN_6,
KEY_EN_7,
KEY_EN_8,
KEY_EN_9,
KEY_EN_0,
KEY_EN_MINUS,
KEY_EN_EQUALS,
KEY_EN_BACKSPACE,
KEY_EN_DELETE,
KEY_EN_TAB,
KEY_EN_Q,
KEY_EN_W,
KEY_EN_E,
KEY_EN_R,
KEY_EN_T,
KEY_EN_Y,
KEY_EN_U,
KEY_EN_I,
KEY_EN_O,
KEY_EN_P,
KEY_EN_LEFT_BRACKET,
KEY_EN_RIGHT_BRACKET,
KEY_EN_UNUSED,
KEY_EN_HOME,
KEY_EN_CAPS_LOCK,
KEY_EN_A,
KEY_EN_S,
KEY_EN_D,
KEY_EN_F,
KEY_EN_G,
KEY_EN_H,
KEY_EN_J,
KEY_EN_K,
KEY_EN_L,
KEY_EN_SEMICOLON,
KEY_EN_QUOTE,
KEY_EN_POUND,
KEY_EN_ISO_ENTER,
KEY_EN_PAGE_UP,
KEY_EN_LEFT_SHIFT,
KEY_EN_Z,
KEY_EN_X,
KEY_EN_C,
KEY_EN_V,
KEY_EN_B,
KEY_EN_N,
KEY_EN_M,
KEY_EN_COMMA,
KEY_EN_PERIOD,
KEY_EN_FORWARD_SLASH,
KEY_EN_RIGHT_SHIFT,
KEY_EN_UP_ARROW,
KEY_EN_UNUSED,
KEY_EN_PAGE_DOWN,
KEY_EN_LEFT_CONTROL,
KEY_EN_ISO_BACK_SLASH,
KEY_EN_LEFT_WINDOWS,
KEY_EN_LEFT_ALT,
KEY_EN_UNUSED,
"Left Space",
KEY_EN_SPACE,
"Right Space",
KEY_EN_UNUSED,
KEY_EN_RIGHT_ALT,
KEY_EN_RIGHT_FUNCTION,
KEY_EN_LEFT_ARROW,
KEY_EN_DOWN_ARROW,
KEY_EN_RIGHT_ARROW
};
/**------------------------------------------------------------------*\
@name HyperX Origins 2 65
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectHyperXOrigins2_65
@comment
\*-------------------------------------------------------------------*/
RGBController_HyperXOrigins2_65::RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "HyperX";
type = DEVICE_TYPE_KEYBOARD;
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
keepalive_thread_run = 1;
keepalive_thread = new std::thread(&RGBController_HyperXOrigins2_65::KeepaliveThread, this);
}
RGBController_HyperXOrigins2_65::~RGBController_HyperXOrigins2_65()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
/*---------------------------------------------------------*\
| Delete the matrix map |
\*---------------------------------------------------------*/
for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++)
{
if(zones[zone_index].matrix_map != NULL)
{
delete zones[zone_index].matrix_map;
}
}
delete controller;
}
void RGBController_HyperXOrigins2_65::SetupZones()
{
/*---------------------------------------------------------*\
| Set up zones |
\*---------------------------------------------------------*/
unsigned int total_led_count = 0;
for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++)
{
zone new_zone;
new_zone.name = zone_names[zone_idx];
new_zone.type = zone_types[zone_idx];
new_zone.leds_min = zone_sizes[zone_idx];
new_zone.leds_max = zone_sizes[zone_idx];
new_zone.leds_count = zone_sizes[zone_idx];
if(zone_types[zone_idx] == ZONE_TYPE_MATRIX)
{
new_zone.matrix_map = new matrix_map_type;
new_zone.matrix_map->height = 5;
new_zone.matrix_map->width = 15;
new_zone.matrix_map->map = (unsigned int *)&matrix_map;
}
else
{
new_zone.matrix_map = NULL;
}
zones.push_back(new_zone);
total_led_count += zone_sizes[zone_idx];
}
for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++)
{
led new_led;
new_led.name = led_names[led_idx];
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_HyperXOrigins2_65::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_HyperXOrigins2_65::DeviceUpdateLEDs()
{
controller->SetLEDsDirect(colors);
}
void RGBController_HyperXOrigins2_65::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXOrigins2_65::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_HyperXOrigins2_65::DeviceUpdateMode()
{
}
void RGBController_HyperXOrigins2_65::KeepaliveThread()
{
while(keepalive_thread_run.load())
{
if(active_mode == 0)
{
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50))
{
UpdateLEDs();
}
}
std::this_thread::sleep_for(10ms);;
}
}
@@ -0,0 +1,42 @@
/*---------------------------------------------------------*\
| RGBController_HyperXOrigins2_65.h |
| |
| RGBController for HyperX Origins 2 65 keyboard |
| |
| Ricardo Amorim 28 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "HyperXOrigins2_65Controller.h"
class RGBController_HyperXOrigins2_65 : public RGBController
{
public:
RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr);
~RGBController_HyperXOrigins2_65();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void KeepaliveThread();
private:
HyperXOrigins2_65Controller* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};
@@ -38,6 +38,7 @@
#define LEGION_7GEN9 0xC997
#define LEGION_7GEN9_H 0xC998
#define LEGION_7GEN10 0xC197
#define LEGION_5GEN10 0xC195
enum LENOVO_KEYBOARD
{
@@ -67,3 +67,4 @@ REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 8", DetectLenovoLegionUSBControl
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9_H, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN10, LENOVO_PAGE, LENOVO_USAGE);
REGISTER_HID_DETECTOR_PU("Lenovo Legion 5 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_5GEN10, LENOVO_PAGE, LENOVO_USAGE);
@@ -17,7 +17,7 @@ using namespace std;
static void SetGen10PayloadLength(uint16_t pid, uint8_t* buffer, uint16_t payload_length)
{
if(pid != LEGION_7GEN10)
if(pid != LEGION_7GEN10 && pid != LEGION_5GEN10)
{
return;
}
@@ -26,6 +26,11 @@ static void SetGen10PayloadLength(uint16_t pid, uint8_t* buffer, uint16_t payloa
buffer[3] = (payload_length >> 8) & 0xFF;
}
static bool UsesGen10PacketFormat(uint16_t pid)
{
return pid == LEGION_7GEN10 || pid == LEGION_5GEN10;
}
LenovoGen7And8USBController::LenovoGen7And8USBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name)
{
dev = dev_handle;
@@ -114,7 +119,7 @@ void LenovoGen7And8USBController::setLedsByGroup(uint8_t profile_id, vector<led_
i+= led_count * sizeof(uint16_t);
}
if(pid == LEGION_7GEN10)
if(UsesGen10PacketFormat(pid))
{
SetGen10PayloadLength(pid, buffer, static_cast<uint16_t>(i - 4));
}
@@ -162,7 +167,7 @@ void LenovoGen7And8USBController::setLedsDirectOff(uint8_t profile_id)
void LenovoGen7And8USBController::setLedsDirect(std::vector<led> &leds, std::vector<RGBColor> &colors)
{
if(pid == LEGION_7GEN10)
if(UsesGen10PacketFormat(pid))
{
/*---------------------------------------------------------*\
| Gen10 uses 0x07/A1 direct updates, with payload length |
@@ -13,6 +13,78 @@
using namespace std;
static bool UsesGen10PacketFormat(uint16_t pid)
{
return pid == LEGION_7GEN10 || pid == LEGION_5GEN10;
}
static bool Is24ZoneDevice(uint16_t pid)
{
return pid == LEGION_5GEN10;
}
static bool IsKeyboardOnlyDevice(uint16_t pid)
{
return pid == LEGION_5GEN10;
}
static const lenovo_led legion_5gen10_24zone_leds[] =
{
{0x01, "Zone R1C1"},
{0x02, "Zone R1C2"},
{0x03, "Zone R1C3"},
{0x04, "Zone R1C4"},
{0x05, "Zone R1C5"},
{0x06, "Zone R1C6"},
{0x07, "Zone R2C1"},
{0x08, "Zone R2C2"},
{0x09, "Zone R2C3"},
{0x0A, "Zone R2C4"},
{0x0B, "Zone R2C5"},
{0x0C, "Zone R2C6"},
{0x0D, "Zone R3C1"},
{0x0E, "Zone R3C2"},
{0x0F, "Zone R3C3"},
{0x10, "Zone R3C4"},
{0x11, "Zone R3C5"},
{0x12, "Zone R3C6"},
{0x13, "Zone R4C1"},
{0x14, "Zone R4C2"},
{0x15, "Zone R4C3"},
{0x16, "Zone R4C4"},
{0x17, "Zone R4C5"},
{0x18, "Zone R4C6"},
};
static const unsigned int legion_5gen10_24zone_matrix_map[] =
{
0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23,
};
static const lenovo_zone legion_5gen10_kbd_24zone =
{
"Keyboard (24-zone)",
ZONE_TYPE_MATRIX,
0,
4,
6,
legion_5gen10_24zone_matrix_map,
legion_5gen10_24zone_leds,
0,
23,
};
static const RGBColor legion_5gen10_zone_visualization_colors[24] =
{
0xFF0000, 0x00FFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF00FF,
0xFF7F00, 0x007FFF, 0x7FFF00, 0x7F00FF, 0xFF007F, 0x00FF7F,
0xFFD700, 0x0055FF, 0x55FF00, 0xFF0055, 0x00BFFF, 0xBF00FF,
0xFF3B00, 0x00FF3B, 0x3B00FF, 0xFF1493, 0x14FF93, 0x9314FF,
};
LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBController* controller_ptr)
{
controller = controller_ptr;
@@ -139,72 +211,79 @@ LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBControll
Smooth.brightness = brightness;
modes.push_back(Smooth);
mode Rain;
Rain.name = "Rain";
Rain.value = LENOVO_LEGION_GEN7_8_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Rain.speed_min = 0x01;
Rain.speed_max = 0x03;
Rain.speed = 2;
Rain.colors_min = 1;
Rain.colors_max = 4;
Rain.colors.resize(4);
Rain.colors[0] = 0xFFF500;
Rain.color_mode = MODE_COLORS_RANDOM;
Rain.brightness_min = 0;
Rain.brightness_max = 9;
Rain.brightness = brightness;
modes.push_back(Rain);
if(!Is24ZoneDevice(controller->getPid()))
{
mode Rain;
Rain.name = "Rain";
Rain.value = LENOVO_LEGION_GEN7_8_MODE_RAIN;
Rain.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Rain.speed_min = 0x01;
Rain.speed_max = 0x03;
Rain.speed = 2;
Rain.colors_min = 1;
Rain.colors_max = 4;
Rain.colors.resize(4);
Rain.colors[0] = 0xFFF500;
Rain.color_mode = MODE_COLORS_RANDOM;
Rain.brightness_min = 0;
Rain.brightness_max = 9;
Rain.brightness = brightness;
modes.push_back(Rain);
}
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = LENOVO_LEGION_GEN7_8_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Ripple.speed_min = 0x01;
Ripple.speed_max = 0x03;
Ripple.speed = 2;
Ripple.colors_min = 1;
Ripple.colors_max = 4;
Ripple.colors.resize(4);
Ripple.colors[0] = 0xFFF500;
Ripple.color_mode = MODE_COLORS_RANDOM;
Ripple.brightness_min = 0;
Ripple.brightness_max = 9;
Ripple.brightness = brightness;
modes.push_back(Ripple);
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
mode Ripple;
Ripple.name = "Ripple";
Ripple.value = LENOVO_LEGION_GEN7_8_MODE_RIPPLE;
Ripple.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Ripple.speed_min = 0x01;
Ripple.speed_max = 0x03;
Ripple.speed = 2;
Ripple.colors_min = 1;
Ripple.colors_max = 4;
Ripple.colors.resize(4);
Ripple.colors[0] = 0xFFF500;
Ripple.color_mode = MODE_COLORS_RANDOM;
Ripple.brightness_min = 0;
Ripple.brightness_max = 9;
Ripple.brightness = brightness;
modes.push_back(Ripple);
mode AudioBounce;
AudioBounce.name = "Audio Bounce Lighting";
AudioBounce.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE;
AudioBounce.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioBounce.color_mode = MODE_COLORS_NONE;
AudioBounce.brightness_min = 0;
AudioBounce.brightness_max = 9;
AudioBounce.brightness = brightness;
modes.push_back(AudioBounce);
mode AudioBounce;
AudioBounce.name = "Audio Bounce Lighting";
AudioBounce.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE;
AudioBounce.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioBounce.color_mode = MODE_COLORS_NONE;
AudioBounce.brightness_min = 0;
AudioBounce.brightness_max = 9;
AudioBounce.brightness = brightness;
modes.push_back(AudioBounce);
mode AudioRipple;
AudioRipple.name = "Audio Ripple Lighting";
AudioRipple.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE;
AudioRipple.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioRipple.color_mode = MODE_COLORS_NONE;
AudioRipple.brightness_min = 0;
AudioRipple.brightness_max = 9;
AudioRipple.brightness = brightness;
modes.push_back(AudioRipple);
mode AudioRipple;
AudioRipple.name = "Audio Ripple Lighting";
AudioRipple.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE;
AudioRipple.flags = MODE_FLAG_HAS_BRIGHTNESS;
AudioRipple.color_mode = MODE_COLORS_NONE;
AudioRipple.brightness_min = 0;
AudioRipple.brightness_max = 9;
AudioRipple.brightness = brightness;
modes.push_back(AudioRipple);
}
mode Static;
Static.name = "Static";
Static.value = LENOVO_LEGION_GEN7_8_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Static.color_mode = MODE_COLORS_PER_LED;
@@ -213,37 +292,43 @@ LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBControll
Static.brightness = brightness;
modes.push_back(Static);
mode Type;
Type.name = "Type Lighting";
Type.value = LENOVO_LEGION_GEN7_8_MODE_TYPE;
Type.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Type.speed_min = 0x01;
Type.speed_max = 0x03;
Type.speed = 2;
Type.colors_min = 1;
Type.colors_max = 4;
Type.colors.resize(4);
Type.colors[0] = 0xFFF500;
Type.color_mode = MODE_COLORS_RANDOM;
Type.brightness_min = 0;
Type.brightness_max = 9;
Type.brightness = brightness;
modes.push_back(Type);
if(!Is24ZoneDevice(controller->getPid()))
{
mode Type;
Type.name = "Type Lighting";
Type.value = LENOVO_LEGION_GEN7_8_MODE_TYPE;
Type.flags = MODE_FLAG_HAS_SPEED |
MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |
MODE_FLAG_HAS_RANDOM_COLOR |
MODE_FLAG_HAS_BRIGHTNESS |
MODE_FLAG_AUTOMATIC_SAVE;
Type.speed_min = 0x01;
Type.speed_max = 0x03;
Type.speed = 2;
Type.colors_min = 1;
Type.colors_max = 4;
Type.colors.resize(4);
Type.colors[0] = 0xFFF500;
Type.color_mode = MODE_COLORS_RANDOM;
Type.brightness_min = 0;
Type.brightness_max = 9;
Type.brightness = brightness;
modes.push_back(Type);
}
mode Direct;
Direct.name = "Direct";
Direct.value = LENOVO_LEGION_GEN7_8_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR |
MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 9;
Direct.brightness = brightness;
modes.push_back(Direct);
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
mode Direct;
Direct.name = "Direct";
Direct.value = LENOVO_LEGION_GEN7_8_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR |
MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 9;
Direct.brightness = brightness;
modes.push_back(Direct);
}
name = controller->getName();
type = DEVICE_TYPE_KEYBOARD;
@@ -275,6 +360,10 @@ LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBControll
case LEGION_7GEN10:
description = "Lenovo Legion 7 Generation 10";
break;
case LEGION_5GEN10:
description = "Lenovo Legion 5 Gen 10";
break;
}
brightness = controller->getCurrentBrightness();
@@ -310,8 +399,19 @@ LenovoRGBController_Gen7_8::~LenovoRGBController_Gen7_8()
void LenovoRGBController_Gen7_8::SetupZones()
{
vector<lenovo_zone> lenovo_zones;
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
lenovo_zones.push_back(legion7_gen7and8_neon);
if(Is24ZoneDevice(controller->getPid()))
{
lenovo_zones.push_back(legion_5gen10_kbd_24zone);
}
else
{
lenovo_zones.push_back(legion7_gen7and8_kbd_ansi);
}
if(!IsKeyboardOnlyDevice(controller->getPid()))
{
lenovo_zones.push_back(legion7_gen7and8_neon);
}
if (controller->getPid() == LEGION_7GEN7)
{
@@ -422,7 +522,7 @@ void LenovoRGBController_Gen7_8::DeviceUpdateMode()
{
controller->setLedsDirectOn(profile_id);
direct_enabled = true;
if(controller->getPid() != LEGION_7GEN10)
if(!UsesGen10PacketFormat(controller->getPid()))
{
controller->setLedsByGroup(profile_id, GetLedGroups());
}
@@ -446,7 +546,7 @@ void LenovoRGBController_Gen7_8::DeviceUpdateLEDs()
{
if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT)
{
if(controller->getPid() == LEGION_7GEN10)
if(UsesGen10PacketFormat(controller->getPid()))
{
/*---------------------------------------------*\
| Gen10 may ignore A1 updates unless D0 is |
@@ -570,6 +670,29 @@ void LenovoRGBController_Gen7_8::ReadDeviceSettings()
std::vector<led_group> LenovoRGBController_Gen7_8::GetLedGroups()
{
if(Is24ZoneDevice(controller->getPid()) &&
modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_STATIC &&
modes[active_mode].color_mode == MODE_COLORS_RANDOM)
{
vector<led_group> led_groups;
led_groups.reserve(leds.size());
for(size_t i = 0; i < leds.size(); i++)
{
led_group group;
group.mode = modes[active_mode].value;
group.speed = modes[active_mode].speed;
group.spin = 0x00;
group.direction = 0x00;
group.color_mode = 0x02;
group.colors.push_back(legion_5gen10_zone_visualization_colors[i % 24]);
group.leds.push_back(leds[i].value);
led_groups.push_back(group);
}
return led_groups;
}
std::unordered_map<RGBColor, vector<uint16_t>> led_map;
if(modes[active_mode].color_mode == MODE_COLORS_PER_LED &&
@@ -235,7 +235,7 @@ void LianLiUniHubSLV2Controller::SetChannelMode(unsigned char channel, const mod
void LianLiUniHubSLV2Controller::SendStartAction(unsigned char channel, unsigned int num_fans)
{
unsigned char usb_buf[65];
unsigned char usb_buf[353];
/*-----------------------------------------------------*\
| Zero out buffer |
@@ -291,7 +291,7 @@ void LianLiUniHubSLV2Controller::SendColorData(unsigned char channel, unsigned
void LianLiUniHubSLV2Controller::SendCommitAction(unsigned char channel, unsigned char effect, unsigned char speed, unsigned int direction, unsigned int brightness)
{
unsigned char usb_buf[65];
unsigned char usb_buf[353];
/*-----------------------------------------------------*\
| Zero out buffer |
@@ -47,6 +47,7 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim",
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim MLG", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_MLG_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68);
@@ -67,11 +68,13 @@ REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Trio",
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_GAMING_X_SLIM_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim Liquid X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_LIQUID_X_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_X_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Gaming Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, MSI_SUB_VEN, MSI_RTX5070_GAMING_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti Gaming Trio OC Plus", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_GAMING_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_VANGUARD_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_GAMING_TRIO_OC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 VANGUARD OC SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_VANGUARD_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_GAMING_TRIO_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_VANGUARD_SOC_SUB_DEV, 0x68);
REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_SUPRIM_SOC_SUB_DEV, 0x68);
@@ -20,7 +20,7 @@
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectMSIMysticLight1565Controller
@detectors DetectMSIKeyboardController
@comment
\*-------------------------------------------------------------------*/
@@ -0,0 +1,89 @@
/*---------------------------------------------------------*\
| MSILaptopController.cpp |
| |
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include "MSILaptopController.h"
#include "StringUtils.h"
#define MSI_LAPTOP_REPORT_ID 0x00
#define MSI_LAPTOP_COMMAND 0x0C
#define MSI_LAPTOP_KLC_PACKET_ID 0x66
#define MSI_LAPTOP_ALC_PACKET_ID 0x06
#define MSI_LAPTOP_PACKET_SIZE 525
#define MSI_LAPTOP_PAYLOAD_OFFSET 5
MSILaptopController::MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type)
{
dev = dev_handle;
location = path;
name = dev_name;
type = device_type;
}
MSILaptopController::~MSILaptopController()
{
hid_close(dev);
}
std::string MSILaptopController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string MSILaptopController::GetDeviceName()
{
return(name);
}
std::string MSILaptopController::GetSerialString()
{
wchar_t serial_string[128];
int ret = hid_get_serial_number_string(dev, serial_string, 128);
if(ret != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
msi_laptop_device MSILaptopController::GetDeviceType()
{
return(type);
}
void MSILaptopController::SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors)
{
unsigned char buf[MSI_LAPTOP_PACKET_SIZE];
unsigned int led_count = (leds.size() < colors.size()) ? leds.size() : colors.size();
memset(buf, 0x00, sizeof(buf));
buf[0x00] = MSI_LAPTOP_REPORT_ID;
buf[0x01] = MSI_LAPTOP_COMMAND;
buf[0x03] = (type == MSI_LAPTOP_KLC) ? MSI_LAPTOP_KLC_PACKET_ID : MSI_LAPTOP_ALC_PACKET_ID;
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
unsigned int offset = MSI_LAPTOP_PAYLOAD_OFFSET + (led_idx * 4);
if((offset + 3) >= sizeof(buf))
{
break;
}
buf[offset + 0] = leds[led_idx].value;
buf[offset + 1] = RGBGetRValue(colors[led_idx]);
buf[offset + 2] = RGBGetGValue(colors[led_idx]);
buf[offset + 3] = RGBGetBValue(colors[led_idx]);
}
hid_send_feature_report(dev, buf, sizeof(buf));
}
@@ -0,0 +1,67 @@
/*---------------------------------------------------------*\
| MSILaptopController.h |
| |
| Driver for MSI laptop SteelSeries KLC/ALC RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
typedef enum
{
MSI_LAPTOP_KLC,
MSI_LAPTOP_ALC,
} msi_laptop_device;
typedef struct
{
const char* name;
unsigned char id;
} msi_laptop_led;
struct MSILaptopModel
{
const char* sys_vendor;
const char* product_name;
/* Keyboard layout */
const msi_laptop_led* klc_leds;
unsigned int klc_leds_count;
unsigned int klc_matrix_height;
unsigned int klc_matrix_width;
const unsigned int* klc_matrix_map;
/* Lightbar layout */
const msi_laptop_led* alc_leds;
unsigned int alc_leds_count;
unsigned int alc_lightbar_leds;
};
class MSILaptopController
{
public:
MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type);
~MSILaptopController();
std::string GetDeviceLocation();
std::string GetDeviceName();
std::string GetSerialString();
msi_laptop_device GetDeviceType();
void SetLEDs(std::vector<led> leds, std::vector<RGBColor> colors);
private:
hid_device* dev;
std::string location;
std::string name;
msi_laptop_device type;
};
@@ -0,0 +1,225 @@
/*---------------------------------------------------------*\
| MSILaptopControllerDetect.cpp |
| |
| Detector for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBControllerKeyNames.h"
#include "RGBController_MSILaptop.h"
#include "MSILaptopController.h"
#include "dmiinfo.h"
#define MSI_LAPTOP_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define STEELSERIES_VID 0x1038
#define STEELSERIES_MSI_RAIDER_A18_KLC_PID 0x1122
#define STEELSERIES_MSI_RAIDER_A18_ALC_PID 0x1161
#define NA 0xFFFFFFFF
#define MSI_LAPTOP_KLC_MATRIX_HEIGHT 6
#define MSI_LAPTOP_KLC_MATRIX_WIDTH 23
#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 5
static const msi_laptop_led msi_raider_a18_klc_leds[] =
{
{ KEY_EN_A, 0x04 },
{ KEY_EN_B, 0x05 },
{ KEY_EN_C, 0x06 },
{ KEY_EN_D, 0x07 },
{ KEY_EN_E, 0x08 },
{ KEY_EN_F, 0x09 },
{ KEY_EN_G, 0x0A },
{ KEY_EN_H, 0x0B },
{ KEY_EN_I, 0x0C },
{ KEY_EN_J, 0x0D },
{ KEY_EN_K, 0x0E },
{ KEY_EN_L, 0x0F },
{ KEY_EN_M, 0x10 },
{ KEY_EN_N, 0x11 },
{ KEY_EN_O, 0x12 },
{ KEY_EN_P, 0x13 },
{ KEY_EN_Q, 0x14 },
{ KEY_EN_R, 0x15 },
{ KEY_EN_S, 0x16 },
{ KEY_EN_T, 0x17 },
{ KEY_EN_U, 0x18 },
{ KEY_EN_V, 0x19 },
{ KEY_EN_W, 0x1A },
{ KEY_EN_X, 0x1B },
{ KEY_EN_Y, 0x1C },
{ KEY_EN_Z, 0x1D },
{ KEY_EN_1, 0x1E },
{ KEY_EN_2, 0x1F },
{ KEY_EN_3, 0x20 },
{ KEY_EN_4, 0x21 },
{ KEY_EN_5, 0x22 },
{ KEY_EN_6, 0x23 },
{ KEY_EN_7, 0x24 },
{ KEY_EN_8, 0x25 },
{ KEY_EN_9, 0x26 },
{ KEY_EN_0, 0x27 },
{ KEY_EN_ESCAPE, 0x29 },
{ KEY_EN_TAB, 0x2B },
{ KEY_EN_SPACE, 0x2C },
{ KEY_EN_MINUS, 0x2D },
{ KEY_EN_EQUALS, 0x2E },
{ KEY_EN_LEFT_BRACKET, 0x2F },
{ KEY_EN_RIGHT_BRACKET, 0x30 },
{ KEY_EN_SEMICOLON, 0x33 },
{ KEY_EN_QUOTE, 0x34 },
{ KEY_EN_BACK_TICK, 0x35 },
{ KEY_EN_COMMA, 0x36 },
{ KEY_EN_PERIOD, 0x37 },
{ KEY_EN_FORWARD_SLASH, 0x38 },
{ KEY_EN_CAPS_LOCK, 0x39 },
{ KEY_EN_F1, 0x3A },
{ KEY_EN_F2, 0x3B },
{ KEY_EN_F3, 0x3C },
{ KEY_EN_F4, 0x3D },
{ KEY_EN_F5, 0x3E },
{ KEY_EN_F6, 0x3F },
{ KEY_EN_F7, 0x40 },
{ KEY_EN_F8, 0x41 },
{ KEY_EN_F9, 0x42 },
{ KEY_EN_F10, 0x43 },
{ KEY_EN_F11, 0x44 },
{ KEY_EN_F12, 0x45 },
{ KEY_EN_PRINT_SCREEN, 0x46 },
{ KEY_EN_SCROLL_LOCK, 0x47 },
{ KEY_EN_INSERT, 0x49 },
{ "Home/Page Up", 0x4B },
{ KEY_EN_DELETE, 0x4C },
{ KEY_EN_PAGE_DOWN, 0x4E },
{ KEY_EN_RIGHT_ARROW, 0x4F },
{ KEY_EN_LEFT_ARROW, 0x50 },
{ KEY_EN_DOWN_ARROW, 0x51 },
{ KEY_EN_UP_ARROW, 0x52 },
{ KEY_EN_NUMPAD_LOCK, 0x53 },
{ KEY_EN_NUMPAD_DIVIDE, 0x54 },
{ KEY_EN_NUMPAD_TIMES, 0x55 },
{ KEY_EN_NUMPAD_MINUS, 0x56 },
{ KEY_EN_NUMPAD_PLUS, 0x57 },
{ KEY_EN_NUMPAD_ENTER, 0x58 },
{ KEY_EN_NUMPAD_1, 0x59 },
{ KEY_EN_NUMPAD_2, 0x5A },
{ KEY_EN_NUMPAD_3, 0x5B },
{ KEY_EN_NUMPAD_4, 0x5C },
{ KEY_EN_NUMPAD_5, 0x5D },
{ KEY_EN_NUMPAD_6, 0x5E },
{ KEY_EN_NUMPAD_7, 0x5F },
{ KEY_EN_NUMPAD_8, 0x60 },
{ KEY_EN_NUMPAD_9, 0x61 },
{ KEY_EN_NUMPAD_0, 0x62 },
{ KEY_EN_NUMPAD_PERIOD, 0x63 },
{ KEY_EN_POWER, 0x66 },
{ KEY_EN_LEFT_CONTROL, 0xE0 },
{ KEY_EN_LEFT_SHIFT, 0xE1 },
{ KEY_EN_LEFT_ALT, 0xE2 },
{ KEY_EN_LEFT_WINDOWS, 0xE3 },
{ KEY_EN_RIGHT_WINDOWS, 0xE4 },
{ KEY_EN_RIGHT_FUNCTION, 0xF0 },
{ KEY_EN_ANSI_ENTER, 0x28 },
{ KEY_EN_BACKSPACE, 0x2A },
{ KEY_EN_BACK_SLASH, 0x31 },
{ KEY_EN_ISO_BACK_SLASH, 0x64 },
{ KEY_EN_RIGHT_SHIFT, 0xE5 },
{ KEY_EN_RIGHT_ALT, 0xE6 },
};
static unsigned int msi_raider_a18_klc_matrix_map[MSI_LAPTOP_KLC_MATRIX_HEIGHT][MSI_LAPTOP_KLC_MATRIX_WIDTH] =
{
{ 36, NA, 50, 51, 52, 53, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, 62, 63, NA, NA, NA, NA, 89 },
{ 45, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 39, 40, 97, NA, 64, 65, NA, 72, 73, 74, 75, NA },
{ 37, 16, 22, 4, 17, 19, 24, 20, 8, 14, 15, 41, 42, 98, NA, 66, 67, NA, 84, 85, 86, 76, NA },
{ 49, 0, 18, 3, 5, 6, 7, 9, 10, 11, 43, 44, 96, NA, NA, NA, NA, NA, 81, 82, 83, NA, NA },
{ 91, 25, 23, 2, 21, 1, 13, 12, 46, 47, 48,100, NA, NA, NA, NA, 71, NA, 78, 79, 80, 77, NA },
{ 90, 93, 92, NA, NA, NA, 38, NA, NA, NA,101, 94, 95, NA, 69, 70, 68, NA, 87, NA, 88, NA, NA },
};
static const msi_laptop_led msi_raider_a18_alc_leds[] =
{
{ "Lightbar 1", 0x00 },
{ "Lightbar 2", 0x01 },
{ "Lightbar 3", 0x02 },
{ "Lightbar 4", 0x03 },
{ "Lightbar 5", 0x04 },
{ "Logo", 0x05 },
};
static const MSILaptopModel msi_laptop_models[] =
{
{
"Micro-Star International Co., Ltd.",
"Raider A18 HX A9WJG",
/* Keyboard layout */
msi_raider_a18_klc_leds,
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_klc_leds),
MSI_LAPTOP_KLC_MATRIX_HEIGHT,
MSI_LAPTOP_KLC_MATRIX_WIDTH,
(const unsigned int*)msi_raider_a18_klc_matrix_map,
/* Lightbar layout */
msi_raider_a18_alc_leds,
MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_alc_leds),
MSI_LAPTOP_ALC_LIGHTBAR_LEDS,
},
};
static const MSILaptopModel* GetMSILaptopModelDMI()
{
DMIInfo dmi;
for(unsigned int i = 0; i < MSI_LAPTOP_ARRAY_SIZE(msi_laptop_models); i++)
{
if((dmi.getManufacturer() == msi_laptop_models[i].sys_vendor) &&
(dmi.getProductName() == msi_laptop_models[i].product_name))
{
return &msi_laptop_models[i];
}
}
return nullptr;
}
void DetectMSILaptop(hid_device_info* info, const std::string& name)
{
const MSILaptopModel* model = GetMSILaptopModelDMI();
if(!model)
{
return;
}
msi_laptop_device device_type;
if(info->product_id == STEELSERIES_MSI_RAIDER_A18_KLC_PID)
{
device_type = MSI_LAPTOP_KLC;
}
else if(info->product_id == STEELSERIES_MSI_RAIDER_A18_ALC_PID)
{
device_type = MSI_LAPTOP_ALC;
}
else
{
return;
}
hid_device* dev = hid_open_path(info->path);
if(dev)
{
MSILaptopController* controller = new MSILaptopController(dev, info->path, name, device_type);
RGBController_MSILaptop* rgb_controller = new RGBController_MSILaptop(controller, model);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_I("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID, 0);
REGISTER_HID_DETECTOR_I("MSI Laptop Lightbar", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_ALC_PID, 0);
@@ -0,0 +1,141 @@
/*---------------------------------------------------------*\
| RGBController_MSILaptop.cpp |
| |
| RGBController for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBControllerKeyNames.h"
#include "RGBController_MSILaptop.h"
#define NA 0xFFFFFFFF
/**------------------------------------------------------------------*\
@name MSI Laptop SteelSeries RGB
@category Keyboard
@type USB
@save :x:
@direct :white_check_mark:
@effects :x:
@detectors DetectMSILaptop
@comment
\*-------------------------------------------------------------------*/
RGBController_MSILaptop::RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr)
{
controller = controller_ptr;
model = model_ptr;
name = controller->GetDeviceName();
vendor = "SteelSeries";
description = std::string(model->sys_vendor) + " " + std::string(model->product_name) + " RGB Device";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
type = (controller->GetDeviceType() == MSI_LAPTOP_KLC) ? DEVICE_TYPE_KEYBOARD : DEVICE_TYPE_LEDSTRIP;
mode Direct;
Direct.name = "Direct";
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
RGBController_MSILaptop::~RGBController_MSILaptop()
{
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
if(zones[zone_idx].matrix_map != NULL)
{
delete zones[zone_idx].matrix_map;
}
}
delete controller;
}
void RGBController_MSILaptop::SetupZones()
{
if(controller->GetDeviceType() == MSI_LAPTOP_KLC)
{
zone keyboard_zone;
keyboard_zone.name = ZONE_EN_KEYBOARD;
keyboard_zone.type = ZONE_TYPE_MATRIX;
keyboard_zone.leds_min = model->klc_leds_count;
keyboard_zone.leds_max = model->klc_leds_count;
keyboard_zone.leds_count = model->klc_leds_count;
keyboard_zone.matrix_map = new matrix_map_type;
keyboard_zone.matrix_map->height = model->klc_matrix_height;
keyboard_zone.matrix_map->width = model->klc_matrix_width;
keyboard_zone.matrix_map->map = (unsigned int *)model->klc_matrix_map;
zones.push_back(keyboard_zone);
for(unsigned int led_idx = 0; led_idx < model->klc_leds_count; led_idx++)
{
led new_led;
new_led.name = model->klc_leds[led_idx].name;
new_led.value = model->klc_leds[led_idx].id;
leds.push_back(new_led);
}
}
else
{
zone lightbar_zone;
lightbar_zone.name = "Lightbar";
lightbar_zone.type = ZONE_TYPE_LINEAR;
lightbar_zone.start_idx = 0;
lightbar_zone.leds_min = model->alc_lightbar_leds;
lightbar_zone.leds_max = model->alc_lightbar_leds;
lightbar_zone.leds_count = model->alc_lightbar_leds;
lightbar_zone.matrix_map = NULL;
zones.push_back(lightbar_zone);
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.start_idx = model->alc_lightbar_leds;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
for(unsigned int led_idx = 0; led_idx < model->alc_leds_count; led_idx++)
{
led new_led;
new_led.name = model->alc_leds[led_idx].name;
new_led.value = model->alc_leds[led_idx].id;
leds.push_back(new_led);
}
}
SetupColors();
}
void RGBController_MSILaptop::ResizeZone(int /*zone*/, int /*new_size*/)
{
}
void RGBController_MSILaptop::DeviceUpdateLEDs()
{
controller->SetLEDs(leds, colors);
}
void RGBController_MSILaptop::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSILaptop::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSILaptop::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_MSILaptop.h |
| |
| RGBController for MSI laptop SteelSeries RGB devices |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "MSILaptopController.h"
class RGBController_MSILaptop : public RGBController
{
public:
RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr);
~RGBController_MSILaptop();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
MSILaptopController* controller;
const MSILaptopModel* model;
};
@@ -232,6 +232,18 @@ const std::vector<MSI_ZONE> zones_set20 =
MSI_ZONE_J_RGB_1,
};
const std::vector<MSI_ZONE> zones_set21 =
{
MSI_ZONE_J_RGB_1,
MSI_ZONE_J_RGB_2,
MSI_ZONE_J_RAINBOW_1,
MSI_ZONE_J_RAINBOW_2,
MSI_ZONE_J_CORSAIR,
MSI_ZONE_J_PIPE_1,
MSI_ZONE_J_PIPE_2,
MSI_ZONE_ON_BOARD_LED_0
};
/*---------------------------------------------------------------------------------------------------------------------------------*\
| Definition of the board sepcific configurations (number of onboard LEDs and supported zones). |
@@ -333,10 +345,12 @@ static const mystic_light_185_config board_configs[] =
{ 0x7E09, 0, 0, 0, 0, &zones_set19, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650M PROJECT ZERO
{ 0x7E10, 0, 6, 0, 2, &zones_set17, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B650 EDGE WIFI
{ 0x7E12, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X670E TOMAHAWK WIFI
{ 0x0076, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X670E TOMAHAWK WIFI (Common PID)
{ 0x7E16, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // X670E GAMING PLUS WIFI
{ 0x7E24, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650M GAMING PLUS WIFI
{ 0x7E26, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650 GAMING PLUS WIFI
{ 0x7E27, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B650M-P
{ 0x7E28, 6, 0, 0, 2, &zones_set21, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO A620M-B (MS-7E28)
};
@@ -704,6 +718,19 @@ bool MSIMysticLight185Controller::Update
}
else
{
/*-----------------------------------------------------*\
| Save new state, read current state from board, |
| send old state first, then new state. |
| Windows Mystic Light sends two consecutive reports. |
\*-----------------------------------------------------*/
FeaturePacket_185 new_data = data;
memcpy((unsigned char*)&new_data, (unsigned char*)&data, sizeof(data));
ReadSettings();
data.save_data = save;
hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data));
memcpy((unsigned char*)&data, (unsigned char*)&new_data, sizeof(data));
data.save_data = save;
return (hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof(data));
}
@@ -478,7 +478,7 @@ void RGBController_MSIMysticLight185::GetDeviceConfig()
if(zones[i].colors != nullptr)
{
for(size_t j = 0; j < zones[i].leds_count; ++j)
for(size_t j = 0; j < GetLEDsInZone((unsigned int)i); ++j)
{
zones[i].colors[j] = color;
}
@@ -65,7 +65,12 @@ static const std::string board_names[] =
"MSI PRO X870-P WIFI (MS-7E47)",
"MSI MPG X870E EDGE TI WIFI (MS-7E59)",
"MSI MAG B850 TOMAHAWK MAX WIFI (MS-7E62)",
"MSI PRO B850M-P WIFI (MS-7E71)"
"MSI PRO B850M-P WIFI (MS-7E71)",
"MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)",
"MSI MPG B850 EDGE TI WIFI (MS-7E62)",
"MSI PRO B850M-VC WIFI6E (MS-7E71)",
"MSI MAG B850 TOMAHAWK WIFI (MS-7E53)",
"MSI MEG Z890 UNIFY-X (MS-7E20)",
};
static const mystic_light_761_config board_configs[] =
@@ -89,6 +94,11 @@ static const mystic_light_761_config board_configs[] =
{ &(board_names[16]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E EDGE TI WIFI
{ &(board_names[17]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK MAX WIFI
{ &(board_names[18]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-P WIFI (MS-7E71)
{ &(board_names[19]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)
{ &(board_names[20]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850 EDGE TI WIFI (MS-7E62)
{ &(board_names[21]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-VC WIFI6E (MS-7E71)
{ &(board_names[22]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK WIFI (MS-7E53)
{ &(board_names[23]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MEG Z890 UNIFY-X (MS-7E20)
};
enum MSI_ZONE setup_map [] =
@@ -417,7 +417,7 @@ void RGBController_MSIMysticLight761::GetDeviceConfig()
if(zones[i].colors != nullptr)
{
for(unsigned int j = 0; j < zones[i].leds_count; ++j)
for(size_t j = 0; j < GetLEDsInZone((unsigned int)i); ++j)
{
zones[i].colors[j] = color;
}
@@ -252,6 +252,8 @@ REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E59", DetectMSIMysticLightCont
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E80", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E80, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E81", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E81, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E34", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E34, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E32", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E32, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E20", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E20, 0x0001, 0x00);
// Detector for the set of common boards
REGISTER_HID_DETECTOR_PU("MSI Mystic Light Common", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0x0001, 0x00);
REGISTER_HID_DETECTOR_PU("MSI Mystic Light X870", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0xFF00, 0x01);
@@ -28,7 +28,7 @@ std::vector<unsigned int> mountain60_keyboard_key_id_values =
/* CPLK A S D F G H J K L ; " ENTR */
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76,
/* LSFT Z X C V B N M , . / RSFT ARWU DEL */
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 97, 94, 56,
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 97, 99, 56,
/* LCTL LWIN LALT SPC RALT RFNC FNC ARWL ARWD ARWR */
105, 106, 107, 110, 113, 115, 119, 120, 121,
};
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| NollieController.cpp |
| |
| Driver for Nollie |
@@ -82,7 +82,7 @@ void NollieController::SetMos(bool mos)
void NollieController::SetChannelLEDs(unsigned char channel, RGBColor* colors, unsigned int num_colors)
{
if(usb_pid == NOLLIE32_PID || usb_pid == NOLLIE16_PID)
if(usb_pid == NOLLIE32_PID || usb_pid == NOLLIE16_PID || usb_pid == NOLLIE32_OS21_PID || usb_pid == NOLLIE16_OS21_PID)
{
SendPacket(channel,&colors[0], num_colors);
}
@@ -162,9 +162,12 @@ void NollieController::SendPacketFS(unsigned char channel,unsigned char packet_i
packet_interval = 2;
break;
case NOLLIE8_PID:
case NOLLIE8_OS21_PID:
case PRISM8_OS21_PID:
packet_interval = 6;
break;
case NOLLIE1_PID:
case NOLLIE1_OS21_PID:
packet_interval = 30;
break;
default:
@@ -179,7 +182,7 @@ void NollieController::SendPacketFS(unsigned char channel,unsigned char packet_i
usb_buf[0x02 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]);
usb_buf[0x03 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]);
usb_buf[0x04 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]);
if(dev_pid == NOLLIE8_PID || dev_pid == NOLLIE1_PID )
if(dev_pid == NOLLIE8_PID || dev_pid == NOLLIE1_PID || dev_pid == NOLLIE8_OS21_PID || dev_pid == PRISM8_OS21_PID || dev_pid == NOLLIE1_OS21_PID)
{
usb_buf[0x02 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]);
usb_buf[0x03 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]);
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| NollieController.h |
| |
| Driver for Nollie |
@@ -26,18 +26,23 @@
#define NOLLIE32_CHANNELS_NUM 32
#define NOLLIE32_PID 0x4714
#define NOLLIE32_OS21_PID 0x2A32
#define NOLLIE32_VID 0x3061
#define NOLLIE16_CHANNELS_NUM 16
#define NOLLIE16_PID 0x4716
#define NOLLIE16_OS21_PID 0x2A16
#define NOLLIE16_VID 0x3061
#define NOLLIE8_CHANNELS_NUM 8
#define NOLLIE8_PID 0x1F01
#define NOLLIE8_OS21_PID 0x2A08
#define PRISM8_OS21_PID 0x2C08
#define NOLLIE8_VID 0x16D2
#define NOLLIE1_CHANNELS_NUM 1
#define NOLLIE1_PID 0x1F11
#define NOLLIE1_OS21_PID 0x2A01
#define NOLLIE1_VID 0x16D2
#define NOLLIE28_12_CHANNELS_NUM 12
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| NollieControllerDetect.cpp |
| |
| Detector for Nollie |
@@ -16,6 +16,18 @@
void DetectNollieControllers(hid_device_info* info, const std::string& name)
{
if((info->product_id == NOLLIE1_OS21_PID || info->product_id == NOLLIE8_OS21_PID || info->product_id == PRISM8_OS21_PID)
&& info->interface_number != 2)
{
return;
}
if((info->product_id == NOLLIE16_OS21_PID || info->product_id == NOLLIE32_OS21_PID)
&& info->interface_number != 0)
{
return;
}
hid_device* dev = hid_open_path(info->path);
if(dev)
@@ -37,6 +49,11 @@ REGISTER_HID_DETECTOR("Nollie 32CH", DetectNollieControllers, NOLLIE32_VID, NOLL
REGISTER_HID_DETECTOR("Nollie 16CH", DetectNollieControllers, NOLLIE16_VID, NOLLIE16_PID);
REGISTER_HID_DETECTOR("Nollie 8CH", DetectNollieControllers, NOLLIE8_VID, NOLLIE8_PID);
REGISTER_HID_DETECTOR("Nollie 1CH", DetectNollieControllers, NOLLIE1_VID, NOLLIE1_PID);
REGISTER_HID_DETECTOR("Nollie 32_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE32_OS21_PID);
REGISTER_HID_DETECTOR("Nollie 16_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE16_OS21_PID);
REGISTER_HID_DETECTOR("Nollie 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE8_OS21_PID);
REGISTER_HID_DETECTOR("Prism 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, PRISM8_OS21_PID);
REGISTER_HID_DETECTOR("Nollie 1_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE1_OS21_PID);
REGISTER_HID_DETECTOR("Nollie 28 12", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_12_PID);
REGISTER_HID_DETECTOR("Nollie 28 L1", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L1_PID);
REGISTER_HID_DETECTOR("Nollie 28 L2", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L2_PID);
@@ -1,4 +1,4 @@
/*---------------------------------------------------------*\
/*---------------------------------------------------------*\
| RGBController_Nollie.cpp |
| |
| RGBController for Nollie |
@@ -64,11 +64,13 @@ void RGBController_Nollie::SetupZones()
switch(controller->GetUSBPID())
{
case NOLLIE32_PID:
case NOLLIE32_OS21_PID:
channels_num = NOLLIE32_CHANNELS_NUM;
ch_led_num = NOLLIE_HS_CH_LED_NUM;
channel_index = ch32;
break;
case NOLLIE16_PID:
case NOLLIE16_OS21_PID:
channels_num = NOLLIE16_CHANNELS_NUM;
ch_led_num = NOLLIE_HS_CH_LED_NUM;
channel_index = ch16;
@@ -80,10 +82,13 @@ void RGBController_Nollie::SetupZones()
ch_led_num = NOLLIE_12_CH_LED_NUM;
break;
case NOLLIE8_PID:
case NOLLIE8_OS21_PID:
case PRISM8_OS21_PID:
channels_num = NOLLIE8_CHANNELS_NUM;
ch_led_num = NOLLIE_8_CH_LED_NUM;
break;
case NOLLIE1_PID:
case NOLLIE1_OS21_PID:
channels_num = NOLLIE1_CHANNELS_NUM;
ch_led_num = NOLLIE_1_CH_LED_NUM;
break;
@@ -171,7 +176,7 @@ void RGBController_Nollie::ResizeZone(int zone, int new_size)
/*-----------------------------------------------------*\
| Nollie1 needs to report the number of LEDs |
\*-----------------------------------------------------*/
if(controller->GetUSBVID() == NOLLIE1_VID && controller->GetUSBPID() == NOLLIE1_PID)
if(controller->GetUSBPID() == NOLLIE1_PID)
{
controller->InitChLEDs(&new_size,NOLLIE1_CHANNELS_NUM);
}
@@ -192,7 +197,7 @@ void RGBController_Nollie::ResizeZone(int zone, int new_size)
void RGBController_Nollie::DeviceUpdateLEDs()
{
unsigned int DevPid = controller->GetUSBPID();
if(DevPid == NOLLIE32_PID || DevPid == NOLLIE16_PID)
if(DevPid == NOLLIE32_PID || DevPid == NOLLIE16_PID || DevPid == NOLLIE32_OS21_PID || DevPid == NOLLIE16_OS21_PID)
{
std::vector<int> ChSort;
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
@@ -39,6 +39,7 @@ void DetectPNYARGBEpicXGPULargeControllers(i2c_smbus_interface* bus, uint8_t i2c
}
}
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5060Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PNY_SUB_VEN, PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070 ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PNY_SUB_VEN, PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5080 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PNY_SUB_VEN, PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV, 0x60);
@@ -20,6 +20,8 @@ typedef struct
bool has_speed;
} vialrgb_mode;
#define VIALRGB_NUM_MODES (sizeof(vialrgb_modes) / sizeof(vialrgb_mode))
static const vialrgb_mode vialrgb_modes[] =
{
{ VIALRGB_EFFECT_OFF, "Off", false },
@@ -85,9 +87,16 @@ RGBController_QMKVialRGB::RGBController_QMKVialRGB(QMKVialRGBController* control
\*-----------------------------------------------------*/
for(std::size_t effect_idx = 0; effect_idx < controller->GetEffectCount(); effect_idx++)
{
mode new_mode;
new_mode.name = vialrgb_modes[controller->GetEffect(effect_idx)].name;
new_mode.value = vialrgb_modes[controller->GetEffect(effect_idx)].value;
unsigned short mode_index = controller->GetEffect(effect_idx);
mode new_mode;
if(mode_index > VIALRGB_NUM_MODES)
{
continue;
}
new_mode.name = vialrgb_modes[mode_index].name;
new_mode.value = vialrgb_modes[mode_index].value;
if(new_mode.value == VIALRGB_EFFECT_DIRECT)
{
@@ -104,7 +113,7 @@ RGBController_QMKVialRGB::RGBController_QMKVialRGB(QMKVialRGBController* control
new_mode.colors.resize(1);
}
if(vialrgb_modes[controller->GetEffect(effect_idx)].has_speed)
if(vialrgb_modes[mode_index].has_speed)
{
new_mode.flags |= MODE_FLAG_HAS_SPEED;
new_mode.speed_min = 0;
@@ -441,6 +441,8 @@ bool RazerController::SupportsWave()
case RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID:
case RAZER_BLACKWIDOW_V4_75_WIRED_PID:
case RAZER_BLACKWIDOW_V4_X_PID:
case RAZER_BLACKWIDOW_V4_TKL_WIRED_PID:
case RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID:
case RAZER_BLACKWIDOW_X_CHROMA_PID:
case RAZER_BLACKWIDOW_X_CHROMA_TE_PID:
case RAZER_BLADE_2016_PID:
@@ -242,6 +242,8 @@ REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wired)", Det
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID, 0x02, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 75% (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_75_WIRED_PID, 0x03, 0x01, 0x00);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 X", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_X_PID, 0x02, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRED_PID, 0x03, 0x0C, 0x01);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID, 0x02, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_PID, 0x02, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_TE_PID, 0x02, 0x01, 0x02);
REGISTER_HID_DETECTOR_IPU("Razer Cynosa Chroma", DetectRazerControllers, RAZER_VID, RAZER_CYNOSA_CHROMA_PID, 0x02, 0x01, 0x02);
@@ -415,6 +415,29 @@ keyboard_keymap_overlay_values razer_blackwidow_v4_x_layout
}
};
keyboard_keymap_overlay_values razer_blackwidow_v4_tkl_layout
{
KEYBOARD_SIZE::KEYBOARD_SIZE_TKL,
{
{ /* ANSI Value set not used */ },
{
/* Add more regional layout fixes here */
}
},
{
/*-------------------------------------------------------------------------------------------------------------------------------------*\
| Edit Keys |
| Zone, Row, Column, Value, Name, Alternate Name, OpCode |
\*-------------------------------------------------------------------------------------------------------------------------------------*/
{ 0, 0, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move F9 1 right (Shifts row)
{ 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Insert 1 right (Shifts row)
{ 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Del 1 right (Shifts row)
{ 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Up Arrow 1 right (Shifts row)
{ 0, 5, 5, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Right Alt 1 right (Shifts row)
}
};
keyboard_keymap_overlay_values razer_blackwidow_x_chroma_te_layout
{
KEYBOARD_SIZE::KEYBOARD_SIZE_TKL,
@@ -1963,6 +1986,76 @@ static const razer_device blackwidow_v4_x_device =
&razer_blackwidow_v4_x_layout
};
/*-------------------------------------------------------------*\
| Razer Blackwidow V4 TKL (Wired) 1532:02D7 |
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 18 Columns |
\*-------------------------------------------------------------*/
static const razer_zone blackwidow_v4_tkl_wired_zone =
{
ZONE_EN_KEYBOARD,
ZONE_TYPE_MATRIX,
6,
18
};
static const razer_device blackwidow_v4_tkl_wired_device =
{
"Razer Blackwidow V4 TKL (Wired)",
RAZER_BLACKWIDOW_V4_TKL_WIRED_PID,
DEVICE_TYPE_KEYBOARD,
RAZER_MATRIX_TYPE_EXTENDED,
0x1F,
6,
18,
{
&blackwidow_v4_tkl_wired_zone,
NULL,
NULL,
NULL,
NULL,
NULL
},
&razer_blackwidow_v4_tkl_layout
};
/*-------------------------------------------------------------*\
| Razer Blackwidow V4 TKL (Wireless) 1532:02D5 |
| |
| Zone "Keyboard" |
| Matrix |
| 6 Rows, 18 Columns |
\*-------------------------------------------------------------*/
static const razer_zone blackwidow_v4_tkl_wireless_zone =
{
ZONE_EN_KEYBOARD,
ZONE_TYPE_MATRIX,
6,
18
};
static const razer_device blackwidow_v4_tkl_wireless_device =
{
"Razer Blackwidow V4 TKL (Wireless)",
RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID,
DEVICE_TYPE_KEYBOARD,
RAZER_MATRIX_TYPE_EXTENDED,
0x9F,
6,
18,
{
&blackwidow_v4_tkl_wireless_zone,
NULL,
NULL,
NULL,
NULL,
NULL
},
&razer_blackwidow_v4_tkl_layout
};
/*-------------------------------------------------------------*\
| Razer Blackwidow X Chroma 1532:0216 |
| |
@@ -9234,6 +9327,8 @@ const razer_device* razer_device_list[] =
&blackwidow_v4_pro_75_wireless_device,
&blackwidow_v4_75_wired_device,
&blackwidow_v4_x_device,
&blackwidow_v4_tkl_wired_device,
&blackwidow_v4_tkl_wireless_device,
&blackwidow_x_chroma_device,
&blackwidow_x_chroma_te_device,
&cynosa_chroma_device,
@@ -57,6 +57,8 @@
#define RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID 0x02B3
#define RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID 0x02B4
#define RAZER_BLACKWIDOW_V4_75_WIRED_PID 0x02A5
#define RAZER_BLACKWIDOW_V4_TKL_WIRED_PID 0x02D7
#define RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID 0x02D5
#define RAZER_BLACKWIDOW_X_CHROMA_PID 0x0216
#define RAZER_BLACKWIDOW_X_CHROMA_TE_PID 0x021A
#define RAZER_BLADE_2016_PID 0x020F
@@ -0,0 +1,303 @@
/*---------------------------------------------------------*\
| RGBController_RealtekBridge.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_RealtekBridge.h"
/**------------------------------------------------------------------*\
@name Realtek Bridge Device
@category Storage
@type USB
@save :x:
@direct :rotating_light:
@effects :white_check_mark:
@detectors RealtekBridgeControllerDetect
@comment
\*-------------------------------------------------------------------*/
RGBController_RealtekBridge::RGBController_RealtekBridge(RealtekBridgeController* controller_ptr)
{
controller = controller_ptr;
name = controller_ptr->get_product_name();
vendor = controller_ptr->get_manu_name();
location = controller_ptr->get_dev_loc();
serial = controller_ptr->get_sn();
version = controller_ptr->get_fw_ver();
description = vendor + "Storage Device";
type = DEVICE_TYPE_STORAGE;
SetupModes();
SetupZones();
}
RGBController_RealtekBridge::~RGBController_RealtekBridge()
{
delete controller;
}
void RGBController_RealtekBridge::SetupModes()
{
int brightness = controller->get_argb_brightness() >> 8;
mode Direct;
Direct.name = "Direct";
Direct.value = REALTEK_BRIDGE_LED_EFF_NONE;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.brightness_min = 0;
Direct.brightness_max = 255;
Direct.brightness = brightness;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = REALTEK_BRIDGE_LED_EFF_ALWAYS;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.colors_min = 1;
Static.colors_max = 1;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors.resize(1);
Static.brightness_min = 0;
Static.brightness_max = 255;
Static.brightness = brightness;
modes.push_back(Static);
mode Blink;
Blink.name = "Blink";
Blink.value = REALTEK_BRIDGE_LED_EFF_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Blink.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Blink.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Blink.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Blink.colors_min = 1;
Blink.colors_max = 1;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
Blink.colors.resize(1);
Blink.brightness_min = 0;
Blink.brightness_max = 255;
Blink.brightness = brightness;
modes.push_back(Blink);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = REALTEK_BRIDGE_LED_EFF_BREATHE;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Breathing.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Breathing.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.colors.resize(1);
Breathing.brightness_min = 0;
Breathing.brightness_max = 255;
Breathing.brightness = brightness;
modes.push_back(Breathing);
mode Spectrum;
Spectrum.name = "Spectrum";
Spectrum.value = REALTEK_BRIDGE_LED_EFF_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Spectrum.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Spectrum.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Spectrum.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Spectrum.color_mode = MODE_COLORS_NONE;
Spectrum.brightness_min = 0;
Spectrum.brightness_max = 255;
Spectrum.brightness = brightness;
modes.push_back(Spectrum);
mode Scroll;
Scroll.name = "Scroll";
Scroll.value = REALTEK_BRIDGE_LED_EFF_SCROLL;
Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Scroll.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Scroll.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Scroll.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Scroll.colors_min = 1;
Scroll.colors_max = 1;
Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC;
Scroll.colors.resize(1);
Scroll.brightness_min = 0;
Scroll.brightness_max = 255;
Scroll.brightness = brightness;
modes.push_back(Scroll);
mode RainbowScroll;
RainbowScroll.name = "Rainbow Scroll";
RainbowScroll.value = REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL;
RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowScroll.speed_min = REALTEK_BRIDGE_SPEED_MIN;
RainbowScroll.speed_max = REALTEK_BRIDGE_SPEED_MAX;
RainbowScroll.speed = REALTEK_BRIDGE_SPEED_NORMAL;
RainbowScroll.color_mode = MODE_COLORS_NONE;
RainbowScroll.brightness_min = 0;
RainbowScroll.brightness_max = 255;
RainbowScroll.brightness = brightness;
modes.push_back(RainbowScroll);
mode RunningWater;
RunningWater.name = "Running Water";
RunningWater.value = REALTEK_BRIDGE_LED_EFF_RUNNING_WATER;
RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RunningWater.speed_min = REALTEK_BRIDGE_SPEED_MIN;
RunningWater.speed_max = REALTEK_BRIDGE_SPEED_MAX;
RunningWater.speed = REALTEK_BRIDGE_SPEED_NORMAL;
RunningWater.colors_min = 1;
RunningWater.colors_max = 1;
RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC;
RunningWater.colors.resize(1);
RunningWater.brightness_min = 0;
RunningWater.brightness_max = 255;
RunningWater.brightness = brightness;
modes.push_back(RunningWater);
mode Sliding;
Sliding.name = "Sliding";
Sliding.value = REALTEK_BRIDGE_LED_EFF_SLIDING;
Sliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Sliding.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Sliding.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Sliding.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Sliding.color_mode = MODE_COLORS_NONE;
Sliding.brightness_min = 0;
Sliding.brightness_max = 255;
Sliding.brightness = brightness;
modes.push_back(Sliding);
mode NewtonCradle;
NewtonCradle.name = "Newton Cradle";
NewtonCradle.value = REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE;
NewtonCradle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
NewtonCradle.speed_min = REALTEK_BRIDGE_SPEED_MIN;
NewtonCradle.speed_max = REALTEK_BRIDGE_SPEED_MAX;
NewtonCradle.speed = REALTEK_BRIDGE_SPEED_NORMAL;
NewtonCradle.color_mode = MODE_COLORS_NONE;
NewtonCradle.brightness_min = 0;
NewtonCradle.brightness_max = 255;
NewtonCradle.brightness = brightness;
modes.push_back(NewtonCradle);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = REALTEK_BRIDGE_LED_EFF_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.speed_min = REALTEK_BRIDGE_SPEED_MIN;
Meteor.speed_max = REALTEK_BRIDGE_SPEED_MAX;
Meteor.speed = REALTEK_BRIDGE_SPEED_NORMAL;
Meteor.colors_min = 1;
Meteor.colors_max = 1;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Meteor.colors.resize(1);
Meteor.brightness_min = 0;
Meteor.brightness_max = 255;
Meteor.brightness = brightness;
modes.push_back(Meteor);
}
void RGBController_RealtekBridge::SetupZones()
{
zone argb_zone;
argb_zone.name = "strip";
argb_zone.type = ZONE_TYPE_LINEAR;
argb_zone.leds_min = controller->get_argb_num();
argb_zone.leds_max = controller->get_argb_num();
argb_zone.leds_count = controller->get_argb_num();
argb_zone.matrix_map = NULL;
zones.push_back(argb_zone);
for(unsigned int led_idx = 0; led_idx < argb_zone.leds_count; led_idx++)
{
led StripLED;
StripLED.name = "led ";
StripLED.name.append(std::to_string(led_idx + 1));
leds.push_back(StripLED);
}
SetupColors();
}
void RGBController_RealtekBridge::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_RealtekBridge::DeviceUpdateLEDs()
{
UpdateZoneLEDs(0);
}
void RGBController_RealtekBridge::UpdateZoneLEDs(int /*zone*/)
{
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
if(curr_mode.color_mode == MODE_COLORS_PER_LED &&
curr_mode.value == REALTEK_BRIDGE_LED_EFF_NONE) //direct mode
{
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
controller->set_argb_direct(colors, brightness);
}
else
{
UpdateSingleLED(0);
}
}
void RGBController_RealtekBridge::UpdateSingleLED(int /*led*/)
{
unsigned char speed = REALTEK_BRIDGE_SPEED_NORMAL;
unsigned char dir = 0;
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> rtk_colors = curr_mode.colors;
if(curr_mode.flags & MODE_FLAG_HAS_SPEED)
{
speed = curr_mode.speed;
}
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR)
{
if(curr_mode.direction == MODE_DIRECTION_RIGHT)
{
dir = 1;
}
}
if(curr_mode.color_mode == MODE_COLORS_PER_LED)
{
rtk_colors = colors;
}
else if(curr_mode.color_mode == MODE_COLORS_NONE)
{
rtk_colors.clear();
}
controller->set_argb_effect(curr_mode.value, rtk_colors, speed, brightness);
}
void RGBController_RealtekBridge::DeviceUpdateMode()
{
if(modes[active_mode].value != REALTEK_BRIDGE_LED_EFF_NONE)
{
DeviceUpdateLEDs();
}
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_RealtekBridge.h |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "RealtekBridgeController.h"
class RGBController_RealtekBridge : public RGBController
{
public:
RGBController_RealtekBridge(RealtekBridgeController* controller_ptr);
~RGBController_RealtekBridge();
void SetupModes();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
RealtekBridgeController* controller;
};
@@ -0,0 +1,802 @@
/*---------------------------------------------------------*\
| RealtekBridgeController.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RealtekBridgeController.h"
#include "hsv.h"
#include <StringUtils.h>
#include <MathUtils.h>
static const unsigned char hid_set_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_get_packet[] =
{
0x56, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0xE2,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char hid_end_packet[] =
{
0x58, 0x53, 0x42, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
RealtekBridgeController::RealtekBridgeController(hid_device* dev, hid_device_info* info)
{
hdev = dev;
hidinfo = info;
brctl_data = (unsigned char*)calloc(REALTEK_BRIDGE_CTL_DATA_SIZE, 1);
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
set_write_unlock();
get_brctl_hdr();
get_brctl_data();
set_appctl(true);
}
RealtekBridgeController::~RealtekBridgeController()
{
if(hdev)
{
set_appctl(false);
hid_close(hdev);
hdev = NULL;
}
memset(brctl_hdr, 0, REALTEK_BRIDGE_CTL_HDR_SIZE);
free(brctl_data);
}
int RealtekBridgeController::usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in)
{
int id;
int ret = 0;
int buf_len = usb_hid_get_report(data_len, &id);
if(!is_in && data_len)
{
usb_buf[0x04] = data[0];
}
memcpy(&usb_buf[0x08], &data_len, sizeof(data_len));
memcpy(&usb_buf[0x1B], &data_len, sizeof(data_len));
ret = hid_send_feature_report(hdev, usb_buf, sizeof(hid_get_packet));
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, data, data_len);
usb_buf[0x00] = id;
if(is_in)
{
ret = hid_get_feature_report(hdev, usb_buf, buf_len);
memcpy(data, usb_buf + offset, data_len);
}
else
{
ret = hid_send_feature_report(hdev, usb_buf, buf_len);
}
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_end_packet, sizeof(hid_end_packet));
ret = hid_get_feature_report(hdev, usb_buf, sizeof(hid_end_packet));
free(usb_buf);
return (ret > 0) ? 0 : ret;
}
int RealtekBridgeController::usb_hid_get_report(int data_len, int* id)
{
int retlen = 0;
if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH2)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH2;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH2;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH3)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH3;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH3;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH4)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH4;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH4;
}
else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH5)
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH5;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH5;
}
else
{
*id = REALTEK_BRIDGE_HID_ID_DATA_CH1;
retlen = REALTEK_BRIDGE_HID_DATALEN_CH1;
}
return retlen;
}
int RealtekBridgeController::set_write_unlock()
{
int ret = 0;
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int data_len = 96;
unsigned int addr = 0xAC004000;
unsigned char* data = (unsigned char*)calloc(data_len, 1);
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0x92;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, data, data_len, 0, true);
free(data);
return ret;
}
int RealtekBridgeController::get_brctl_hdr()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, true);
}
int RealtekBridgeController::set_brctl_hdr()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, false);
}
int RealtekBridgeController::get_brctl_data()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int offset = REALTEK_BRIDGE_CTL_HDR_SIZE;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, true);
}
int RealtekBridgeController::set_brctl_data()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int offset = REALTEK_BRIDGE_CTL_HDR_SIZE;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x02;
memcpy(&usb_buf[0x17], &offset, sizeof(offset));
return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, false);
}
unsigned char RealtekBridgeController::get_support_openrgb()
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char is_support = 0;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xCC;
usb_buf[0x14] = 0x04;
usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB;
if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true))
{
is_support = 0;
}
return is_support;
}
std::string RealtekBridgeController::get_manu_name()
{
return StringUtils::wchar_to_char(hidinfo->manufacturer_string);
}
std::string RealtekBridgeController::get_product_name()
{
return StringUtils::wchar_to_char(hidinfo->product_string);
}
std::string RealtekBridgeController::get_sn()
{
return StringUtils::wchar_to_char(hidinfo->serial_number);
}
std::string RealtekBridgeController::get_dev_loc()
{
return hidinfo->path;
}
std::string RealtekBridgeController::get_fw_ver()
{
struct RealtekBridgeControllerFWVersion fw_ver;
std::string ver = "";
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet));
usb_buf[0x13] = 0xA5;
if(!usb_hid_ioctl(usb_buf, (unsigned char*)&fw_ver, sizeof(fw_ver), 0, true))
{
ver += std::to_string(fw_ver.fw_major_ver) + "." +
std::to_string(fw_ver.fw_minor_ver) + "." +
std::to_string(fw_ver.fw_extra_ver) + "." +
std::to_string(fw_ver.fw_build_date);
}
return ver;
}
int RealtekBridgeController::get_argb_num()
{
int num_rgb = 0;
memcpy(&num_rgb, &brctl_hdr[24], sizeof(num_rgb));
return num_rgb;
}
int RealtekBridgeController::get_argb_brightness()
{
int bright = 0;
memcpy(&bright, &brctl_hdr[28], 2);
return bright;
}
int RealtekBridgeController::set_argb_brightness(unsigned short bright)
{
memcpy(&brctl_hdr[28], &bright, sizeof(bright));
set_brctl_hdr();
return 0;
}
int RealtekBridgeController::set_appctl(unsigned char ctl_sts)
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x01;
usb_buf[0x15] = ctl_sts;
return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false);
}
int RealtekBridgeController::set_direct(unsigned char* color, int color_num)
{
int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1;
int data_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x4C;
usb_buf[0x14] = 0x03;
usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB;
memcpy(&usb_buf[0x17], &color_num, sizeof(color_num));
return usb_hid_ioctl(usb_buf, color, data_len, 0, false);
}
int RealtekBridgeController::eff_set_always_on(RGBColor rgb)
{
int i;
int j;
int row = 1;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 1;
brctl_data[i * single_size + 1] = row;
for(j = 0; j < row; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_blink(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 1;
for(j = 0; j < row; j++)
{
if((j & 0x1) == 0)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_breathe(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 2;
for(j = 0; j < row; j++)
{
if((j & 0x1) == 0)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_spectrum(int cycle)
{
int i;
int j;
int row = 6;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
static const RGBColor rainbow_table[6] = {0x0000FF, 0x0050FF, 0x0080FF, 0x00FF00, 0xFF0000, 0xFF0080};
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 3;
for(j = 0; j < row; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rainbow_table[j]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rainbow_table[j]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rainbow_table[j]);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_scroll(RGBColor rgb, int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2 - 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 4;
if(start_idx == -1)
{
start_idx = i;
}
j = i - start_idx;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
if(i != start_idx)
{
j = row - (i - start_idx);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_rainbow_scroll(int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2 - 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0};
hsv_t hsv_color;
hsv_color.saturation = 255;
hsv_color.value = 255;
for(i = 0; i < get_argb_num(); i++)
{
hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num());
slide_table[i] = hsv2rgb(&hsv_color);
}
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x85;
if(start_idx == -1)
{
start_idx = i;
}
j = i - start_idx;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]);
if(i != start_idx)
{
j = row - (i - start_idx);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_running_water(RGBColor rgb, int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num() * 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 5;
if(start_idx == -1)
{
start_idx = i;
}
for(j = i - start_idx; j < row / 2; j++)
{
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_sliding(int cycle)
{
int i;
int j;
int start_idx = -1;
int row = get_argb_num();
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0};
RGBColor rgb = 0;
hsv_t hsv_color;
hsv_color.saturation = 255;
hsv_color.value = 255;
for(i = 0; i < get_argb_num(); i++)
{
hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num());
slide_table[i] = hsv2rgb(&hsv_color);
}
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x83;
if(start_idx == -1)
{
start_idx = i;
}
for(j = 0; j < row; j++)
{
rgb = slide_table[(i - start_idx + j) % row];
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_newton_cradle(int cycle)
{
int i;
int j;
int start_idx = -1;
int row;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
RGBColor rgb = 0;
static const int const_ball_num = 3;
static const RGBColor ball_color[] = {0x0080FF, 0x0000FF, 0x00FF00, 0xFF0000};
static const int mid = get_argb_num() >> 1;
if(get_argb_num() <= const_ball_num)
{
return 0;
}
row = (get_argb_num() - const_ball_num) * 2;
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 2;
brctl_data[i * single_size + 1] = row;
memcpy(&brctl_data[i * single_size + 2], &cycle, 2);
brctl_data[i * single_size + 8] = 0x84;
if(start_idx == -1)
{
start_idx = i;
}
for(j = 0; j < row; j++)
{
rgb = 0;
if(i - start_idx > mid - 2 &&
i - start_idx < mid + 2)
{
if(j < mid - 1 || j > row - mid)
{
if(i - start_idx == mid - 1)
{
rgb = ball_color[1];
}
else if(i - start_idx == mid)
{
rgb = ball_color[2];
}
else
{
rgb = ball_color[3];
}
}
else
{
if(i - start_idx == mid - 1)
{
rgb = ball_color[0];
}
else if(i - start_idx == mid)
{
rgb = ball_color[1];
}
else
{
rgb = ball_color[2];
}
}
}
else if(i - start_idx < mid)
{
if(i - start_idx == j || i - start_idx + j == row - 1)
{
rgb = ball_color[0];
}
}
else if(i - start_idx > mid + 1)
{
if(i - start_idx - j == const_ball_num ||
i - start_idx + j == row - 1 + const_ball_num)
{
rgb = ball_color[3];
}
}
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
}
return set_brctl_data();
}
int RealtekBridgeController::eff_set_meteor(RGBColor rgb, int cycle)
{
int i;
int j;
int row = 2;
int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW;
int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32;
int stable_ms = cycle / row;
int latency = stable_ms / get_argb_num();
memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE);
for(i = 0; i < get_argb_num(); i++)
{
brctl_data[i * single_size + 0] = 4;
brctl_data[i * single_size + 1] = row;
brctl_data[i * single_size + 6] = stable_ms / 100;
brctl_data[i * single_size + 7] = stable_ms / 100;
brctl_data[i * single_size + 8] = 6;
memcpy(&brctl_data[i * single_size + 9], &latency, 2);
j = 0;
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb);
brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb);
}
return set_brctl_data();
}
int RealtekBridgeController::set_argb_direct(std::vector<RGBColor> color_buf, unsigned short brightness)
{
int ret = -1;
size_t color_num = color_buf.size();
size_t buf_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH;
static unsigned short prev_bright = 0xFFFF;
unsigned char* buf;
if(color_num <= 0)
{
goto exit;
}
ret = set_appctl(true);
if(ret)
{
goto exit;
}
if(prev_bright != brightness)
{
prev_bright = brightness;
ret = set_argb_brightness(brightness << 8);
if(ret)
{
goto exit;
}
}
buf = (unsigned char*)malloc(buf_len);
memset(buf, 0, buf_len);
for(int i = 0; i < (int)color_num; i++)
{
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]);
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]);
buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]);
}
ret = set_direct(buf, (int)color_num);
free(buf);
if(ret)
{
goto exit;
}
exit:
return ret;
}
int RealtekBridgeController::set_argb_effect(unsigned char mode, std::vector<RGBColor> color_buf, int speed, unsigned short brightness)
{
int ret = -1;
int cycle = MathUtils::IntInterpolate(REALTEK_BRIDGE_CYCLE_MAX, REALTEK_BRIDGE_CYCLE_MIN, 0, REALTEK_BRIDGE_SPEED_MAX, speed);
RGBColor rgb = 0;
static unsigned short prev_bright = 0xFFFF;
if(color_buf.size() >= 1)
{
rgb = color_buf[0];
}
if(prev_bright != brightness)
{
prev_bright = brightness;
ret = set_argb_brightness(brightness << 8);
if(ret)
{
goto exit;
}
}
switch(mode)
{
case REALTEK_BRIDGE_LED_EFF_ALWAYS:
ret = eff_set_always_on(rgb);
break;
case REALTEK_BRIDGE_LED_EFF_BLINK:
ret = eff_set_blink(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_BREATHE:
ret = eff_set_breathe(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SPECTRUM:
ret = eff_set_spectrum(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SCROLL:
ret = eff_set_scroll(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL:
ret = eff_set_rainbow_scroll(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_RUNNING_WATER:
ret = eff_set_running_water(rgb, cycle);
break;
case REALTEK_BRIDGE_LED_EFF_SLIDING:
ret = eff_set_sliding(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE:
ret = eff_set_newton_cradle(cycle);
break;
case REALTEK_BRIDGE_LED_EFF_METEOR:
ret = eff_set_meteor(rgb, cycle);
break;
default:
break;
}
if(!ret)
{
ret = set_appctl(false);
}
exit:
return ret;
}
@@ -0,0 +1,126 @@
/*---------------------------------------------------------*\
| RealtekBridgeController.h |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <cmath>
#include <cstring>
#include <vector>
#include <hidapi.h>
#include "RGBController.h"
#define REALTEK_BRIDGE_SYNC_METHOD_OPENRGB 6
#define REALTEK_BRIDGE_COLOR_DEPTH 3
#define REALTEK_BRIDGE_CTL_HDR_SIZE 64
#define REALTEK_BRIDGE_CTL_DATA_SIZE 3220
#define REALTEK_BRIDGE_MAX_ARGB_NUM 20
#define REALTEK_BRIDGE_MAX_APPCTL_ROW 43
#define REALTEK_BRIDGE_HID_DATALEN_CH1 4096
#define REALTEK_BRIDGE_HID_DATALEN_CH2 64
#define REALTEK_BRIDGE_HID_DATALEN_CH3 512
#define REALTEK_BRIDGE_HID_DATALEN_CH4 1200
#define REALTEK_BRIDGE_HID_DATALEN_CH5 2048
#define REALTEK_BRIDGE_HID_ID_DATA_CH1 0x57
#define REALTEK_BRIDGE_HID_ID_DATA_CH2 0x59
#define REALTEK_BRIDGE_HID_ID_DATA_CH3 0x5A
#define REALTEK_BRIDGE_HID_ID_DATA_CH4 0x5B
#define REALTEK_BRIDGE_HID_ID_DATA_CH5 0x5C
enum REALTEK_BRIDGE_LED_EFF
{
REALTEK_BRIDGE_LED_EFF_NONE,
REALTEK_BRIDGE_LED_EFF_ALWAYS,
REALTEK_BRIDGE_LED_EFF_BLINK,
REALTEK_BRIDGE_LED_EFF_BREATHE,
REALTEK_BRIDGE_LED_EFF_SPECTRUM,
REALTEK_BRIDGE_LED_EFF_SCROLL,
REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL,
REALTEK_BRIDGE_LED_EFF_RUNNING_WATER,
REALTEK_BRIDGE_LED_EFF_SLIDING,
REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE,
REALTEK_BRIDGE_LED_EFF_METEOR,
REALTEK_BRIDGE_NUMBER_OF_LED_EFF_MODE,
};
enum REALTEK_BRIDGE_SPEED
{
REALTEK_BRIDGE_SPEED_MIN = 1,
REALTEK_BRIDGE_SPEED_NORMAL = 50,
REALTEK_BRIDGE_SPEED_MAX = 100,
};
enum REALTEK_BRIDGE_CYCLE_MS
{
REALTEK_BRIDGE_CYCLE_MIN = 200,
REALTEK_BRIDGE_CYCLE_NORMAL = 2000,
REALTEK_BRIDGE_CYCLE_MAX = 10000,
};
struct RealtekBridgeControllerFWVersion
{
unsigned int fw_major_ver;
unsigned int fw_minor_ver;
unsigned int fw_extra_ver;
unsigned int fw_build_ver;
unsigned int fw_build_date;
};
class RealtekBridgeController
{
public:
RealtekBridgeController(hid_device* dev, hid_device_info* info);
~RealtekBridgeController();
unsigned char get_support_openrgb();
std::string get_manu_name();
std::string get_product_name();
std::string get_sn();
std::string get_dev_loc();
std::string get_fw_ver();
int get_argb_num();
int get_argb_brightness();
int set_argb_brightness(unsigned short bright);
int set_argb_direct(std::vector<RGBColor> color_buf, unsigned short brightness);
int set_argb_effect(unsigned char mode, std::vector<RGBColor> color_buf, int speed, unsigned short brightness);
private:
hid_device* hdev;
hid_device_info* hidinfo = NULL;
unsigned char brctl_hdr[REALTEK_BRIDGE_CTL_HDR_SIZE] = {0};
unsigned char* brctl_data = NULL;
int usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len,
unsigned int offset, unsigned char is_in);
int usb_hid_get_report(int data_len, int* id);
int set_write_unlock();
int get_brctl_hdr();
int set_brctl_hdr();
int get_brctl_data();
int set_brctl_data();
int set_appctl(unsigned char ctl_sts);
int set_direct(unsigned char* color, int color_num);
int eff_set_always_on(RGBColor rgb);
int eff_set_blink(RGBColor rgb, int cycle);
int eff_set_breathe(RGBColor rgb, int cycle);
int eff_set_spectrum(int cycle);
int eff_set_scroll(RGBColor rgb, int cycle);
int eff_set_rainbow_scroll(int cycle);
int eff_set_running_water(RGBColor rgb, int cycle);
int eff_set_sliding(int cycle);
int eff_set_newton_cradle(int cycle);
int eff_set_meteor(RGBColor rgb, int cycle);
};
@@ -0,0 +1,61 @@
/*---------------------------------------------------------*\
| RealtekBridgeControllerDetect.cpp |
| |
| Controller for Realtek USB to SSD Bridge ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "RGBController_RealtekBridge.h"
#define REALTEK_BRIDGE_VID 0x0BDA
#define REALTEK_BRIDGE_PID0 0x9220
#define REALTEK_BRIDGE_PID1 0x9201
#define REALTEK_BRIDGE_PID2 0x9210
#define REALTEK_HID2SCSI_PG 0xFF00
#define REALTEK_HID2SCSI_USAGE 0x0001
/******************************************************************************************\
* *
* DetectRealtekBridgeControllers *
* *
* Tests the USB address to see if an Realtek Bridge controller exists there *
* *
\******************************************************************************************/
void DetectRealtekBridgeControllers(hid_device_info* info, const std::string& /*name*/)
{
RealtekBridgeController* controller = NULL;
RGBController_RealtekBridge* rgb_controller = NULL;
hid_device* dev = hid_open_path(info->path);
if(dev)
{
controller = new RealtekBridgeController(dev, info);
if(controller->get_support_openrgb())
{
rgb_controller = new RGBController_RealtekBridge(controller);
if(rgb_controller->type != DEVICE_TYPE_UNKNOWN)
{
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
else
{
delete rgb_controller;
}
}
else
{
delete controller;
}
}
return;
}
REGISTER_HID_DETECTOR_PU("RTL9220", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID0, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
REGISTER_HID_DETECTOR_PU("RTL9201", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID1, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
REGISTER_HID_DETECTOR_PU("RTL9210", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID2, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE);
@@ -123,6 +123,7 @@ void DetectRoccatVulcanKeyboardControllers(hid_device_info* info, const std::str
case ROCCAT_MAGMA_PID:
case ROCCAT_MAGMA_MINI_PID:
case ROCCAT_VULCAN_II_PID:
case ROCCAT_VULCAN_II_MAX_PID:
case TURTLE_BEACH_VULCAN_II_PID:
dev_led_page = 0xFF00;
dev_ctrl_page = 0xFF01;
@@ -337,6 +338,7 @@ REGISTER_HID_DETECTOR_IP ("Roccat Vulcan TKL", DetectRoccatVulc
REGISTER_HID_DETECTOR_IP ("Roccat Vulcan Pro", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_PRO_PID, 1, 0xFF01);
REGISTER_HID_DETECTOR_IP ("Roccat Vulcan TKL Pro", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_TKL_PRO_PID, 1, 0xFF01);
REGISTER_HID_DETECTOR_IP ("Roccat Vulcan II", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_II_PID, 1, 0xFF01);
REGISTER_HID_DETECTOR_IP ("Roccat Vulcan II Max", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_II_MAX_PID, 1, 0xFF01);
REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_PID, 1, 0xFF01);
REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II TKL", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_TKL_PID, 1, 11);
REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II TKL Pro", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_TKL_PRO_PID, 1, 0x0001);
@@ -127,6 +127,9 @@ void RGBController_RoccatVulcanKeyboard::SetupZones()
case TURTLE_BEACH_VULCAN_II_PID:
keyboard_ptr = &RoccatVulcanIILayouts;
break;
case ROCCAT_VULCAN_II_MAX_PID:
keyboard_ptr = &RoccatVulcanIIMaxLayouts;
break;
case ROCCAT_MAGMA_PID:
case ROCCAT_MAGMA_MINI_PID:
keyboard_ptr = &RoccatMagmaLayouts;
@@ -152,18 +155,97 @@ void RGBController_RoccatVulcanKeyboard::SetupZones()
}
/*---------------------------------------------------------*\
| Determine zone sizes |
| Vulcan II MAX has 108 keyboard keys + 24 secondary LEDs |
| (physically under parent keys) + 16 palm rest LEDs. |
| Secondary LEDs are split into labeled linear zones. |
\*---------------------------------------------------------*/
int keyboard_size = keyboard[layout].size;
if(pid == ROCCAT_VULCAN_II_MAX_PID)
{
keyboard_size = 108;
}
zone keyboard_zone;
keyboard_zone.name = "Keyboard";
keyboard_zone.type = ZONE_TYPE_MATRIX;
keyboard_zone.leds_min = keyboard[layout].size;
keyboard_zone.leds_max = keyboard[layout].size;
keyboard_zone.leds_count = keyboard[layout].size;
keyboard_zone.leds_min = keyboard_size;
keyboard_zone.leds_max = keyboard_size;
keyboard_zone.leds_count = keyboard_size;
keyboard_zone.matrix_map = new matrix_map_type;
keyboard_zone.matrix_map->height = keyboard[layout].rows;
keyboard_zone.matrix_map->width = keyboard[layout].cols;
keyboard_zone.matrix_map->map = keyboard[layout].matrix_map;
zones.push_back(keyboard_zone);
if(pid == ROCCAT_VULCAN_II_MAX_PID)
{
zone fkey_ind_zone;
fkey_ind_zone.name = "F-Key Indicators";
fkey_ind_zone.type = ZONE_TYPE_LINEAR;
fkey_ind_zone.leds_min = 15;
fkey_ind_zone.leds_max = 15;
fkey_ind_zone.leds_count = 15;
fkey_ind_zone.matrix_map = NULL;
zones.push_back(fkey_ind_zone);
zone nav1_zone;
nav1_zone.name = "Nav Cluster Indicators";
nav1_zone.type = ZONE_TYPE_LINEAR;
nav1_zone.leds_min = 3;
nav1_zone.leds_max = 3;
nav1_zone.leds_count = 3;
nav1_zone.matrix_map = NULL;
zones.push_back(nav1_zone);
zone numlock_zone;
numlock_zone.name = "Num Lock Indicator";
numlock_zone.type = ZONE_TYPE_LINEAR;
numlock_zone.leds_min = 1;
numlock_zone.leds_max = 1;
numlock_zone.leds_count = 1;
numlock_zone.matrix_map = NULL;
zones.push_back(numlock_zone);
zone nav2_zone;
nav2_zone.name = "Nav Cluster Indicators 2";
nav2_zone.type = ZONE_TYPE_LINEAR;
nav2_zone.leds_min = 3;
nav2_zone.leds_max = 3;
nav2_zone.leds_count = 3;
nav2_zone.matrix_map = NULL;
zones.push_back(nav2_zone);
zone caps_zone;
caps_zone.name = "Caps Lock Indicator";
caps_zone.type = ZONE_TYPE_LINEAR;
caps_zone.leds_min = 1;
caps_zone.leds_max = 1;
caps_zone.leds_count = 1;
caps_zone.matrix_map = NULL;
zones.push_back(caps_zone);
zone win_zone;
win_zone.name = "Win Key Indicator";
win_zone.type = ZONE_TYPE_LINEAR;
win_zone.leds_min = 1;
win_zone.leds_max = 1;
win_zone.leds_count = 1;
win_zone.matrix_map = NULL;
zones.push_back(win_zone);
zone palmrest_zone;
palmrest_zone.name = "Palm Rest";
palmrest_zone.type = ZONE_TYPE_LINEAR;
palmrest_zone.leds_min = 16;
palmrest_zone.leds_max = 16;
palmrest_zone.leds_count = 16;
palmrest_zone.matrix_map = NULL;
zones.push_back(palmrest_zone);
}
for(int led_id = 0; led_id < keyboard[layout].size; led_id++)
{
led new_led;
@@ -69,6 +69,7 @@ device_info RoccatVulcanKeyboardController::InitDeviceInfo()
case ROCCAT_VULCAN_PRO_PID:
case ROCCAT_VULCAN_TKL_PRO_PID:
case ROCCAT_VULCAN_II_PID:
case ROCCAT_VULCAN_II_MAX_PID:
case TURTLE_BEACH_VULCAN_II_PID:
case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID:
packet_length = 9;
@@ -85,9 +86,14 @@ device_info RoccatVulcanKeyboardController::InitDeviceInfo()
buf[0] = report_id;
hid_get_feature_report(dev_ctrl, buf, packet_length);
char version[5];
snprintf(version, 5, "%d.%02d", buf[2] / 100, buf[2] % 100);
/*-------------------------------------------------------------*\
| buf[2] is version e.g. 103 means v1.03 |
\*-------------------------------------------------------------*/
dev_info.version_major = buf[2] / 100;
dev_info.version_minor = buf[2] % 100;
char version[5];
snprintf(version, 5, "%d.%02d", dev_info.version_major, dev_info.version_minor);
dev_info.version = version;
if(device_pid == ROCCAT_MAGMA_PID || device_pid == ROCCAT_MAGMA_MINI_PID)
@@ -115,6 +121,11 @@ device_info RoccatVulcanKeyboardController::GetDeviceInfo()
return dev_info;
}
bool RoccatVulcanKeyboardController::IsBigEndianDirectMode()
{
return (dev_info.version_major >= 1 && dev_info.version_minor >= 16);
}
void RoccatVulcanKeyboardController::EnableDirect(bool on_off_switch)
{
uint8_t* buf;
@@ -126,6 +137,7 @@ void RoccatVulcanKeyboardController::EnableDirect(bool on_off_switch)
case ROCCAT_VULCAN_PRO_PID:
case ROCCAT_VULCAN_TKL_PRO_PID:
case ROCCAT_VULCAN_II_PID:
case ROCCAT_VULCAN_II_MAX_PID:
case TURTLE_BEACH_VULCAN_II_PID:
case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID:
buf = new uint8_t[5] { 0x0E, 0x05, on_off_switch, 0x00, 0x00 };
@@ -169,6 +181,11 @@ void RoccatVulcanKeyboardController::SendColors(std::vector<led_color> colors)
column_length = 1;
protocol_version = 2;
break;
case ROCCAT_VULCAN_II_MAX_PID:
packet_length = 567;
column_length = 1;
protocol_version = 2;
break;
case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID:
packet_length = 320;
column_length = 1;
@@ -211,8 +228,16 @@ void RoccatVulcanKeyboardController::SendColors(std::vector<led_color> colors)
}
else
{
bufs[0][3] = packet_length & 0xFF;
bufs[0][4] = (packet_length >> 8) & 0xFF;
if(IsBigEndianDirectMode())
{
bufs[0][3] = (packet_length >> 8) & 0xFF;
bufs[0][4] = packet_length & 0xFF;
}
else
{
bufs[0][3] = packet_length & 0xFF;
bufs[0][4] = (packet_length >> 8) & 0xFF;
}
}
unsigned int data_length_packet = 64 - header_length_first;
@@ -342,6 +367,11 @@ void RoccatVulcanKeyboardController::SendMode(unsigned int mode, unsigned int sp
packet_length = 377;
column_length = 1;
break;
case ROCCAT_VULCAN_II_MAX_PID:
protocol_version = 2;
packet_length = 542;
column_length = 1;
break;
case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID:
packet_length = 284;
column_length = 1;
@@ -26,6 +26,7 @@
#define ROCCAT_VULCAN_PRO_PID 0x30F7
#define ROCCAT_VULCAN_TKL_PRO_PID 0x311A
#define ROCCAT_VULCAN_II_PID 0x2F4E
#define ROCCAT_VULCAN_II_MAX_PID 0x2EE2
#define ROCCAT_PYRO_PID 0x314C
#define ROCCAT_MAGMA_PID 0x3124
#define ROCCAT_MAGMA_MINI_PID 0x69A0
@@ -60,6 +61,8 @@ enum
struct device_info
{
std::string version;
int version_major;
int version_minor;
int layout_type;
};
@@ -88,6 +91,7 @@ public:
void AwaitResponse(int ms);
void ClearResponses();
uint16_t device_pid;
private:
@@ -96,4 +100,6 @@ private:
device_info dev_info;
std::string location;
std::string name;
bool IsBigEndianDirectMode();
};
@@ -805,6 +805,229 @@ static std::map<int, layout_info> RoccatVulcanIILayouts =
},
};
static unsigned int ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_ANSI[6][24] =
{
{ 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, 105, 106, 107, NA },
{ 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 },
{ 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, 70, NA, 77, 81, 86, NA, 89, 94, 98, 103 },
{ 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 73, NA, NA, NA, NA, NA, NA, 90, 95, 99, NA },
{ 4, NA, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, 71, NA, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 },
{ 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, 74, NA, NA, 78, 83, 87, NA, 92, NA, 101, NA }
};
static std::map<int, layout_info> RoccatVulcanIIMaxLayouts =
{
{
ROCCAT_VULCAN_LAYOUT_US,
{
*ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_ANSI,
148,
6,
24,
{
{ KEY_EN_ESCAPE, 0x02 },
{ KEY_EN_BACK_TICK, 0x03 },
{ KEY_EN_TAB, 0x04 },
{ KEY_EN_CAPS_LOCK, 0x05 },
{ KEY_EN_LEFT_SHIFT, 0x00 },
{ KEY_EN_LEFT_CONTROL, 0x01 },
{ KEY_EN_1, 0x08 },
{ KEY_EN_UNUSED, 0x06 },
{ KEY_EN_LEFT_WINDOWS, 0x07 },
{ KEY_EN_F1, 0x0D },
{ KEY_EN_2, 0x0E },
{ KEY_EN_Q, 0x09 },
{ KEY_EN_A, 0x0A },
{ KEY_EN_Z, 0x0B },
{ KEY_EN_LEFT_ALT, 0x0C },
{ KEY_EN_F2, 0x14 },
{ KEY_EN_3, 0x15 },
{ KEY_EN_W, 0x0F },
{ KEY_EN_S, 0x10 },
{ KEY_EN_X, 0x11 },
{ KEY_EN_F3, 0x19 },
{ KEY_EN_4, 0x1A },
{ KEY_EN_E, 0x16 },
{ KEY_EN_D, 0x17 },
{ KEY_EN_C, 0x18 },
{ KEY_EN_F4, 0x1F },
{ KEY_EN_5, 0x1E },
{ KEY_EN_R, 0x1B },
{ KEY_EN_F, 0x1C },
{ KEY_EN_V, 0x21 },
{ KEY_EN_6, 0x24 },
{ KEY_EN_T, 0x20 },
{ KEY_EN_G, 0x1D },
{ KEY_EN_B, 0x22 },
{ KEY_EN_SPACE, 0x28 },
{ KEY_EN_F5, 0x23 },
{ KEY_EN_7, 0x29 },
{ KEY_EN_Y, 0x25 },
{ KEY_EN_H, 0x26 },
{ KEY_EN_N, 0x27 },
{ KEY_EN_F6, 0x2F },
{ KEY_EN_8, 0x30 },
{ KEY_EN_U, 0x3D },
{ KEY_EN_J, 0x2B },
{ KEY_EN_M, 0x2C },
{ KEY_EN_F7, 0x35 },
{ KEY_EN_9, 0x36 },
{ KEY_EN_I, 0x31 },
{ KEY_EN_K, 0x32 },
{ KEY_EN_COMMA, 0x33 },
{ KEY_EN_F8, 0x3B },
{ KEY_EN_0, 0x3C },
{ KEY_EN_O, 0x37 },
{ KEY_EN_L, 0x38 },
{ KEY_EN_PERIOD, 0x39 },
{ KEY_EN_RIGHT_ALT, 0x3A },
{ KEY_EN_F9, 0x41 },
{ KEY_EN_MINUS, 0x42 },
{ KEY_EN_P, 0x2A },
{ KEY_EN_SEMICOLON, 0x3E },
{ KEY_EN_FORWARD_SLASH, 0x3F },
{ KEY_EN_RIGHT_FUNCTION, 0x40 },
{ KEY_EN_F10, 0x47 },
{ KEY_EN_EQUALS, 0x48 },
{ KEY_EN_LEFT_BRACKET, 0x43 },
{ KEY_EN_QUOTE, 0x44 },
{ KEY_EN_MENU, 0x46 },
{ KEY_EN_F11, 0x4D },
{ KEY_EN_BACKSPACE, 0x50 },
{ KEY_EN_RIGHT_BRACKET, 0x49 },
{ KEY_EN_ANSI_BACK_SLASH, 0x51 },
{ KEY_EN_RIGHT_SHIFT, 0x4B },
{ KEY_EN_F12, 0x4F },
{ KEY_EN_ANSI_ENTER, 0x52 },
{ KEY_EN_RIGHT_CONTROL, 0x4C },
{ KEY_EN_PRINT_SCREEN, 0x53 },
{ KEY_EN_INSERT, 0x54 },
{ KEY_EN_DELETE, 0x55 },
{ KEY_EN_LEFT_ARROW, 0x56 },
{ KEY_EN_SCROLL_LOCK, 0x57 },
{ KEY_EN_HOME, 0x58 },
{ KEY_EN_END, 0x59 },
{ KEY_EN_UP_ARROW, 0x5A },
{ KEY_EN_DOWN_ARROW, 0x5B },
{ KEY_EN_PAUSE_BREAK, 0x5C },
{ KEY_EN_PAGE_UP, 0x5D },
{ KEY_EN_PAGE_DOWN, 0x5E },
{ KEY_EN_RIGHT_ARROW, 0x5F },
{ KEY_EN_NUMPAD_LOCK, 0x60 },
{ KEY_EN_NUMPAD_7, 0x61 },
{ KEY_EN_NUMPAD_4, 0x62 },
{ KEY_EN_NUMPAD_1, 0x6C },
{ KEY_EN_NUMPAD_0, 0x64 },
{ KEY_EN_NUMPAD_DIVIDE, 0x65 },
{ KEY_EN_NUMPAD_8, 0x66 },
{ KEY_EN_NUMPAD_5, 0x67 },
{ KEY_EN_NUMPAD_2, 0x68 },
{ KEY_EN_NUMPAD_TIMES, 0x69 },
{ KEY_EN_NUMPAD_9, 0x6A },
{ KEY_EN_NUMPAD_6, 0x6B },
{ KEY_EN_NUMPAD_3, 0x63 },
{ KEY_EN_NUMPAD_PERIOD, 0x6D },
{ KEY_EN_NUMPAD_MINUS, 0x6E },
{ KEY_EN_NUMPAD_PLUS, 0x6F },
{ KEY_EN_NUMPAD_ENTER, 0x71 },
{ KEY_EN_MEDIA_PREVIOUS, 0x72 },
{ KEY_EN_MEDIA_PLAY_PAUSE, 0x73 },
{ KEY_EN_MEDIA_NEXT, 0x74 },
/*-------------------------------------------------------------*\
| F-Key Indicators (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: F1 LED 2", 0x89 },
{ "Key: F2 LED 2", 0x7B },
{ "Key: F3 LED 2", 0x81 },
{ "Key: F4 LED 2", 0x87 },
{ "Key: F5 LED 2", 0x8D },
{ "Key: F6 LED 2", 0x76 },
{ "Key: F7 LED 2", 0x7C },
{ "Key: F8 LED 2", 0x82 },
{ "Key: F9 LED 2", 0x88 },
{ "Key: F10 LED 2", 0x8E },
{ "Key: F11 LED 2", 0x7A },
{ "Key: F12 LED 2", 0x80 },
{ "Key: Print Screen LED 2", 0x86 },
{ "Key: Scroll Lock LED 2", 0x8C },
{ "Key: Pause/Break LED 2", 0x92 },
/*-------------------------------------------------------------*\
| Nav Cluster Indicators (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: Insert LED 2", 0x75 },
{ "Key: Home LED 2", 0x7E },
{ "Key: Page Up LED 2", 0x84 },
/*-------------------------------------------------------------*\
| Num Lock Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Num Lock LED 2", 0x8B },
/*-------------------------------------------------------------*\
| Nav Cluster Indicators 2 (secondary LEDs) |
\*-------------------------------------------------------------*/
{ "Key: Delete LED 2", 0x78 },
{ "Key: End LED 2", 0x90 },
{ "Key: Page Down LED 2", 0x8A },
/*-------------------------------------------------------------*\
| Caps Lock Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Caps Lock LED 2", 0x7D },
/*-------------------------------------------------------------*\
| Win Key Indicator (secondary LED) |
\*-------------------------------------------------------------*/
{ "Key: Left Windows LED 2", 0x83 },
/*-------------------------------------------------------------*\
| Palm Rest LEDs (order approximate - needs verification) |
\*-------------------------------------------------------------*/
{ "Palm Rest LED 1", 0x96 },
{ "Palm Rest LED 2", 0xAD },
{ "Palm Rest LED 3", 0xA7 },
{ "Palm Rest LED 4", 0xA1 },
{ "Palm Rest LED 5", 0x9B },
{ "Palm Rest LED 6", 0x95 },
{ "Palm Rest LED 7", 0xAC },
{ "Palm Rest LED 8", 0xA6 },
{ "Palm Rest LED 9", 0xA0 },
{ "Palm Rest LED 10", 0x9A },
{ "Palm Rest LED 11", 0x94 },
{ "Palm Rest LED 12", 0xAB },
{ "Palm Rest LED 13", 0xA5 },
{ "Palm Rest LED 14", 0x9F },
{ "Palm Rest LED 15", 0x99 },
{ "Palm Rest LED 16", 0x93 }
}
}
},
};
static std::map<int, layout_info> RoccatVulcanTKLLayouts =
{
{
@@ -10,6 +10,7 @@
\*---------------------------------------------------------*/
#include "SteelSeriesApexBaseController.h"
#include <algorithm>
SteelSeriesApexBaseController::SteelSeriesApexBaseController(hid_device* dev_handle, const char* path, std::string dev_name)
{
@@ -34,17 +35,15 @@ std::string SteelSeriesApexBaseController::GetName()
}
/*---------------------------------------------------------*\
| The serial number of the keyboard is acquired by sending |
| an output report to address 0xFF and reading the result. |
| The HID capability table is not used. The serial number |
| also contains the model number which can be used to |
| determine the physical layout of different region |
| keyboards throughout the product stack. |
| Gen 1 Apex Pro stores the unit serial number in firmware. |
| The first 5 digits determine the region of the keyboard. |
| This is not the case for Gen 3, call to this function |
| will be ignored. |
\*---------------------------------------------------------*/
std::string SteelSeriesApexBaseController::GetSerial()
{
std::string return_string = "";
if(proto_type == APEX)
if(proto_type == APEX && kbd_quirk == APEX_GEN1)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
@@ -70,6 +69,27 @@ std::string SteelSeriesApexBaseController::GetSerial()
return(return_string);
}
std::string ExtractVersion(std::string version_string)
{
/*---------------------------------------------*\
| Find 2 periods in string, if found we can |
| form a X.Y.Z revision. |
\*---------------------------------------------*/
std::size_t majorp = version_string.find('.');
if(majorp != std::string::npos)
{
std::size_t minorp = version_string.find('.', majorp+1);
if(minorp != std::string::npos)
{
std::string major = version_string.substr(0, majorp);
std::string minor = version_string.substr(majorp+1, (minorp-majorp-1));
std::string build = version_string.substr(minorp+1);
return major + "." + minor + "." + build;
}
}
return "";
}
std::string SteelSeriesApexBaseController::GetVersion()
{
std::string return_string = "Unsupported protocol";
@@ -77,11 +97,9 @@ std::string SteelSeriesApexBaseController::GetVersion()
if(proto_type == APEX)
{
/*-------------------------------------------------*\
| For the Apex Pro there are two firmware versions |
| which can be acquired, KBD and LED. We know |
| where both are located, we do not know which is |
| what. For now we'll make an assumption and fix |
| if proven wrong. |
| Gen 1 & 2 Apex Pro report KBD and LED firmware |
| Gen 3 only reports the KBD firmware, ignoring |
| requests to read the LED version |
\*-------------------------------------------------*/
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
@@ -96,50 +114,36 @@ std::string SteelSeriesApexBaseController::GetVersion()
if(result > 0)
{
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
fwver = fwver.c_str();
fwver.erase(std::remove(fwver.begin(), fwver.end(), '\0'), fwver.end());
/*---------------------------------------------*\
| Find 2 periods in string, if found we can |
| form a X.Y.Z revision. |
| Apex Pro Gen 3 needs the first char dropped |
\*---------------------------------------------*/
std::size_t majorp = fwver.find('.');
if(majorp != std::string::npos)
if(kbd_quirk == APEX_GEN3)
{
std::size_t minorp = fwver.find('.', majorp+1);
if(minorp != std::string::npos)
{
std::string major = fwver.substr(0, majorp);
std::string minor = fwver.substr(majorp+1, (minorp-majorp-1));
std::string build = fwver.substr(minorp+1);
return_string = "KBD: " + major + "." + minor + "." + build;
}
fwver.erase(0,1);
}
return_string = "KBD: " + ExtractVersion(fwver);
}
/*-------------------------------------------------*\
| Clear and reuse buffer |
\*-------------------------------------------------*/
memset(ibuf, 0x00, sizeof(ibuf));
obuf[0x02] = 0x01;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 10);
if(result > 0)
if(kbd_quirk != APEX_GEN3)
{
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
fwver = fwver.c_str();
memset(ibuf, 0x00, sizeof(ibuf));
obuf[0x02] = 0x01;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 10);
std::size_t majorp = fwver.find('.');
if(majorp != std::string::npos)
if(result > 0)
{
std::size_t minorp = fwver.find('.', majorp+1);
if(minorp != std::string::npos)
{
std::string major = fwver.substr(0, majorp);
std::string minor = fwver.substr(majorp+1, (minorp-majorp-1));
std::string build = fwver.substr(minorp+1);
return_string = return_string + " / LED: " + major + "." + minor + "." + build;
}
std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE);
fwver.erase(std::remove(fwver.begin(), fwver.end(), '\0'), fwver.end());
fwver = fwver.c_str();
return_string = return_string + " / LED: " + ExtractVersion(fwver);
}
}
}
@@ -19,6 +19,20 @@
#define STEELSERIES_PACKET_IN_SIZE 64
#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1
/*-------------------------------------------------*\
| Gen 1: 2019-22 models (all FW) & 2023 FW < 1.19.7 |
| Gen 2: 2023 models with FW >= 1.19.7 |
| Gen 3: 2025+ and may feature Gen 3 in the name |
\*-------------------------------------------------*/
typedef enum
{
APEX_GEN1 = 0x00,
APEX_GEN2 = 0x01,
APEX_GEN3 = 0x02,
} protocol_quirk;
class SteelSeriesApexBaseController
{
public:
@@ -41,4 +55,5 @@ protected:
unsigned char active_mode;
std::string location;
std::string name;
protocol_quirk kbd_quirk;
};
@@ -33,18 +33,31 @@ static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x
0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100
0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62,
0x63 };
0x63, 0xFB };
SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name)
{
proto_type = type;
use_new_protocol = false;
kbd_quirk = APEX_GEN1;
SendInitialization();
}
SteelSeriesApexController::~SteelSeriesApexController()
{
/*-----------------------------------------------------*\
| Gen 3 models must be explicitly cleared for on-board |
| config selection to apply without power cycling after |
| OpenRGB shuts down. |
\*-----------------------------------------------------*/
if(kbd_quirk == APEX_GEN3)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
memset(obuf, 0x00, sizeof(obuf));
obuf[0x00] = 0;
obuf[0x01] = APEX_GEN3_PACKET_CLEAR_LIGHTING;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
}
hid_close(dev);
}
@@ -70,7 +83,7 @@ void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
num_keys = sizeof(keys) / sizeof(*keys);
if(use_new_protocol)
if(kbd_quirk >= APEX_GEN2)
{
struct hid_device_info* info = hid_get_device_info(dev);
@@ -79,7 +92,9 @@ void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
| protocol, make sure to place their PID here and |
| further below when developing. |
\*-------------------------------------------------*/
if(info && (info->product_id == 0x162C || info->product_id == 0x162D))
if(info && (info->product_id == 0x1630 || info->product_id == 0x1632
|| info->product_id == 0x162C || info->product_id == 0x162D
|| info->product_id == 0x1644 || info->product_id == 0x1646))
{
packet_id = APEX_2023_PACKET_ID_DIRECT_WIRELESS;
}
@@ -99,7 +114,7 @@ void SteelSeriesApexController::SetLEDsDirect(std::vector<RGBColor> colors)
\*-----------------------------------------------------*/
buf[0x00] = 0;
buf[0x01] = packet_id;
buf[0x02] = (use_new_protocol) ? (unsigned char)colors.size() : num_keys;
buf[0x02] = kbd_quirk ? (unsigned char)colors.size() : num_keys;
/*-----------------------------------------------------*\
| Fill in color data |
@@ -149,7 +164,7 @@ void SteelSeriesApexController::SendInitialization()
unsigned short pid = (info) ? info->product_id : 0;
/*-----------------------------------------------------*\
| Firmware check |
| Firmware check for TKL 2023 |
\*-----------------------------------------------------*/
if(pid == 0x1628)
{
@@ -190,17 +205,17 @@ void SteelSeriesApexController::SendInitialization()
\*-----------------------------------------*/
if(major > 1)
{
use_new_protocol = true;
kbd_quirk = APEX_GEN2;
}
else if(major == 1)
{
if(minor > 19)
{
use_new_protocol = true;
kbd_quirk = APEX_GEN2;
}
else if(minor == 19 && patch >= 7)
{
use_new_protocol = true;
kbd_quirk = APEX_GEN2;
}
}
}
@@ -211,15 +226,17 @@ void SteelSeriesApexController::SendInitialization()
| sure to place their PID here and further above for |
| wireless when developing. |
\*-----------------------------------------------------*/
else if(pid == 0x162C || pid == 0x162D)
else if(pid == 0x1630 || pid == 0x1632
|| pid == 0x162C || pid == 0x162D
|| pid == 0x1642 || pid == 0x1644 || pid == 0x1646)
{
use_new_protocol = true;
kbd_quirk = APEX_GEN3;
}
/*-----------------------------------------------------*\
| Send Initialization packet on new protocol. |
\*-----------------------------------------------------*/
if(use_new_protocol)
if(kbd_quirk >= APEX_GEN2)
{
memset(buf, 0x00, sizeof(buf));
buf[0x00] = 0x00;
@@ -236,8 +253,64 @@ void SteelSeriesApexController::SendInitialization()
std::string SteelSeriesApexController::GetSerial()
{
if(use_new_protocol)
/*-------------------------------------------------*\
| Gen 3 doesn't expose the serial number in |
| firmware. A region code is instead set by the |
| user and subsequently read back. This region code |
| is used by all 5 on-board configs. |
| For consistency with other Apex keyboards, this |
| region code in combination with the PID is mapped |
| to an approximate product number as the region |
| patch logic from that point is identical. |
| The product number used may not be an exact match |
| for the keyboard but should reflect the form |
| factor, region and RGB layout |
\*-------------------------------------------------*/
if(kbd_quirk >= APEX_GEN2)
{
unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE];
unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE];
int result;
struct hid_device_info* info = hid_get_device_info(dev);
unsigned short pid = (info) ? info->product_id : 0;
memset(obuf, 0x00, sizeof(obuf));
if(pid == 0x1642)
{
obuf[0x00] = 0;
obuf[0x01] = 0xF5;
hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE);
result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2);
if(result > 3 && ibuf[0] == 0xF5)
{
switch(ibuf[2])
{
case 0x1:
return "64740";
break;
case 0x3:
return "64741";
break;
case 0x4:
return "64743";
break;
case 0x6:
return "64744";
break;
case 0xA:
return "64742";
break;
case 0xD:
return "64745";
break;
default:
break;
}
}
}
return "64865";
}
@@ -28,6 +28,7 @@ enum
APEX_2023_PACKET_ID_DIRECT_WIRELESS = 0x61, /* New Wireless Direct mode */
APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */
APEX_2023_PACKET_LENGTH = 643,
APEX_GEN3_PACKET_CLEAR_LIGHTING = 0x41,
};
class SteelSeriesApexController : public SteelSeriesApexBaseController
@@ -55,5 +56,4 @@ private:
void SendInitialization();
bool use_new_protocol;
};
@@ -20,17 +20,16 @@
#define NA 0xFFFFFFFF
/*----------------------------------------------------------------------*\
| As of firmware 4.1.0 there are in total 111 possible standard keys |
| which are shared across the Apex Pro / 7 / TKL / 5 and their regional |
| SKUs in addition to the 6 media keys. No SKU has all 111, however |
| regardless of the physial layout, the one configuration can be used |
| across all SKUs with missing keys simply having no effect. |
| The complication comes in the visualisation as different key layouts |
| change the LED positions, additionally some labels / scancodes are |
| overloaded based on the language which clashes with the OpenRGB |
| defaults. In order to account for this a base SKU (ANSI) is assumed |
| which is transformed into a regional SKU when device detection returns |
| a known SKU number from the first 5 characters of the serial number. |
| Steelseries keyboards share a library of 111 standard keys plus extra |
| ambients across all physical form factors. No keyboard has every key |
| fitted but the same format packet can be sent regardless of model and |
| the firmware will pick the keys applicable to it, ignoring the rest. |
| The complication comes in commuicating this to the OpenRGB GUI as |
| different key layouts change the LED positions, additionally some |
| labels / scancodes are overloaded based on the language which clashes |
| with the OpenRGB defaults. In order to account for this a base SKU |
| (ANSI) is assumed which is transformed into a regional SKU when device |
| detection returns a known product number acquired from the keyboard |
\*----------------------------------------------------------------------*/
#define MATRIX_HEIGHT 6
@@ -50,6 +49,10 @@
static const int matrix_mapsize = MATRIX_HEIGHT * MATRIX_WIDTH;
/*-------------------------------------------------------*\
| These map to the values defined in the keys array at |
| SteelSeriesApexController |
\*-------------------------------------------------------*/
static const char* led_names[] =
{
KEY_EN_A,
@@ -163,6 +166,7 @@ static const char* led_names[] =
KEY_EN_NUMPAD_9,
KEY_EN_NUMPAD_0,
KEY_EN_NUMPAD_PERIOD,
KEY_EN_MEDIA_PLAY_PAUSE
};
struct matrix_region_patch
@@ -244,7 +248,7 @@ static const std::vector<matrix_region_patch> apex_tkl_us_region_patch =
{
{0, 15, NA},
{0, 16, NA},
{0, 17, NA},
{0, 17, 111},
{1, 18, NA},
{1, 19, NA},
{1, 20, NA},
@@ -367,6 +371,9 @@ static const std::map<std::string, sku_patch> patch_lookup =
{ "64660", { {}, {}, {} }},
{ "64740", { apex_tkl_us_region_patch, {}, {} }},
{ "64742", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }},
{ "64743", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }},
{ "64745", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }},
{ "64871", { apex_tkl_us_region_patch, {}, {} }},
{ "64913", { apex_mini_us_region_patch, {}, {} }},
@@ -488,7 +495,7 @@ static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned
| SKU codes for all known Apex Pro / 7 / 5 & TKL variant |
| keyboards as at Janauary 2022. Generated by cross-checking |
| store listings aginst Steelseries website. |
| Updated 2025 for Apex 9 and Pro Gen 2 & Gen 3 |
| Updated 2026 for Apex 9 and Pro Gen 2 & Gen 3 |
| The product Pro Mini seem to belong to Gen 2 |
| |
| -- APEX PRO Gen 3 -- |
@@ -497,11 +504,17 @@ static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned
| |
| >> APEX PRO TKL Gen 3 |
| |
| "64740", // US TKL |
| "64740", // US TKL Black |
| "64741", // UK TKL Black |
| "64742", // Nordic TKL Black |
| "64743", // German TKL Black |
| "64744", // French TKL Black |
| "64745", // Japanese TKL Black |
| |
| >> APEX PRO TKL Wireless Gen 3 |
| |
| "64871", // US TKL Wireless |
| "64871", // US TKL Black Wireless |
| "64876", // Japanese TKL Black Wireless |
| |
| >> APEX PRO Mini Gen 3 |
| |
@@ -512,6 +525,7 @@ static void SetSkuLedNames (std::vector<led>& input, std::string& sku, unsigned
| >> APEX PRO TKL Gen 2 / 2023 |
| |
| "64856", // US TKL |
| "64861", // Japanese TKL |
| |
| >> APEX PRO TKL Wireless Gen 2 / 2023 |
| |
@@ -124,6 +124,9 @@
#define STEELSERIES_APEX_OG_PID 0x1202
#define STEELSERIES_APEX_350_PID 0x1206
#define STEELSERIES_APEX_PRO3_PID 0x1640
#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646
void DetectSteelSeriesAerox3(hid_device_info* info, const std::string& name)
{
@@ -517,3 +520,6 @@ REGISTER_HID_DETECTOR_I ("SteelSeries Apex M750", Dete
REGISTER_HID_DETECTOR_I ("SteelSeries Apex (OG)/Apex Fnatic", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_OG_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1);
@@ -46,7 +46,7 @@ std::string ThermaltakeRiingController::GetSerialString()
std::string ThermaltakeRiingController::GetFirmwareVersion()
{
unsigned char usb_buf[64];
unsigned char usb_buf[65];
/*-----------------------------------------------------*\
| Zero out buffer |
@@ -56,14 +56,15 @@ std::string ThermaltakeRiingController::GetFirmwareVersion()
/*-----------------------------------------------------*\
| Set up Get Firmware Version packet |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x33;
usb_buf[0x01] = 0x50;
usb_buf[0x00] = 0x00;
usb_buf[0x01] = 0x33;
usb_buf[0x02] = 0x50;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 64);
hid_read(dev, usb_buf, 64);
hid_write(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
std::string ret_str = std::to_string(usb_buf[2]) + "." + std::to_string(usb_buf[3]) + "." + std::to_string(usb_buf[4]);
@@ -117,7 +118,7 @@ void ThermaltakeRiingController::SendInit()
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
}
void ThermaltakeRiingController::SendRGB
@@ -154,5 +155,5 @@ void ThermaltakeRiingController::SendRGB
| Send packet |
\*-----------------------------------------------------*/
hid_write(dev, usb_buf, 65);
hid_read(dev, usb_buf, 65);
hid_read_timeout(dev, usb_buf, 65, 100);
}
@@ -0,0 +1,353 @@
/*---------------------------------------------------------*\
| RGBController_ThrustmasterSol.cpp |
| |
| RGBController for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ThrustmasterSol.h"
/**------------------------------------------------------------------*\
@name Thrustmaster Sol
@category Gamepad
@type USB
@save :white_check_mark:
@direct :white_check_mark:
@effects :x:
@detectors DetectThrustmasterSolControllers
@comment Thrustmaster Sol series joystick RGB LED control. Supports
Sol-R, Sol F16, and Sol F18 variants. Only Sol-R has been tested.
Uses vendor-specific USB interface 1 (not HID).
\*-------------------------------------------------------------------*/
#define NA 0xFFFFFFFF
static unsigned int logo_matrix_map[2][2] =
{
{ 0, 3 },
{ 1, 2 },
};
static unsigned int ring_matrix_map[3][5] =
{
{ NA, 7, 0, 1, NA },
{ 6, NA, NA, NA, 2 },
{ NA, 5, 4, 3, NA },
};
static unsigned int right_buttons_matrix_map[2][2] =
{
{ 0, 1 },
{ 2, 3 },
};
static unsigned int left_buttons_matrix_map[2][2] =
{
{ 0, 1 },
{ 2, 3 },
};
RGBController_ThrustmasterSol::RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr)
{
controller = controller_ptr;
vendor = "Thrustmaster";
type = DEVICE_TYPE_GAMEPAD;
description = "Thrustmaster Sol Series Joystick";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
/*---------------------------------------------------------*\
| Read current EEPROM colors from device so the UI starts |
| with the actual hardware state rather than all-black |
\*---------------------------------------------------------*/
std::vector<unsigned int> hw_zones;
std::vector<RGBColor> hw_colors;
controller->ReadColors(hw_zones, hw_colors);
for(unsigned int i = 0; i < hw_zones.size(); i++)
{
for(unsigned int j = 0; j < leds.size(); j++)
{
if(leds[j].value == hw_zones[i])
{
colors[j] = hw_colors[i];
break;
}
}
}
}
RGBController_ThrustmasterSol::~RGBController_ThrustmasterSol()
{
delete controller;
}
void RGBController_ThrustmasterSol::SetupZones()
{
/*---------------------------------------------------------*\
| Thumbstick zone (grip, 1 LED) |
\*---------------------------------------------------------*/
zone thumbstick_zone;
thumbstick_zone.name = "Thumbstick";
thumbstick_zone.type = ZONE_TYPE_SINGLE;
thumbstick_zone.leds_min = 1;
thumbstick_zone.leds_max = 1;
thumbstick_zone.leds_count = 1;
thumbstick_zone.matrix_map = NULL;
zones.push_back(thumbstick_zone);
led thumbstick_led;
thumbstick_led.name = "Thumbstick";
thumbstick_led.value = THRUSTMASTER_SOL_GRIP_FLAG | 0x00;
leds.push_back(thumbstick_led);
/*---------------------------------------------------------*\
| TM Logo zone (3 LEDs) |
\*---------------------------------------------------------*/
zone logo_zone;
logo_zone.name = "TM Logo";
logo_zone.type = ZONE_TYPE_MATRIX;
logo_zone.leds_min = 4;
logo_zone.leds_max = 4;
logo_zone.leds_count = 4;
logo_zone.matrix_map = new matrix_map_type;
logo_zone.matrix_map->height = 2;
logo_zone.matrix_map->width = 2;
logo_zone.matrix_map->map = (unsigned int *)&logo_matrix_map;
zones.push_back(logo_zone);
led logo_top_left;
logo_top_left.name = "TM Logo Top Left";
logo_top_left.value = 0x01;
leds.push_back(logo_top_left);
led logo_bottom_left;
logo_bottom_left.name = "TM Logo Bottom Left";
logo_bottom_left.value = 0x02;
leds.push_back(logo_bottom_left);
led logo_bottom_right;
logo_bottom_right.name = "TM Logo Bottom Right";
logo_bottom_right.value = 0x03;
leds.push_back(logo_bottom_right);
led logo_top_right;
logo_top_right.name = "TM Logo Top Right";
logo_top_right.value = 0x00;
leds.push_back(logo_top_right);
/*---------------------------------------------------------*\
| Left Buttons zone (4 LEDs: buttons 5-8) |
\*---------------------------------------------------------*/
zone left_buttons_zone;
left_buttons_zone.name = "Left Buttons";
left_buttons_zone.type = ZONE_TYPE_MATRIX;
left_buttons_zone.leds_min = 4;
left_buttons_zone.leds_max = 4;
left_buttons_zone.leds_count = 4;
left_buttons_zone.matrix_map = new matrix_map_type;
left_buttons_zone.matrix_map->height = 2;
left_buttons_zone.matrix_map->width = 2;
left_buttons_zone.matrix_map->map = (unsigned int *)&left_buttons_matrix_map;
zones.push_back(left_buttons_zone);
led btn5;
btn5.name = "Button 5";
btn5.value = 0x11;
leds.push_back(btn5);
led btn6;
btn6.name = "Button 6";
btn6.value = 0x10;
leds.push_back(btn6);
led btn7;
btn7.name = "Button 7";
btn7.value = 0x12;
leds.push_back(btn7);
led btn8;
btn8.name = "Button 8";
btn8.value = 0x13;
leds.push_back(btn8);
/*---------------------------------------------------------*\
| Base Ring zone (8 LEDs, clockwise from top) |
\*---------------------------------------------------------*/
zone ring_zone;
ring_zone.name = "Base Ring";
ring_zone.type = ZONE_TYPE_MATRIX;
ring_zone.leds_min = 8;
ring_zone.leds_max = 8;
ring_zone.leds_count = 8;
ring_zone.matrix_map = new matrix_map_type;
ring_zone.matrix_map->height = 3;
ring_zone.matrix_map->width = 5;
ring_zone.matrix_map->map = (unsigned int *)&ring_matrix_map;
zones.push_back(ring_zone);
led ring_upper;
ring_upper.name = "Upper";
ring_upper.value = 0x04;
leds.push_back(ring_upper);
led ring_upper_right;
ring_upper_right.name = "Upper Right";
ring_upper_right.value = 0x05;
leds.push_back(ring_upper_right);
led ring_right;
ring_right.name = "Right";
ring_right.value = 0x06;
leds.push_back(ring_right);
led ring_bottom_right;
ring_bottom_right.name = "Bottom Right";
ring_bottom_right.value = 0x0B;
leds.push_back(ring_bottom_right);
led ring_bottom;
ring_bottom.name = "Bottom";
ring_bottom.value = 0x0C;
leds.push_back(ring_bottom);
led ring_bottom_left;
ring_bottom_left.name = "Bottom Left";
ring_bottom_left.value = 0x0D;
leds.push_back(ring_bottom_left);
led ring_left;
ring_left.name = "Left";
ring_left.value = 0x0E;
leds.push_back(ring_left);
led ring_upper_left;
ring_upper_left.name = "Upper Left";
ring_upper_left.value = 0x0F;
leds.push_back(ring_upper_left);
/*---------------------------------------------------------*\
| Right Buttons zone (4 LEDs: buttons 16-19) |
\*---------------------------------------------------------*/
zone right_buttons_zone;
right_buttons_zone.name = "Right Buttons";
right_buttons_zone.type = ZONE_TYPE_MATRIX;
right_buttons_zone.leds_min = 4;
right_buttons_zone.leds_max = 4;
right_buttons_zone.leds_count = 4;
right_buttons_zone.matrix_map = new matrix_map_type;
right_buttons_zone.matrix_map->height = 2;
right_buttons_zone.matrix_map->width = 2;
right_buttons_zone.matrix_map->map = (unsigned int *)&right_buttons_matrix_map;
zones.push_back(right_buttons_zone);
led btn17;
btn17.name = "Button 17";
btn17.value = 0x07;
leds.push_back(btn17);
led btn16;
btn16.name = "Button 16";
btn16.value = 0x08;
leds.push_back(btn16);
led btn19;
btn19.name = "Button 19";
btn19.value = 0x0A;
leds.push_back(btn19);
led btn18;
btn18.name = "Button 18";
btn18.value = 0x09;
leds.push_back(btn18);
SetupColors();
}
void RGBController_ThrustmasterSol::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_ThrustmasterSol::DeviceUpdateLEDs()
{
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++)
{
led_zones[led_idx] = leds[led_idx].value;
led_colors[led_idx] = colors[led_idx];
}
controller->SetLEDColors(led_zones, led_colors, static_cast<unsigned int>(leds.size()));
}
void RGBController_ThrustmasterSol::UpdateZoneLEDs(int zone)
{
unsigned int start_idx = 0;
unsigned int zone_size = 0;
for(unsigned int z_idx = 0; z_idx < zones.size(); z_idx++)
{
if(z_idx == (unsigned int)zone)
{
zone_size = zones[z_idx].leds_count;
break;
}
start_idx += zones[z_idx].leds_count;
}
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < zone_size; led_idx++)
{
unsigned int current_idx = start_idx + led_idx;
led_zones[led_idx] = leds[current_idx].value;
led_colors[led_idx] = colors[current_idx];
}
controller->SetLEDColors(led_zones, led_colors, zone_size);
}
void RGBController_ThrustmasterSol::UpdateSingleLED(int led)
{
controller->SetLEDColor(leds[led].value, colors[led]);
}
void RGBController_ThrustmasterSol::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
void RGBController_ThrustmasterSol::DeviceSaveMode()
{
unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++)
{
led_zones[led_idx] = leds[led_idx].value;
led_colors[led_idx] = colors[led_idx];
}
controller->SaveColors(led_zones, led_colors, static_cast<unsigned int>(leds.size()));
}
@@ -1,35 +1,35 @@
/*---------------------------------------------------------*\
| RGBController_CorsairVengeancePro.h |
| |
| RGBController for Corsair Vengeance Pro RGB RAM |
| |
| Adam Honse (CalcProgrammer1) 30 Jun 2019 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "CorsairVengeanceProController.h"
class RGBController_CorsairVengeancePro : public RGBController
{
public:
RGBController_CorsairVengeancePro(CorsairVengeanceProController* controller_ptr);
~RGBController_CorsairVengeancePro();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
CorsairVengeanceProController* controller;
};
/*---------------------------------------------------------*\
| RGBController_ThrustmasterSol.h |
| |
| RGBController for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ThrustmasterSolController.h"
class RGBController_ThrustmasterSol : public RGBController
{
public:
RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr);
~RGBController_ThrustmasterSol();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
ThrustmasterSolController* controller;
};
@@ -0,0 +1,315 @@
/*---------------------------------------------------------*\
| ThrustmasterSolController.cpp |
| |
| Driver for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <cstring>
#include <thread>
#include <chrono>
#include "ThrustmasterSolController.h"
#include "StringUtils.h"
ThrustmasterSolController::ThrustmasterSolController(libusb_device_handle* dev_handle,
const char* path,
unsigned short pid)
{
dev = dev_handle;
this->pid = pid;
location = "USB: ";
location += path;
/*---------------------------------------------------------*\
| Get serial number string descriptor |
\*---------------------------------------------------------*/
libusb_device_descriptor desc;
libusb_get_device_descriptor(libusb_get_device(dev_handle), &desc);
if(desc.iSerialNumber != 0)
{
unsigned char serial_str[256];
int ret = libusb_get_string_descriptor_ascii(dev_handle,
desc.iSerialNumber,
serial_str,
sizeof(serial_str));
if(ret > 0)
{
serial = std::string(reinterpret_cast<char*>(serial_str), ret);
}
}
}
ThrustmasterSolController::~ThrustmasterSolController()
{
if(dev != nullptr)
{
libusb_release_interface(dev, THRUSTMASTER_SOL_INTERFACE);
libusb_close(dev);
}
}
std::string ThrustmasterSolController::GetDeviceLocation()
{
return(location);
}
std::string ThrustmasterSolController::GetSerialString()
{
return(serial);
}
unsigned short ThrustmasterSolController::GetPID()
{
return(pid);
}
void ThrustmasterSolController::SendPacket(unsigned char* packet, unsigned int size)
{
if(dev == nullptr)
{
return;
}
int actual_length = 0;
libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_OUT,
packet,
size,
&actual_length,
1000);
}
void ThrustmasterSolController::BuildAndSendPackets(unsigned int* zones,
RGBColor* colors,
unsigned int count,
bool persistent)
{
unsigned char persist_flag = persistent ? THRUSTMASTER_SOL_PERSISTENT
: THRUSTMASTER_SOL_VOLATILE;
/*---------------------------------------------------------*\
| Separate grip zones from base zones. Zone 0x00 exists on |
| both the base (logo LED) and the grip (thumbstick LED). |
| The GRIP_FLAG bit selects the grip report type. |
\*---------------------------------------------------------*/
unsigned char base_zones[THRUSTMASTER_SOL_R_ZONE_COUNT];
RGBColor base_colors[THRUSTMASTER_SOL_R_ZONE_COUNT];
unsigned int base_count = 0;
for(unsigned int i = 0; i < count; i++)
{
if(zones[i] & THRUSTMASTER_SOL_GRIP_FLAG)
{
/*-------------------------------------------------*\
| Send grip packet for thumbstick immediately |
\*-------------------------------------------------*/
unsigned char zone_id = zones[i] & 0xFF;
unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE];
memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
packet[0] = THRUSTMASTER_SOL_REPORT_GRIP_LO;
packet[1] = THRUSTMASTER_SOL_REPORT_GRIP_HI;
packet[2] = persist_flag | 0x01;
packet[3] = THRUSTMASTER_SOL_MARKER;
packet[4] = zone_id;
packet[5] = RGBGetRValue(colors[i]);
packet[6] = RGBGetGValue(colors[i]);
packet[7] = RGBGetBValue(colors[i]);
SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
else
{
base_zones[base_count] = zones[i] & 0xFF;
base_colors[base_count] = colors[i];
base_count++;
}
}
/*---------------------------------------------------------*\
| Send base zone packets, batching up to 15 entries each |
\*---------------------------------------------------------*/
for(unsigned int offset = 0; offset < base_count; offset += THRUSTMASTER_SOL_MAX_ENTRIES)
{
unsigned int entries = base_count - offset;
if(entries > THRUSTMASTER_SOL_MAX_ENTRIES)
{
entries = THRUSTMASTER_SOL_MAX_ENTRIES;
}
unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE];
memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
packet[0] = THRUSTMASTER_SOL_REPORT_BASE_LO;
packet[1] = THRUSTMASTER_SOL_REPORT_BASE_HI;
packet[2] = persist_flag | (unsigned char)entries;
packet[3] = THRUSTMASTER_SOL_MARKER;
for(unsigned int j = 0; j < entries; j++)
{
unsigned int idx = offset + j;
packet[4 + j * 4] = base_zones[idx];
packet[4 + j * 4 + 1] = RGBGetRValue(base_colors[idx]);
packet[4 + j * 4 + 2] = RGBGetGValue(base_colors[idx]);
packet[4 + j * 4 + 3] = RGBGetBValue(base_colors[idx]);
}
SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE);
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
void ThrustmasterSolController::SetLEDColor(unsigned int zone, RGBColor color)
{
BuildAndSendPackets(&zone, &color, 1, false);
}
void ThrustmasterSolController::SetLEDColors(unsigned int* zones,
RGBColor* colors,
unsigned int count)
{
BuildAndSendPackets(zones, colors, count, false);
}
void ThrustmasterSolController::SaveColors(unsigned int* zones,
RGBColor* colors,
unsigned int count)
{
BuildAndSendPackets(zones, colors, count, true);
}
void ThrustmasterSolController::ReadColors(std::vector<unsigned int>& zones,
std::vector<RGBColor>& colors)
{
if(dev == nullptr)
{
return;
}
zones.clear();
colors.clear();
/*---------------------------------------------------------*\
| Read base zones (report 0x0002). May require multiple |
| queries if more than 14 zones are returned per response. |
| Zone IDs are returned without the grip flag, so they |
| match the base LED values (including zone 0x00 = logo). |
\*---------------------------------------------------------*/
unsigned char start = 0;
for(int page = 0; page < 4; page++)
{
unsigned char query[THRUSTMASTER_SOL_PACKET_SIZE];
memset(query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
query[0] = THRUSTMASTER_SOL_READ_BASE_LO;
query[1] = THRUSTMASTER_SOL_READ_BASE_HI;
query[2] = start;
SendPacket(query, THRUSTMASTER_SOL_PACKET_SIZE);
unsigned char resp[THRUSTMASTER_SOL_PACKET_SIZE];
int actual = 0;
int ret = libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_IN,
resp,
THRUSTMASTER_SOL_PACKET_SIZE,
&actual,
1000);
if(ret != LIBUSB_SUCCESS || actual < 4)
{
break;
}
unsigned int n = resp[2] & 0x0F;
if(n == 0)
{
break;
}
unsigned char last = start;
for(unsigned int i = 0; i < n; i++)
{
unsigned int off = 4 + i * 4;
if(off + 3 >= (unsigned int)actual)
{
break;
}
unsigned char zid = resp[off];
unsigned char r = resp[off + 1];
unsigned char g = resp[off + 2];
unsigned char b = resp[off + 3];
zones.push_back(zid);
colors.push_back(ToRGBColor(r, g, b));
last = zid;
}
start = last + 1;
if(n < 14 || start > 0x20)
{
break;
}
}
/*---------------------------------------------------------*\
| Read grip zones (report 0x8002). Grip zone IDs are |
| returned with the GRIP_FLAG set so they match the |
| thumbstick LED value. They are added as new entries, |
| not overwriting base entries (zone 0x00 exists on both). |
\*---------------------------------------------------------*/
unsigned char grip_query[THRUSTMASTER_SOL_PACKET_SIZE];
memset(grip_query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE);
grip_query[0] = THRUSTMASTER_SOL_READ_GRIP_LO;
grip_query[1] = THRUSTMASTER_SOL_READ_GRIP_HI;
SendPacket(grip_query, THRUSTMASTER_SOL_PACKET_SIZE);
unsigned char grip_resp[THRUSTMASTER_SOL_PACKET_SIZE];
int grip_actual = 0;
int grip_ret = libusb_interrupt_transfer(dev,
THRUSTMASTER_SOL_ENDPOINT_IN,
grip_resp,
THRUSTMASTER_SOL_PACKET_SIZE,
&grip_actual,
1000);
if(grip_ret == LIBUSB_SUCCESS && grip_actual >= 8)
{
unsigned int grip_n = grip_resp[2] & 0x0F;
for(unsigned int i = 0; i < grip_n; i++)
{
unsigned int off = 4 + i * 4;
if(off + 3 >= (unsigned int)grip_actual)
{
break;
}
unsigned char zid = grip_resp[off];
unsigned char r = grip_resp[off + 1];
unsigned char g = grip_resp[off + 2];
unsigned char b = grip_resp[off + 3];
zones.push_back(THRUSTMASTER_SOL_GRIP_FLAG | zid);
colors.push_back(ToRGBColor(r, g, b));
}
}
}
@@ -0,0 +1,116 @@
/*---------------------------------------------------------*\
| ThrustmasterSolController.h |
| |
| Driver for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <vector>
#include "RGBController.h"
#ifdef _WIN32
#include "dependencies/libusb-1.0.27/include/libusb.h"
#else
#include <libusb.h>
#endif
/*-----------------------------------------------------*\
| Thrustmaster vendor ID |
\*-----------------------------------------------------*/
#define THRUSTMASTER_VID 0x044F
/*-----------------------------------------------------*\
| Thrustmaster Sol series product IDs |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_R_RIGHT_PID 0x0422
#define THRUSTMASTER_SOL_R_LEFT_PID 0x042A
#define THRUSTMASTER_SOL_F16_RIGHT_PID 0x0420
#define THRUSTMASTER_SOL_F18_RIGHT_PID 0x0421
#define THRUSTMASTER_SOL_F16_LEFT_PID 0x0428
#define THRUSTMASTER_SOL_F18_LEFT_PID 0x0429
/*-----------------------------------------------------*\
| Thrustmaster Sol USB constants |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_INTERFACE 1
#define THRUSTMASTER_SOL_ENDPOINT_OUT 0x02
#define THRUSTMASTER_SOL_ENDPOINT_IN 0x82
#define THRUSTMASTER_SOL_PACKET_SIZE 64
#define THRUSTMASTER_SOL_MAX_ENTRIES 15
/*-----------------------------------------------------*\
| Thrustmaster Sol report type identifiers |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_REPORT_BASE_LO 0x01
#define THRUSTMASTER_SOL_REPORT_BASE_HI 0x08
#define THRUSTMASTER_SOL_REPORT_GRIP_LO 0x01
#define THRUSTMASTER_SOL_REPORT_GRIP_HI 0x88
/*-----------------------------------------------------*\
| Thrustmaster Sol persistence flags |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_VOLATILE 0x00
#define THRUSTMASTER_SOL_PERSISTENT 0x80
/*-----------------------------------------------------*\
| Thrustmaster Sol read query report types |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_READ_BASE_LO 0x02
#define THRUSTMASTER_SOL_READ_BASE_HI 0x00
#define THRUSTMASTER_SOL_READ_GRIP_LO 0x02
#define THRUSTMASTER_SOL_READ_GRIP_HI 0x80
/*-----------------------------------------------------*\
| Thrustmaster Sol zone constants |
| |
| Zone 0x00 is shared: on the base it is a logo LED, |
| on the grip it is the thumbstick LED. The grip flag |
| (bit 8) selects the grip report type for zone 0x00. |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_ZONE_THUMBSTICK 0x00
#define THRUSTMASTER_SOL_GRIP_FLAG 0x100
#define THRUSTMASTER_SOL_MARKER 0xFF
/*-----------------------------------------------------*\
| Thrustmaster Sol zone count for Sol-R |
| 19 base zones + thumbstick + logo zone 0x00 = 21 |
\*-----------------------------------------------------*/
#define THRUSTMASTER_SOL_R_ZONE_COUNT 21
class ThrustmasterSolController
{
public:
ThrustmasterSolController(libusb_device_handle* dev_handle,
const char* path,
unsigned short pid);
~ThrustmasterSolController();
std::string GetDeviceLocation();
std::string GetSerialString();
unsigned short GetPID();
void SetLEDColor(unsigned int zone, RGBColor color);
void SetLEDColors(unsigned int* zones, RGBColor* colors,
unsigned int count);
void SaveColors(unsigned int* zones, RGBColor* colors,
unsigned int count);
void ReadColors(std::vector<unsigned int>& zones,
std::vector<RGBColor>& colors);
private:
libusb_device_handle* dev;
std::string location;
std::string serial;
unsigned short pid;
void SendPacket(unsigned char* packet, unsigned int size);
void BuildAndSendPackets(unsigned int* zones, RGBColor* colors,
unsigned int count, bool persistent);
};
@@ -0,0 +1,125 @@
/*---------------------------------------------------------*\
| ThrustmasterSolControllerDetect.cpp |
| |
| Detector for Thrustmaster Sol series joysticks |
| |
| Ken Sanislo 02 Apr 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "ThrustmasterSolController.h"
#include "RGBController_ThrustmasterSol.h"
typedef struct
{
unsigned short usb_vid;
unsigned short usb_pid;
const char* name;
} thrustmaster_sol_device;
#define THRUSTMASTER_SOL_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[0]))
static const thrustmaster_sol_device device_list[] =
{
/*-----------------------------------------------------------------------------------------------------*\
| Sol-R variants (tested) |
\*-----------------------------------------------------------------------------------------------------*/
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_R_RIGHT_PID, "Thrustmaster Sol-R Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_R_LEFT_PID, "Thrustmaster Sol-R Left" },
/*-----------------------------------------------------------------------------------------------------*\
| Sol F16/F18 variants (untested, same base protocol) |
\*-----------------------------------------------------------------------------------------------------*/
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_RIGHT_PID, "Thrustmaster Sol F16 Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_RIGHT_PID, "Thrustmaster Sol F18 Right" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_LEFT_PID, "Thrustmaster Sol F16 Left" },
{ THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_LEFT_PID, "Thrustmaster Sol F18 Left" },
};
/******************************************************************************************\
* *
* DetectThrustmasterSolControllers *
* *
* Detect Thrustmaster Sol series joysticks via libusb. *
* LED control is on USB interface 1 (vendor-specific class 255, not HID). *
* Interface 0 (joystick HID) is not touched. *
* *
\******************************************************************************************/
void DetectThrustmasterSolControllers()
{
libusb_init(NULL);
#ifdef _WIN32
libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK);
#endif
libusb_device** devs;
ssize_t num_devs = libusb_get_device_list(NULL, &devs);
if(num_devs <= 0)
{
return;
}
for(ssize_t i = 0; i < num_devs; i++)
{
libusb_device_descriptor desc;
if(libusb_get_device_descriptor(devs[i], &desc) != 0)
{
continue;
}
for(std::size_t d = 0; d < THRUSTMASTER_SOL_NUM_DEVICES; d++)
{
if(desc.idVendor == device_list[d].usb_vid &&
desc.idProduct == device_list[d].usb_pid)
{
libusb_device_handle* handle = NULL;
if(libusb_open(devs[i], &handle) != LIBUSB_SUCCESS)
{
continue;
}
libusb_set_auto_detach_kernel_driver(handle, 1);
if(libusb_claim_interface(handle, THRUSTMASTER_SOL_INTERFACE) != LIBUSB_SUCCESS)
{
libusb_close(handle);
continue;
}
uint8_t bus = libusb_get_bus_number(devs[i]);
uint8_t address = libusb_get_device_address(devs[i]);
char path[32];
snprintf(path, sizeof(path), "%d-%d", bus, address);
ThrustmasterSolController* controller = new ThrustmasterSolController(handle, path, desc.idProduct);
RGBController_ThrustmasterSol* rgb_controller = new RGBController_ThrustmasterSol(controller);
rgb_controller->name = device_list[d].name;
ResourceManager::get()->RegisterRGBController(rgb_controller);
break;
}
}
}
libusb_free_device_list(devs, 1);
}
REGISTER_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers);
/*---------------------------------------------------------------------------------------------------------*\
| Entries for dynamic UDEV rules |
| |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0420 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0421 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0422 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0428 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0429 ) |
| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x042A ) |
\*---------------------------------------------------------------------------------------------------------*/
@@ -187,50 +187,21 @@ static const char *led_names[] =
\*---------------------------------------------------------*/
RGBController_XPGSummoner::RGBController_XPGSummoner(XPGSummonerController *controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "XPG";
description = "XPG Summoner Keyboard Device";
location = controller->GetLocationString();
serial = controller->GetSerialString();
type = DEVICE_TYPE_KEYBOARD;
controller = controller_ptr;
name = controller->GetNameString();
vendor = "XPG";
description = "XPG Summoner Keyboard Device";
location = controller->GetLocationString();
serial = controller->GetSerialString();
type = DEVICE_TYPE_KEYBOARD;
mode Direct;
Direct.name = "Direct";
Direct.value = XPG_SUMMONER_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
Direct.name = "Direct";
Direct.value = XPG_SUMMONER_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = XPG_SUMMONER_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.colors_min = 1;
Static.colors_max = 1;
Static.brightness_min = 5;
Static.brightness_max = 64;
Static.colors.resize(1);
modes.push_back(Static);
mode Stars;
Stars.name = "Stars";
Stars.value = XPG_SUMMONER_MODE_STARS;
Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR;
Stars.colors_min = 1;
Stars.colors_max = 1;
Stars.color_mode = MODE_COLORS_MODE_SPECIFIC;
Stars.colors.resize(1);
modes.push_back(Stars);
mode Off;
Off.name = "Off";
Off.value = XPG_SUMMONER_MODE_OFF;
Off.flags = 0;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
@@ -321,7 +292,9 @@ void RGBController_XPGSummoner::DeviceUpdateLEDs()
for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++)
{
if(leds[led_idx].value == NA)
{
continue;
}
if(modes[active_mode].color_mode == MODE_COLORS_PER_LED)
{
std::size_t real_idx = leds[led_idx].value;
@@ -330,76 +303,6 @@ void RGBController_XPGSummoner::DeviceUpdateLEDs()
frame_buf[(real_idx * 4) + 2] = RGBGetGValue(colors[led_idx]);
frame_buf[(real_idx * 4) + 3] = RGBGetBValue(colors[led_idx]);
}
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && modes[active_mode].value == XPG_SUMMONER_MODE_STATIC)
{
std::size_t real_idx = leds[led_idx].value;
frame_buf[(real_idx * 4) + 0] = modes[active_mode].brightness;
frame_buf[(real_idx * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(real_idx * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(real_idx * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && modes[active_mode].value == XPG_SUMMONER_MODE_STARS)
{
for(std::size_t i = 0; i < leds.size(); ++i)
{
std::size_t real_idx = leds[i].value;
frame_buf[(real_idx * 4) + 0] = 0;
frame_buf[(real_idx * 4) + 1] = 0;
frame_buf[(real_idx * 4) + 2] = 0;
frame_buf[(real_idx * 4) + 3] = 0;
}
controller->SendColors(frame_buf, sizeof(frame_buf));
/*---------------------------------------------------*\
| Select a random central LED in the physical matrix |
\*---------------------------------------------------*/
int rows = 6;
int cols = 21;
int center_row = rand() % rows;
int center_col = rand() % cols;
unsigned int center_led = matrix_map[center_row][center_col];
if(center_led != NA)
{
frame_buf[(center_led * 4) + 0] = brightness;
frame_buf[(center_led * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(center_led * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(center_led * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
/*---------------------------------------------------*\
| Neighbors (fade effect) |
\*---------------------------------------------------*/
unsigned char fade_brightness = 0x32;
int neighbor_offsets[4][2] = { {0, -1}, {0, +1}, {-1, 0}, {+1, 0} };
for(int k = 0; k < 4; ++k)
{
int n_row = center_row + neighbor_offsets[k][0];
int n_col = center_col + neighbor_offsets[k][1];
if(n_row >= 0 && n_row < rows && n_col >= 0 && n_col < cols)
{
unsigned int neighbor_led = matrix_map[n_row][n_col];
if(neighbor_led != NA)
{
frame_buf[(neighbor_led * 4) + 0] = fade_brightness;
frame_buf[(neighbor_led * 4) + 1] = RGBGetRValue(modes[active_mode].colors[0]);
frame_buf[(neighbor_led * 4) + 2] = RGBGetGValue(modes[active_mode].colors[0]);
frame_buf[(neighbor_led * 4) + 3] = RGBGetBValue(modes[active_mode].colors[0]);
}
}
}
controller->SendColors(frame_buf, sizeof(frame_buf));
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
else if(modes[active_mode].color_mode == MODE_COLORS_NONE)
{
std::size_t real_idx = leds[led_idx].value;
frame_buf[(real_idx * 4) + 0] = 0;
frame_buf[(real_idx * 4) + 1] = 0;
frame_buf[(real_idx * 4) + 2] = 0;
frame_buf[(real_idx * 4) + 3] = 0;
}
}
controller->SendColors(frame_buf, sizeof(frame_buf));
}
@@ -27,10 +27,7 @@
enum
{
XPG_SUMMONER_MODE_OFF = 0x00,
XPG_SUMMONER_MODE_DIRECT = 0x01,
XPG_SUMMONER_MODE_STATIC = 0x02,
XPG_SUMMONER_MODE_STARS = 0x03
XPG_SUMMONER_MODE_DIRECT = 0x01
};
class XPGSummonerController
@@ -0,0 +1,369 @@
/*---------------------------------------------------------*\
| RGBController_ZotacBlackwellGPU.cpp |
| |
| RGBController for ZOTAC Blackwell (RTX 50 series) GPU |
| |
| Eder Sánchez 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ZotacBlackwellGPU.h"
/**------------------------------------------------------------------*\
@name ZOTAC RTX 50 series GPU
@category GPU
@type I2C
@save :robot:
@direct :x:
@effects :tools:
@detectors DetectZotacBlackwellGPUControllersPCI
@comment
Supports ZOTAC Blackwell (RTX 50 series) GPUs with 3 zones:
Logo, Side Bar, and Infinity Mirror.
The controller uses individual SMBus byte writes (registers
0x20-0x2F) with a 3ms delay between each transaction.
To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`
and detection entries in
`Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp`.
\*-------------------------------------------------------------------*/
RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetName();
vendor = "ZOTAC";
description = "ZOTAC RTX 50 series RGB GPU Device (" + controller->GetVersion() + ")";
location = controller->GetDeviceLocation();
type = DEVICE_TYPE_GPU;
version = controller->GetVersion();
/*---------------------------------------------------------*\
| Static mode |
\*---------------------------------------------------------*/
mode STATIC;
STATIC.name = "Static";
STATIC.value = ZOTAC_BLACKWELL_GPU_MODE_STATIC;
STATIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR;
STATIC.brightness_min = 0;
STATIC.brightness_max = 100;
STATIC.brightness = 100;
STATIC.color_mode = MODE_COLORS_PER_LED;
modes.push_back(STATIC);
/*---------------------------------------------------------*\
| Breathe mode |
\*---------------------------------------------------------*/
mode BREATHE;
BREATHE.name = "Breathe";
BREATHE.value = ZOTAC_BLACKWELL_GPU_MODE_BREATHE;
BREATHE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
BREATHE.brightness_min = 0;
BREATHE.brightness_max = 100;
BREATHE.brightness = 100;
BREATHE.speed_min = 0;
BREATHE.speed_max = 100;
BREATHE.speed = 20;
BREATHE.color_mode = MODE_COLORS_PER_LED;
modes.push_back(BREATHE);
/*---------------------------------------------------------*\
| Fade mode |
\*---------------------------------------------------------*/
mode FADE;
FADE.name = "Fade";
FADE.value = ZOTAC_BLACKWELL_GPU_MODE_FADE;
FADE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED;
FADE.speed_min = 0;
FADE.speed_max = 100;
FADE.speed = 20;
FADE.color_mode = MODE_COLORS_NONE;
modes.push_back(FADE);
/*---------------------------------------------------------*\
| Wink mode |
\*---------------------------------------------------------*/
mode WINK;
WINK.name = "Wink";
WINK.value = ZOTAC_BLACKWELL_GPU_MODE_WINK;
WINK.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
WINK.brightness_min = 0;
WINK.brightness_max = 100;
WINK.brightness = 100;
WINK.speed_min = 0;
WINK.speed_max = 100;
WINK.speed = 20;
WINK.color_mode = MODE_COLORS_PER_LED;
modes.push_back(WINK);
/*---------------------------------------------------------*\
| Glide mode |
\*---------------------------------------------------------*/
mode GLIDE;
GLIDE.name = "Glide";
GLIDE.value = ZOTAC_BLACKWELL_GPU_MODE_GLIDE;
GLIDE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_PER_LED_COLOR;
GLIDE.brightness_min = 0;
GLIDE.brightness_max = 100;
GLIDE.brightness = 100;
GLIDE.speed_min = 0;
GLIDE.speed_max = 100;
GLIDE.speed = 20;
GLIDE.color_mode = MODE_COLORS_PER_LED;
modes.push_back(GLIDE);
/*---------------------------------------------------------*\
| Prism mode (called "Rainbow" in older firmware) |
\*---------------------------------------------------------*/
mode PRISM;
PRISM.name = "Prism";
PRISM.value = ZOTAC_BLACKWELL_GPU_MODE_PRISM;
PRISM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
PRISM.speed_min = 0;
PRISM.speed_max = 100;
PRISM.speed = 20;
PRISM.color_mode = MODE_COLORS_NONE;
modes.push_back(PRISM);
/*---------------------------------------------------------*\
| Bokeh mode |
\*---------------------------------------------------------*/
mode BOKEH;
BOKEH.name = "Bokeh";
BOKEH.value = ZOTAC_BLACKWELL_GPU_MODE_BOKEH;
BOKEH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
BOKEH.brightness_min = 0;
BOKEH.brightness_max = 100;
BOKEH.brightness = 100;
BOKEH.speed_min = 0;
BOKEH.speed_max = 100;
BOKEH.speed = 20;
BOKEH.color_mode = MODE_COLORS_PER_LED;
modes.push_back(BOKEH);
/*---------------------------------------------------------*\
| Beacon mode |
\*---------------------------------------------------------*/
mode BEACON;
BEACON.name = "Beacon";
BEACON.value = ZOTAC_BLACKWELL_GPU_MODE_BEACON;
BEACON.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
BEACON.brightness_min = 0;
BEACON.brightness_max = 100;
BEACON.brightness = 100;
BEACON.speed_min = 0;
BEACON.speed_max = 100;
BEACON.speed = 20;
BEACON.color_mode = MODE_COLORS_PER_LED;
modes.push_back(BEACON);
/*---------------------------------------------------------*\
| Tandem mode |
\*---------------------------------------------------------*/
mode TANDEM;
TANDEM.name = "Tandem";
TANDEM.value = ZOTAC_BLACKWELL_GPU_MODE_TANDEM;
TANDEM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
TANDEM.brightness_min = 0;
TANDEM.brightness_max = 100;
TANDEM.brightness = 100;
TANDEM.speed_min = 0;
TANDEM.speed_max = 100;
TANDEM.speed = 20;
TANDEM.color_mode = MODE_COLORS_PER_LED;
modes.push_back(TANDEM);
/*---------------------------------------------------------*\
| Tidal mode |
\*---------------------------------------------------------*/
mode TIDAL;
TIDAL.name = "Tidal";
TIDAL.value = ZOTAC_BLACKWELL_GPU_MODE_TIDAL;
TIDAL.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_PER_LED_COLOR;
TIDAL.brightness_min = 0;
TIDAL.brightness_max = 100;
TIDAL.brightness = 100;
TIDAL.speed_min = 0;
TIDAL.speed_max = 100;
TIDAL.speed = 20;
TIDAL.color_mode = MODE_COLORS_PER_LED;
modes.push_back(TIDAL);
/*---------------------------------------------------------*\
| Astra mode |
\*---------------------------------------------------------*/
mode ASTRA;
ASTRA.name = "Astra";
ASTRA.value = ZOTAC_BLACKWELL_GPU_MODE_ASTRA;
ASTRA.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
ASTRA.brightness_min = 0;
ASTRA.brightness_max = 100;
ASTRA.brightness = 100;
ASTRA.speed_min = 0;
ASTRA.speed_max = 100;
ASTRA.speed = 20;
ASTRA.color_mode = MODE_COLORS_PER_LED;
modes.push_back(ASTRA);
/*---------------------------------------------------------*\
| Cosmic mode |
\*---------------------------------------------------------*/
mode COSMIC;
COSMIC.name = "Cosmic";
COSMIC.value = ZOTAC_BLACKWELL_GPU_MODE_COSMIC;
COSMIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
COSMIC.brightness_min = 0;
COSMIC.brightness_max = 100;
COSMIC.brightness = 100;
COSMIC.speed_min = 0;
COSMIC.speed_max = 100;
COSMIC.speed = 20;
COSMIC.color_mode = MODE_COLORS_PER_LED;
modes.push_back(COSMIC);
/*---------------------------------------------------------*\
| Volta mode |
\*---------------------------------------------------------*/
mode VOLTA;
VOLTA.name = "Volta";
VOLTA.value = ZOTAC_BLACKWELL_GPU_MODE_VOLTA;
VOLTA.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR;
VOLTA.brightness_min = 0;
VOLTA.brightness_max = 100;
VOLTA.brightness = 100;
VOLTA.speed_min = 0;
VOLTA.speed_max = 100;
VOLTA.speed = 20;
VOLTA.color_mode = MODE_COLORS_PER_LED;
modes.push_back(VOLTA);
SetupZones();
}
RGBController_ZotacBlackwellGPU::~RGBController_ZotacBlackwellGPU()
{
delete controller;
}
void RGBController_ZotacBlackwellGPU::SetupZones()
{
/*---------------------------------------------------------*\
| Zone 0: Logo |
\*---------------------------------------------------------*/
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
led logo_led;
logo_led.name = "Logo LED";
leds.push_back(logo_led);
/*---------------------------------------------------------*\
| Zone 1: Side Bar |
\*---------------------------------------------------------*/
zone sidebar_zone;
sidebar_zone.name = "Side Bar";
sidebar_zone.type = ZONE_TYPE_SINGLE;
sidebar_zone.leds_min = 1;
sidebar_zone.leds_max = 1;
sidebar_zone.leds_count = 1;
sidebar_zone.matrix_map = NULL;
zones.push_back(sidebar_zone);
led sidebar_led;
sidebar_led.name = "Side Bar LED";
leds.push_back(sidebar_led);
/*---------------------------------------------------------*\
| Zone 2: Infinity Mirror |
\*---------------------------------------------------------*/
zone infinity_zone;
infinity_zone.name = "Infinity Mirror";
infinity_zone.type = ZONE_TYPE_SINGLE;
infinity_zone.leds_min = 1;
infinity_zone.leds_max = 1;
infinity_zone.leds_count = 1;
infinity_zone.matrix_map = NULL;
zones.push_back(infinity_zone);
led infinity_led;
infinity_led.name = "Infinity Mirror LED";
leds.push_back(infinity_led);
SetupColors();
}
void RGBController_ZotacBlackwellGPU::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_ZotacBlackwellGPU::DeviceUpdateLEDs()
{
DeviceUpdateMode();
}
void RGBController_ZotacBlackwellGPU::UpdateZoneLEDs(int zone)
{
DeviceUpdateZone(zone);
}
void RGBController_ZotacBlackwellGPU::UpdateSingleLED(int led)
{
DeviceUpdateZone(led);
}
void RGBController_ZotacBlackwellGPU::DeviceUpdateZone(int zone)
{
unsigned int mode_val = modes[active_mode].value;
unsigned int brightness = modes[active_mode].brightness;
unsigned int speed = modes[active_mode].speed;
unsigned int direction = modes[active_mode].direction == MODE_DIRECTION_RIGHT
? ZOTAC_BLACKWELL_GPU_DIR_RIGHT
: ZOTAC_BLACKWELL_GPU_DIR_LEFT;
RGBColor color1;
RGBColor color2 = ToRGBColor(0, 0, 0);
switch(modes[active_mode].color_mode)
{
case MODE_COLORS_PER_LED:
color1 = colors[zone];
break;
case MODE_COLORS_MODE_SPECIFIC:
color1 = (modes[active_mode].colors.size() >= 1)
? modes[active_mode].colors[0]
: ToRGBColor(0, 0, 0);
color2 = (modes[active_mode].colors.size() >= 2)
? modes[active_mode].colors[1]
: ToRGBColor(0, 0, 0);
break;
default:
color1 = ToRGBColor(0, 0, 0);
break;
}
controller->SetMode(zone, mode_val, color1, color2, brightness, speed, direction);
controller->Commit();
}
void RGBController_ZotacBlackwellGPU::DeviceUpdateMode()
{
for(unsigned int zone_idx = 0; zone_idx < ZOTAC_BLACKWELL_GPU_NUM_ZONES; zone_idx++)
{
DeviceUpdateZone(zone_idx);
}
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| RGBController_ZotacBlackwellGPU.h |
| |
| RGBController for ZOTAC Blackwell (RTX 50 series) GPU |
| |
| Eder Sánchez 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ZotacBlackwellGPUController.h"
class RGBController_ZotacBlackwellGPU : public RGBController
{
public:
RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr);
~RGBController_ZotacBlackwellGPU();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceUpdateZone(int zone);
private:
ZotacBlackwellGPUController* controller;
};
@@ -0,0 +1,125 @@
/*---------------------------------------------------------*\
| ZotacBlackwellGPUController.cpp |
| |
| Driver for ZOTAC Blackwell (RTX 50 series) GPU |
| |
| Eder Sánchez 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <thread>
#include "ZotacBlackwellGPUController.h"
#include "LogManager.h"
ZotacBlackwellGPUController::ZotacBlackwellGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name)
{
this->bus = bus;
this->dev = dev;
this->name = dev_name;
ReadVersion();
}
ZotacBlackwellGPUController::~ZotacBlackwellGPUController()
{
}
std::string ZotacBlackwellGPUController::GetDeviceLocation()
{
std::string return_string(bus->device_name);
char addr[5];
snprintf(addr, 5, "0x%02X", dev);
return_string.append(", address ");
return_string.append(addr);
return("I2C: " + return_string);
}
std::string ZotacBlackwellGPUController::GetName()
{
return(name);
}
std::string ZotacBlackwellGPUController::GetVersion()
{
return(version);
}
void ZotacBlackwellGPUController::ReadVersion()
{
/*---------------------------------------------------------*\
| Read version via raw I2C block read. The first bytes |
| returned contain the ASCII version string (e.g. |
| "N762A-2008e"). If the raw read fails or returns empty, |
| fall back to reading register 0x2F as an identifier. |
\*---------------------------------------------------------*/
u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 };
int rdata_len = sizeof(rdata_pkt);
if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) >= 0 && rdata_pkt[0] != 0x00)
{
version = std::string((char*)rdata_pkt);
}
else
{
version = "Unknown";
}
LOG_INFO("[%s] Firmware version: %s", name.c_str(), version.c_str());
}
void ZotacBlackwellGPUController::SetMode
(
unsigned int zone,
unsigned int mode_val,
RGBColor color1,
RGBColor color2,
unsigned int brightness,
unsigned int speed,
unsigned int direction
)
{
/*---------------------------------------------------------*\
| Write all 16 registers 0x20 - 0x2F via individual |
| i2c_smbus_write_byte_data calls with 3ms delay between |
| each transaction. Partial writes are not supported. |
\*---------------------------------------------------------*/
u8 regs[16];
regs[0x00] = 0x00; /* 0x20 - Fixed = 0x00 */
regs[0x01] = (u8)zone; /* 0x21 - Zone index */
regs[0x02] = (u8)mode_val; /* 0x22 - Mode */
regs[0x03] = (u8)RGBGetRValue(color1); /* 0x23 - Red 1 */
regs[0x04] = (u8)RGBGetGValue(color1); /* 0x24 - Green 1 */
regs[0x05] = (u8)RGBGetBValue(color1); /* 0x25 - Blue 1 */
regs[0x06] = (u8)RGBGetRValue(color2); /* 0x26 - Red 2 */
regs[0x07] = (u8)RGBGetGValue(color2); /* 0x27 - Green 2 */
regs[0x08] = (u8)RGBGetBValue(color2); /* 0x28 - Blue 2 */
regs[0x09] = (u8)brightness; /* 0x29 - Brightness (0-100) */
regs[0x0A] = (u8)speed; /* 0x2A - Speed (0-100) */
regs[0x0B] = (u8)direction; /* 0x2B - Direction */
regs[0x0C] = 0x00; /* 0x2C - Reserved */
regs[0x0D] = 0x00; /* 0x2D - Reserved */
regs[0x0E] = 0x00; /* 0x2E - Reserved */
regs[0x0F] = 0x00; /* 0x2F - Reserved */
for(int i = 0; i < 16; i++)
{
bus->i2c_smbus_write_byte_data(dev, (u8)(ZOTAC_BLACKWELL_GPU_REG_FIXED + i), regs[i]);
std::this_thread::sleep_for(std::chrono::microseconds(ZOTAC_BLACKWELL_GPU_DELAY_US));
}
}
void ZotacBlackwellGPUController::Commit()
{
/*---------------------------------------------------------*\
| Write commit register to apply staged changes. |
| A longer delay is needed after commit to allow the |
| firmware to process the change — without this, some |
| effects don't visually update until a parameter changes. |
\*---------------------------------------------------------*/
bus->i2c_smbus_write_byte_data(dev, ZOTAC_BLACKWELL_GPU_REG_COMMIT, 0x01);
std::this_thread::sleep_for(std::chrono::microseconds(ZOTAC_BLACKWELL_GPU_COMMIT_DELAY_US));
}
@@ -0,0 +1,109 @@
/*---------------------------------------------------------*\
| ZotacBlackwellGPUController.h |
| |
| Driver for ZOTAC Blackwell (RTX 50 series) GPU |
| |
| Eder Sánchez 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
#include "RGBController.h"
/*---------------------------------------------------------*\
| ZOTAC Blackwell I2C address |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_ADDR 0x4B
/*---------------------------------------------------------*\
| Register map (0x20 - 0x2F) |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_REG_FIXED 0x20
#define ZOTAC_BLACKWELL_GPU_REG_ZONE 0x21
#define ZOTAC_BLACKWELL_GPU_REG_MODE 0x22
#define ZOTAC_BLACKWELL_GPU_REG_RED1 0x23
#define ZOTAC_BLACKWELL_GPU_REG_GREEN1 0x24
#define ZOTAC_BLACKWELL_GPU_REG_BLUE1 0x25
#define ZOTAC_BLACKWELL_GPU_REG_RED2 0x26
#define ZOTAC_BLACKWELL_GPU_REG_GREEN2 0x27
#define ZOTAC_BLACKWELL_GPU_REG_BLUE2 0x28
#define ZOTAC_BLACKWELL_GPU_REG_BRIGHTNESS 0x29
#define ZOTAC_BLACKWELL_GPU_REG_SPEED 0x2A
#define ZOTAC_BLACKWELL_GPU_REG_DIRECTION 0x2B
#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2C 0x2C
#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2D 0x2D
#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2E 0x2E
#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2F 0x2F
/*---------------------------------------------------------*\
| Control registers |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_REG_RELOAD 0x11
#define ZOTAC_BLACKWELL_GPU_REG_COMMIT 0x17
/*---------------------------------------------------------*\
| Zone indices |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_ZONE_LOGO 0x00
#define ZOTAC_BLACKWELL_GPU_ZONE_SIDEBAR 0x01
#define ZOTAC_BLACKWELL_GPU_ZONE_INFINITY 0x02
#define ZOTAC_BLACKWELL_GPU_NUM_ZONES 3
/*---------------------------------------------------------*\
| Mode values (from Firestorm V5.0.0.012E reverse eng.) |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_MODE_STATIC 0x01
#define ZOTAC_BLACKWELL_GPU_MODE_BREATHE 0x02
#define ZOTAC_BLACKWELL_GPU_MODE_FADE 0x03
#define ZOTAC_BLACKWELL_GPU_MODE_WINK 0x04
#define ZOTAC_BLACKWELL_GPU_MODE_GLIDE 0x08
#define ZOTAC_BLACKWELL_GPU_MODE_PRISM 0x09
#define ZOTAC_BLACKWELL_GPU_MODE_BOKEH 0x0A
#define ZOTAC_BLACKWELL_GPU_MODE_BEACON 0x0B
#define ZOTAC_BLACKWELL_GPU_MODE_TANDEM 0x18
#define ZOTAC_BLACKWELL_GPU_MODE_TIDAL 0x19
#define ZOTAC_BLACKWELL_GPU_MODE_ASTRA 0x20
#define ZOTAC_BLACKWELL_GPU_MODE_COSMIC 0x21
#define ZOTAC_BLACKWELL_GPU_MODE_VOLTA 0x22
/*---------------------------------------------------------*\
| Direction values |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_DIR_LEFT 0x00
#define ZOTAC_BLACKWELL_GPU_DIR_RIGHT 0x01
/*---------------------------------------------------------*\
| I2C transaction delays (microseconds) |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_DELAY_US 3000
#define ZOTAC_BLACKWELL_GPU_COMMIT_DELAY_US 10000
class ZotacBlackwellGPUController
{
public:
ZotacBlackwellGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name);
~ZotacBlackwellGPUController();
std::string GetDeviceLocation();
std::string GetName();
std::string GetVersion();
void SetMode(unsigned int zone, unsigned int mode_val,
RGBColor color1, RGBColor color2,
unsigned int brightness, unsigned int speed,
unsigned int direction);
void Commit();
private:
i2c_smbus_interface* bus;
u8 dev;
std::string name;
std::string version;
void ReadVersion();
};
@@ -0,0 +1,43 @@
/*---------------------------------------------------------*\
| ZotacBlackwellGPUControllerDetect.cpp |
| |
| Detector for ZOTAC Blackwell (RTX 50 series) GPU |
| |
| Eder Sánchez 27 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "ZotacBlackwellGPUController.h"
#include "RGBController_ZotacBlackwellGPU.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
/******************************************************************************************\
* *
* DetectZotacBlackwellGPUControllersPCI *
* *
* Detect ZOTAC Blackwell (RTX 50 series) RGB controllers on the enumerated *
* I2C busses at address 0x4B. *
* *
* bus - pointer to i2c_smbus_interface where RGB device is connected *
* dev - I2C address of RGB device *
* *
\******************************************************************************************/
void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name)
{
s32 result = bus->i2c_smbus_read_byte_data(i2c_addr, 0x10);
if(result >= 0)
{
ZotacBlackwellGPUController* controller = new ZotacBlackwellGPUController(bus, i2c_addr, name);
RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5080 AMP Extreme INFINITY", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, 0x4B);
+24
View File
@@ -0,0 +1,24 @@
/*---------------------------------------------------------*\
| MathUtils.cpp |
| |
| Math utility functions |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <math.h>
#include "MathUtils.h"
int MathUtils::IntInterpolate(int y0, int y1, int x0, int x1, int x)
{
if(x1 == x0)
return y0;
if(y0 == y1)
return y0;
double t = (double)(x - x0) / (double)(x1 - x0);
double y = y0 * (1.0 - t) + y1 * t;
return (int)round(y);
}

Some files were not shown because too many files have changed in this diff Show More