Compare commits

...
Author SHA1 Message Date
Adam Honse f15c3c1420 Add Redragon M921 2026-06-07 14:08:34 -05:00
Adam Honse aa67045330 Fix duplicate entries in SteelSeriesControllerDetect 2026-06-07 13:28:46 -05:00
Alexey Kopytko 736640f38d Add support for Zotac RTX 5090 SOLID OC 2026-06-07 13:21:29 -05:00
David Glushkov e9caf663bc Fix MSILaptopController ALC zone mapping and packet initialization 2026-06-07 13:09:19 -05:00
Daniel Clark 8afbea53de Update RGBFusion2USB Driver. 2026-06-07 01:13:03 -05:00
metroite c3d496ad5b Initial commit for SteelSeries Apex Pro TKL Gen 3 2026-06-07 01:09:30 -05:00
ilyaKhalidov88 06501f528d Add support for ASUS ROG Astral 5090 2026-06-07 00:46:27 -05:00
Jerry Fan f6a678c1a4 Add support for RealtekARGBController 2026-06-05 01:03:29 -05:00
Andy Herbert 30690d0ebd New device- MSI MAG272CQR monitor 2026-06-04 11:54:51 -05:00
Kyle Finter 7a830cddf9 Added new Govee devices H612F and H607C 2026-06-04 07:27:02 +00:00
Adam Honse 3dc1365d39 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 22:07:17 -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
141 changed files with 16770 additions and 5367 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,7 +469,9 @@ 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", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2, 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);
REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING, 0x67);
@@ -502,6 +502,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);
File diff suppressed because it is too large Load Diff
@@ -16,7 +16,7 @@
#include <map>
#include "RGBController.h"
#define GB_FUSION2_ZONES_MAX 8
#define GB_FUSION2_ZONES_MAX 12
/*--------------------------------------------------------*\
| Base LED mappings found on all controllers. |
@@ -89,6 +89,23 @@ const FwdLedHeaders LedLookup =
\*-------------------------------------------------*/
};
/*--------------------------------------------------------*\
| The layout_id masks for supported effects. |
\*--------------------------------------------------------*/
enum GB_LID_EFFECTS_MASKS : uint32_t
{
GB_EFF_BREATH = 0x001,
GB_EFF_BEAT = 0x002,
GB_EFF_CYCLE = 0x004,
GB_EFF_FLASH = 0x008,
GB_EFF_RANDOM = 0x010,
GB_EFF_WAVE = 0x020,
GB_EFF_DFLASH = 0x040,
GB_EFF_WAVE1 = 0x080,
GB_EFF_WAVE2 = 0x100,
GB_EFF_CORE_MASK = 0x1FF
};
typedef struct
{
GB_FUSION2_LED_IDX idx;
@@ -95,10 +95,9 @@ bool RGBFusion2USBController::RefreshHardwareInfo()
return false;
}
IT8297Report report;
std::memcpy(&report, buffer, sizeof(IT8297Report));
report_loaded = true;
device_num = report.device_num;
description = std::string(report.str_product, 28);
if(std::string::iterator nul = std::find(description.begin(), description.end(), '\0');
nul != description.end())
@@ -318,7 +317,7 @@ bool RGBFusion2USBController::SetCalibration(const EncodedCalibration& cal, bool
cal_data.mainboard = desired.c.rgb_cali;
cal_data.spare[0] = desired.c.c_spare0;
cal_data.spare[1] = desired.c.c_spare1;
if(product_id == 0x5711)
{
cal_data.dled[2] = desired.c.d_strip_c2;
@@ -362,7 +361,6 @@ void RGBFusion2USBController::SetLedCount(unsigned int c0, unsigned int c1, unsi
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
{
static bool first_call = true;
int bitmask = 0;
if(hdr == -1)
@@ -392,16 +390,16 @@ bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable)
}
}
int new_effect_disabled = enable
? (effect_disabled & ~bitmask)
: (effect_disabled | bitmask);
int base_mask = (effect_disabled < 0) ? 0 : effect_disabled;
int new_effect_disabled = enable
? (base_mask & ~bitmask)
: (base_mask | bitmask);
if(!first_call && new_effect_disabled == effect_disabled)
// Skip redundant writes only after we have synchronized at least once
if(effect_disabled >= 0 && new_effect_disabled == effect_disabled)
{
return true;
}
first_call = false;
effect_disabled = new_effect_disabled;
int res = SendCCReport(0x32, effect_disabled);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
@@ -439,7 +437,7 @@ bool RGBFusion2USBController::EnableLampArray(bool enable)
{
return SendCCReport(0x48, enable ? 1 : 0);
}
std::string RGBFusion2USBController::GetDeviceName()
{
return(name);
@@ -703,3 +701,115 @@ void RGBFusion2USBController::ResetController()
}
ApplyEffect(true);
}
/*---------------------------------------------------------*\
| Check controller for gen2 ARGB support |
| Checks for supported device number |
| Then checks for supported controllers |
| Then checks for supported feature bit (SaveLEDState) |
| Finally checks for strip detection value. |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::SupportsGen2() const
{
bool supports_gen2 = false;
supports_gen2 = (device_num == 0x00)
&& (product_id == 0x5702 || product_id==0x5711 || product_id==0x8950)
&& (report.support_cmd_flag & 0x01)
&& (report.strip_detect == 0x01);
return supports_gen2;
}
std::vector<Gen2StripInfo> RGBFusion2USBController::ExportGen2Strips() const
{
size_t count = (product_id == 0x5711) ? 4u : 2u;
std::vector<Gen2StripInfo> out;
out.reserve(count);
for(size_t i = 0; i < count; ++i)
{
out.push_back(g2_strip_info[i]);
}
return out;
}
/*---------------------------------------------------------*\
| Scan Headers for Gen2 Devices |
\*---------------------------------------------------------*/
bool RGBFusion2USBController::ScanGen2Strips()
{
for(unsigned i = 0; i < 4; ++i)
{
if(g2_strip_info[i].LedsOfStrip.capacity() < 15)
{
g2_strip_info[i].LedsOfStrip.reserve(15);
}
g2_strip_info[i].numStrip = 0;
g2_strip_info[i].totalLeds = 0;
g2_strip_info[i].LedsOfStrip.resize(0);
}
const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u;
for(unsigned int slot = 0; slot < hdr_lim; ++slot)
{
static constexpr uint8_t delta[4] = {4, 5, 0, 1};
uint8_t scan_cmd = static_cast<uint8_t>(GEN2_LED_BASE_SCAN + delta[slot]);
uint8_t info_cmd = static_cast<uint8_t>(scan_cmd + 2);
if(!SendCCReport(scan_cmd, 0x00, 0x00))
{
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(700));
if(!SendCCReport(info_cmd, 0x00, 0x00))
{
return false;
}
unsigned char feature_buf[64] = {0};
feature_buf[0] = report_id;
int recv_len = hid_get_feature_report(dev, feature_buf, sizeof(feature_buf));
if(recv_len < 64)
{
return false;
}
int seg_count = static_cast<int>(feature_buf[1]);
if(seg_count < 0)
{
seg_count = 0;
}
if(seg_count > 15)
{
seg_count = 15;
}
Gen2StripInfo& dst = g2_strip_info[slot];
dst.numStrip = static_cast<uint8_t>(seg_count);
dst.LedsOfStrip.resize(static_cast<size_t>(seg_count));
uint32_t total_leds = 0;
const int counts_base = 2;
for(int k = 0; k < seg_count; ++k)
{
const int off = counts_base + (k * 2);
uint16_t lo = static_cast<uint16_t>(feature_buf[off + 0]);
uint16_t hi = static_cast<uint16_t>(feature_buf[off + 1]);
uint16_t cnt = static_cast<uint16_t>(lo | (hi << 8));
dst.LedsOfStrip[static_cast<size_t>(k)] = cnt;
total_leds += cnt;
}
dst.totalLeds = total_leds;
SetLedCount(0, 0, 0, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
SaveLEDState(false);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
return true;
}
@@ -21,6 +21,11 @@
#define FUSION2_USB_BUFFER_SIZE 64
/*--------------------------------------------------------*\
| Gen2 scan/info opcode base |
\*--------------------------------------------------------*/
#define GEN2_LED_BASE_SCAN 0x38
/*---------------------------------------------------------*\
| Effects mode list |
\*---------------------------------------------------------*/
@@ -83,6 +88,16 @@ struct EncodedCalibration
std::string mainboard;
};
/*---------------------------------------------------------*\
| High level struct to contain Gen2 Header Data |
\*---------------------------------------------------------*/
struct Gen2StripInfo
{
uint8_t numStrip = 0;
std::vector<uint16_t> LedsOfStrip;
uint32_t totalLeds = 0;
};
#pragma pack(push, 1)
/*---------------------------------------------------------*\
@@ -321,6 +336,8 @@ public:
void SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color);
bool SetStripBuiltinEffectState(int hdr, bool enable);
void SetStripColors(unsigned int hdr, RGBColor * colors, unsigned int num_colors, int single_led = -1);
bool SupportsGen2() const;
bool ScanGen2Strips();
EncodedCalibration GetCalibration(bool refresh_from_hw = false);
std::string GetDeviceName();
@@ -330,6 +347,8 @@ public:
std::string GetFWVersion();
uint16_t GetProductID();
std::string GetSerial();
std::vector<Gen2StripInfo> ExportGen2Strips() const;
Gen2StripInfo g2_strip_info[4];
private:
std::string DecodeCalibrationBuffer(uint32_t value) const;
@@ -345,7 +364,7 @@ private:
int SendPacket(unsigned char* packet);
hid_device* dev;
int device_num;
uint8_t device_num;
uint16_t product_id;
uint32_t effect_zone_mask = 0;
int mode;
@@ -356,7 +375,7 @@ private:
std::string location;
std::string version;
std::string chip_id;
int effect_disabled = 0;
int effect_disabled = -1;
int report_id = 0xCC;
bool report_loaded = false;
bool cali_loaded = false;
@@ -25,7 +25,7 @@
@effects :white_check_mark:
@detectors DetectGigabyteRGBFusion2USBControllers
@comment The Fusion 2 USB controller applies to most AMD and
Intel mainboards from the x570 and z390 chipsets onwards.
Intel mainboards from the X570 and z390 chipsets onwards.
\*-------------------------------------------------------------------*/
RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController* controller_ptr, std::string detector)
@@ -39,8 +39,10 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
version = controller->GetFWVersion();
location = controller->GetDeviceLocation();
serial = controller->GetSerial();
product_id = controller->GetProductID();
device_num = controller->GetDeviceNum();
mode Direct;
Direct.name = "Direct";
Direct.value = 0xFFFF;
@@ -206,6 +208,13 @@ RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController
RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
{
// Free any zones we allocated for the per-instance layout
for(gb_fusion2_zone* z : allocated_zones)
{
delete z;
}
allocated_zones.clear();
delete controller;
}
@@ -214,94 +223,125 @@ RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB()
\*---------------------------------------------------------*/
void RGBController_RGBFusion2USB::Init_Controller()
{
const std::string SectionCustom = "CustomLayout";
const gb_fusion2_device* src_layout = gb_fusion2_device_list[device_index];
const std::string SectionGen2 = "Gigabyte-Gen2-ARGB";
const std::string SectionCustomBase = "CustomLayout";
const std::string SectionCustom = SectionCustomBase + std::to_string(device_num);
const std::string SectionCalibration = "Calibration";
RvrseLedHeaders ReverseLedLookup = reverse_map(LedLookup);
SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager();
nlohmann::json device_settings = settings_manager->GetSettings(detector_name);
/*---------------------------------------------------------*\
| Create the custom layout from the generic_device |
| Checks for Gen2 support and adds flag to json. |
\*---------------------------------------------------------*/
gb_fusion2_device* layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[device_index]);
if(controller->SupportsGen2())
{
if(!device_settings.contains(SectionGen2))
{
device_settings[SectionGen2]["Enabled"] = false;
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
supports_gen2 = device_settings[SectionGen2]["Enabled"];
if(supports_gen2)
{
controller->ScanGen2Strips();
}
}
/*---------------------------------------------------------*\
| Create the custom layout from the generic layout |
\*---------------------------------------------------------*/
switch(product_id)
{
case 0x8950:
src_layout = gb_fusion2_device_list[device_index + 1];
break;
case 0x5711:
src_layout = gb_fusion2_device_list[device_index + 2];
break;
default:
break;
}
if(!device_settings.contains(SectionCustom))
{
device_settings[SectionCustom]["Enabled"] = false;
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(layout, ReverseLedLookup);
device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(src_layout, ReverseLedLookup);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
bool custom_layout = device_settings[SectionCustom]["Enabled"];
if(custom_layout)
{
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, layout);
}
EncodedCalibration hw_cal = controller->GetCalibration(false);
if(!device_settings.contains(SectionCalibration))
if(device_num == 0)
{
device_settings[SectionCalibration]["Enabled"] = false;
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
else
{
nlohmann::json& cal_sec = device_settings[SectionCalibration];
bool cal_enable = cal_sec.value("Enabled", false);
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
if(!device_settings.contains(SectionCalibration))
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
device_settings[SectionCalibration]["Enabled"] = false;
device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
else
{
nlohmann::json& cdata = cal_sec["Data"];
FillMissingWith(cdata, hw_cal);
nlohmann::json& cal_sec = device_settings[SectionCalibration];
bool cal_enable = cal_sec.value("Enabled", false);
if(!cal_enable)
if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object())
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
}
if(cal_enable)
{
const nlohmann::json& cdata = cal_sec["Data"];
EncodedCalibration desired;
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
if(controller->GetProductID() == 0x5711)
{
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
}
else
{
desired.dled[2] = "OFF";
desired.dled[3] = "OFF";
desired.spare[2] = "OFF";
desired.spare[3] = "OFF";
nlohmann::json& cdata = cal_sec["Data"];
FillMissingWith(cdata, hw_cal);
if(!cal_enable)
{
cal_sec["Data"] = WriteCalJsonFrom(hw_cal);
settings_manager->SetSettings(detector_name, device_settings);
settings_manager->SaveSettings();
}
}
if(cal_enable)
{
const nlohmann::json& cdata = cal_sec["Data"];
EncodedCalibration desired;
desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1");
desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2");
desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard");
desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0");
desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1");
if(controller->GetProductID() == 0x5711)
{
desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3");
desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4");
desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2");
desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3");
}
else
{
desired.dled[2] = "OFF";
desired.dled[3] = "OFF";
desired.spare[2] = "OFF";
desired.spare[3] = "OFF";
}
controller->SetCalibration(desired, false);
}
controller->SetCalibration(desired, false);
}
}
/*---------------------------------------------------------------------*\
| When no match found the first entry (generic_device) will be used |
| otherwise look up channel map based on device name |
@@ -315,35 +355,81 @@ void RGBController_RGBFusion2USB::Init_Controller()
\*-----------------------------------------------------------------*/
for(unsigned int i = 0; i < GB_FUSION2_DEVICE_COUNT; i++)
{
if(gb_fusion2_device_list[i]->name == name)
if(gb_fusion2_device_list[i]->name == name &&
gb_fusion2_device_list[i]->device_num == device_num)
{
/*---------------------------------------------------------*\
| Set device ID |
\*---------------------------------------------------------*/
device_index = i;
layout = const_cast<gb_fusion2_device*>(gb_fusion2_device_list[i]);
src_layout = gb_fusion2_device_list[i];
break;
}
}
}
/*---------------------------------------------------------------------*\
| Creates per instance copy of layouts. |
\*---------------------------------------------------------------------*/
instance_layout.zones = &instance_zones;
instance_layout.layout_id = src_layout->layout_id;
instance_layout.device_num = src_layout->device_num;
instance_layout.name = src_layout->name;
for(uint8_t zi = 0; zi < GB_FUSION2_ZONES_MAX; ++zi)
{
(*instance_layout.zones)[zi] = (*src_layout->zones)[zi];
}
if(custom_layout)
{
LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, &instance_layout);
}
/*---------------------------------------------------------------------*\
| Culls the mode support based on layout_id. |
\*---------------------------------------------------------------------*/
const uint32_t effect_mask = instance_layout.layout_id & GB_EFF_CORE_MASK;
modes.erase(std::remove_if(modes.begin(), modes.end(),
[effect_mask](const mode& m)
{
if(m.value == 0xFFFF /* Direct */) { return false; }
if(m.value == EFFECT_STATIC) { return false; }
uint32_t bit = 0u;
switch(m.value)
{
case EFFECT_PULSE: bit = GB_EFF_BREATH; break;
case EFFECT_COLORCYCLE: bit = GB_EFF_CYCLE; break;
case EFFECT_BLINKING: bit = GB_EFF_FLASH; break;
case EFFECT_RANDOM: bit = GB_EFF_RANDOM; break;
case EFFECT_WAVE: bit = GB_EFF_WAVE; break;
case EFFECT_DFLASH: bit = GB_EFF_DFLASH; break;
case EFFECT_WAVE1: bit = GB_EFF_WAVE1; break;
case EFFECT_WAVE2: bit = GB_EFF_WAVE2; break;
case EFFECT_WAVE3: bit = GB_EFF_WAVE1; break;
case EFFECT_WAVE4: bit = GB_EFF_WAVE2; break;
default: bit = 0u; break;
}
return (bit == 0u) || ((effect_mask & bit) == 0u);
}),
modes.end());
/*---------------------------------------------------------*\
| Iterate through layout and process each zone |
\*---------------------------------------------------------*/
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
{
if(!layout->zones[0][zone_idx])
if(!(*instance_layout.zones)[zone_idx])
{
continue;
}
const gb_fusion2_zone* zone_at_idx = layout->zones[0][zone_idx];
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
zone new_zone;
new_zone.name = zone_at_idx->name;
new_zone.leds_min = zone_at_idx->leds_min;
new_zone.leds_max = zone_at_idx->leds_max;
new_zone.leds_count = new_zone.leds_min;
new_zone.type = (new_zone.leds_min == new_zone.leds_max) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
new_zone.type = ((new_zone.leds_min == 1) && (new_zone.leds_max == 1)) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR;
new_zone.matrix_map = NULL;
zones.emplace_back(new_zone);
}
@@ -363,17 +449,68 @@ void RGBController_RGBFusion2USB::SetupZones()
| Set up zones (Fixed so as to not spam the controller) |
\*---------------------------------------------------------*/
std::vector<Gen2StripInfo> strips = controller->ExportGen2Strips();
for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++)
{
const gb_fusion2_zone* zone_at_idx = gb_fusion2_device_list[device_index]->zones[0][zone_idx];
const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx];
if(!zone_at_idx)
{
continue;
}
bool single_zone = (zone_at_idx->leds_min == zone_at_idx->leds_max);
bool single_zone = ((zone_at_idx->leds_min == 1) && (zone_at_idx->leds_max == 1));
if(!single_zone)
{
if(supports_gen2)
{
int slot = 0;
switch(zone_at_idx->idx)
{
case LED4:
case HDR_D_LED2:
slot = 1;
break;
case HDR_D_LED3:
slot = 2;
break;
case HDR_D_LED4:
slot = 3;
break;
default:
break;
}
if(slot >= 0 && static_cast<size_t>(slot) < strips.size())
{
const Gen2StripInfo& info = strips[static_cast<size_t>(slot)];
if(info.totalLeds > 0u)
{
zones[zone_idx].leds_count = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].leds_min = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].leds_max = static_cast<unsigned int>(info.totalLeds);
zones[zone_idx].segments.clear();
zones[zone_idx].segments.reserve(info.LedsOfStrip.size());
unsigned int offset = 0;
for(size_t si = 0; si < info.LedsOfStrip.size(); ++si)
{
const uint16_t cnt = info.LedsOfStrip[si];
if(cnt == 0) continue;
segment seg;
seg.name = std::string("Segment ") + std::to_string(si);
seg.type = ZONE_TYPE_LINEAR;
seg.start_idx = offset;
seg.leds_count = static_cast<unsigned int>(cnt);
zones[zone_idx].segments.push_back(seg);
offset += static_cast<unsigned int>(cnt);
}
}
}
}
switch(zone_at_idx->idx)
{
case LED4:
@@ -826,6 +963,7 @@ void RGBController_RGBFusion2USB::LoadCustomLayoutFromJson(
&& new_zone->idx <= GB_FUSION2_LED_IDX::LED11)
{
layout->zones[0][zone_idx] = new_zone;
allocated_zones.push_back(new_zone);
}
else
{
@@ -62,17 +62,26 @@ private:
std::string detector_name;
RGBFusion2USBController* controller;
int device_num;
uint8_t device_num;
RGBColor null_color = 0;
bool supports_gen2 = 0;
/*---------------------------------------------------------*\
| The intial value of device_index should point to the |
| layout for the generic_device |
\*---------------------------------------------------------*/
uint32_t device_index = 0;
uint16_t product_id = 0;
uint32_t effects_mask = 0;
void Init_Controller();
int GetLED_Zone(int led_idx);
/*---------------------------------------------------------*\
| Per instance layout lookup tables. |
\*---------------------------------------------------------*/
gb_fusion2_device instance_layout{};
gb_fusion2_layout instance_zones{};
std::vector<gb_fusion2_zone*> allocated_zones;
nlohmann::json WriteCalJsonFrom(
const EncodedCalibration& src);
void FillMissingWith(
@@ -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);
@@ -20,6 +20,8 @@ static std::map<std::string, unsigned int> govee_led_counts
{
{ "H619A", 20 },
{ "H70B1", 20 },
{ "H612F", 12 },
{ "H607C", 174 },
};
RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr)
@@ -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,95 @@
/*---------------------------------------------------------*\
| 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;
// Fill unused LED IDs with 0xFF so they are ignored by the controller
for(int i = 0; i < (MSI_LAPTOP_PACKET_SIZE - MSI_LAPTOP_PAYLOAD_OFFSET) / 4; i++)
{
buf[MSI_LAPTOP_PAYLOAD_OFFSET + (i * 4)] = 0xFF;
}
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,235 @@
/*---------------------------------------------------------*\
| 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 3
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 },
};
/*---------------------------------------------------------*\
| Note on Raider A18 HX ALC (Lightbar) LED mappings: |
| Initial reverse engineering packet captures showed 6 zones|
| being updated (indexes 0x00 to 0x05). |
| However, indexes 0x04 (L4) and 0x05 (L5) are "dummy" zones|
| on this specific model and do not correspond to physical |
| LEDs. To avoid user confusion, they are omitted from the |
| UI profile here. |
| The SetLEDs function handles zero-padding the unused |
| payload slots with 0xFF so that omitting these zones |
| doesn't accidentally overwrite the 0x00 index (L1). |
\*---------------------------------------------------------*/
static const msi_laptop_led msi_raider_a18_alc_leds[] =
{
{ "Lightbar 1", 0x00 },
{ "Lightbar 2", 0x01 },
{ "Lightbar 3", 0x02 },
{ "Logo", 0x03 },
};
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("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID);
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;
};
@@ -0,0 +1,121 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 4 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string.h>
#include "MSIMonitorController.h"
#include "StringUtils.h"
MSIMonitorController::MSIMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name)
{
dev = dev_handle;
location = info.path;
name = dev_name;
}
MSIMonitorController::~MSIMonitorController()
{
hid_close(dev);
}
std::string MSIMonitorController::GetDeviceLocation()
{
return("HID: " + location);
}
std::string MSIMonitorController::GetNameString()
{
return(name);
}
std::string MSIMonitorController::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 MSIMonitorController::Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit)
{
/*---------------------------------------------------------*\
| Prepare color data |
\*---------------------------------------------------------*/
uint8_t data[MSI_MONITOR_PACKET_SIZE];
memset(data, 0x00, MSI_MONITOR_PACKET_SIZE);
unsigned int offset = 0;
data[offset++] = 0x71;
data[offset++] = 0x01;
for(int i = 0; i < 3; i++)
{
data[offset++] = 0x00;
}
data[offset++] = 0x01;
data[offset++] = 0x64;
for(int i = 0; i < 5; i++)
{
data[offset++] = 0x00;
}
/*---------------------------------------------------------*\
| put mode_value |
\*---------------------------------------------------------*/
data[offset++] = mode_value;
for(int i = 0; i < 3; i++)
{
data[offset++] = 0x00;
}
data[offset++] = 0x01;
data[offset++] = 0x64;
for(int i = 0; i < 5; i++)
{
data[offset++] = 0x00;
}
/*-----------------------------------------------------------------------*\
| this data looks like placeholder for additional LEDs in other monitors |
\*-----------------------------------------------------------------------*/
for(int i = 0; i < 9; i++)
{
data[offset++] = 0xff;
data[offset++] = 0x00;
data[offset++] = 0x00;
}
//RGB values begin
for(const RGBColor color: colors)
{
data[offset++] = RGBGetRValue(color);
data[offset++] = RGBGetGValue(color);
data[offset++] = RGBGetBValue(color);
}
/*-------------------------------------------------------------------------------*\
| last bit is probably a write to device bit- 0x01 saves to device, 0x00 doesn't. |
| For direct mode, bit is set to 0x00, otherwise lights will flicker |
\*-------------------------------------------------------------------------------*/
data[offset++] = last_bit;
/*---------------------------------------------------------*\
| Send the data (1 packet) |
\*---------------------------------------------------------*/
hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE);
}
@@ -0,0 +1,53 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| |
| 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"
#define MSI_MONITOR_LEDS 9
#define MSI_MONITOR_PACKET_SIZE 78
enum
{
MSI_MONITOR_OFF_MODE_VALUE = 0x00,
MSI_MONITOR_STATIC_MODE_VALUE = 0x01,
MSI_MONITOR_BREATHING_MODE_VALUE = 0x02,
MSI_MONITOR_FLASHING_MODE_VALUE = 0x03,
MSI_MONITOR_LIGHTNING_MODE_VALUE = 0x05,
MSI_MONITOR_MARQUEE_MODE_VALUE = 0x06,
MSI_MONITOR_METEOR_MODE_VALUE = 0x08,
MSI_MONITOR_RAINBOW_MODE_VALUE = 0x1A,
MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F
};
class MSIMonitorController
{
public:
MSIMonitorController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name);
~MSIMonitorController();
std::string GetDeviceLocation();
std::string GetFirmwareVersion();
std::string GetNameString();
std::string GetSerialString();
void Set(uint8_t mode_value, const std::vector<RGBColor> colors, uint8_t last_bit);
private:
hid_device *dev;
std::string description;
std::string location;
std::string name;
std::string version;
};
@@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| MSIKeyboardControllerDetect.cpp |
| |
| Detector for MSI monitor (MSI Gaming Controller) |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "Detector.h"
#include "MSIMonitorController.h"
#include "RGBController_MSIMonitor.h"
#define MSI_USB_VID 0x1462
#define MSI_USB_PID 0x3FA4
/*----------------------------------------------------------*\
| |
| DetectMSIMonitorController |
| |
| Detect MSI monitor and maybe others |
| |
\*----------------------------------------------------------*/
static void DetectMSIMonitorController(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
MSIMonitorController* controller = new MSIMonitorController(dev, *info, name);
RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_HID_DETECTOR_IPU("MSI Monitor (Gaming Controller)", DetectMSIMonitorController, MSI_USB_VID, MSI_USB_PID, 0, 0x01, 0);
@@ -0,0 +1,171 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 June 1 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <chrono>
#include <thread>
#include "RGBController_MSIMonitor.h"
using namespace std::chrono_literals;
/**------------------------------------------------------------------*\
@name MSIMonitor
@category Accessory
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectMSIMonitorController
@comment Developed with MSI MAG272CQR
\*-------------------------------------------------------------------*/
RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetNameString();
vendor = "MSI";
type = DEVICE_TYPE_MONITOR;
description = "MSI Monitor (Gaming Controller)";
location = controller->GetDeviceLocation();
serial = controller->GetSerialString();
mode Direct;
Direct.name = "Direct";
Direct.value = MSI_MONITOR_STATIC_MODE_VALUE;
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 = MSI_MONITOR_STATIC_MODE_VALUE;
Static.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Static.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = MSI_MONITOR_BREATHING_MODE_VALUE;
Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Breathing.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Breathing);
mode Flashing;
Flashing.name = "Flashing";
Flashing.value = MSI_MONITOR_FLASHING_MODE_VALUE;
Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Flashing.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Flashing);
mode Lightning;
Lightning.name = "Lightning";
Lightning.value = MSI_MONITOR_LIGHTNING_MODE_VALUE;
Lightning.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Lightning.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Lightning);
mode Marquee;
Marquee.name = "Marquee";
Marquee.value = MSI_MONITOR_MARQUEE_MODE_VALUE;
Marquee.flags = MODE_FLAG_AUTOMATIC_SAVE;
Marquee.color_mode = MODE_COLORS_NONE;
modes.push_back(Marquee);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = MSI_MONITOR_METEOR_MODE_VALUE;
Meteor.flags = MODE_FLAG_AUTOMATIC_SAVE;
Meteor.color_mode = MODE_COLORS_NONE;
modes.push_back(Meteor);
mode Rainbow;
Rainbow.name = "Rainbow";
Rainbow.value = MSI_MONITOR_RAINBOW_MODE_VALUE;
Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE;
Rainbow.color_mode = MODE_COLORS_NONE;
modes.push_back(Rainbow);
mode Random;
Random.name = "Random";
Random.value = MSI_MONITOR_RANDOM_MODE_VALUE;
Random.flags = MODE_FLAG_AUTOMATIC_SAVE;
Random.color_mode = MODE_COLORS_RANDOM;
modes.push_back(Random);
mode Off;
Off.name = "Off";
Off.value = MSI_MONITOR_OFF_MODE_VALUE;
Off.flags = MODE_FLAG_AUTOMATIC_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
SetupZones();
}
RGBController_MSIMonitor::~RGBController_MSIMonitor()
{
keepalive_thread_run = 0;
keepalive_thread->join();
delete keepalive_thread;
delete controller;
}
void RGBController_MSIMonitor::SetupZones()
{
zone new_zone;
new_zone.name = "Rear";
new_zone.type = ZONE_TYPE_LINEAR;
new_zone.leds_min = 9;
new_zone.leds_max = 9;
new_zone.leds_count = 9;
new_zone.matrix_map = nullptr;
zones.emplace_back(new_zone);
for(unsigned int i = 0 ; i < 9; i ++)
{
led new_led;
new_led.name = "LED " + std::to_string(i + 1);
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_MSIMonitor::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_MSIMonitor::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01);
}
void RGBController_MSIMonitor::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_MSIMonitor::DeviceUpdateMode()
{
controller->Set(modes[active_mode].value, colors, 0x01);
}
@@ -0,0 +1,38 @@
/*---------------------------------------------------------*\
| RGBController_MSIMonitor.cpp |
| |
| RGBController for MSI monitor (gaming controller) |
| |
| Andy Herbert 2026 May 16 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <chrono>
#include "RGBController.h"
#include "MSIMonitorController.h"
class RGBController_MSIMonitor : public RGBController
{
public:
RGBController_MSIMonitor(MSIMonitorController* controller_ptr);
~RGBController_MSIMonitor();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
MSIMonitorController* controller;
std::thread* keepalive_thread;
std::atomic<bool> keepalive_thread_run;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
void KeepaliveThread();
};
@@ -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,523 @@
/*---------------------------------------------------------*\
| RGBController_RealtekARGB.cpp |
| |
| RGBController for Realtek USB ARGB 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_RealtekARGB.h"
/**------------------------------------------------------------------*\
@name Realtek ARGB Device
@category LEDStrip
@type USB
@save :x:
@direct :rotating_light:
@effects :white_check_mark:
@detectors RealtekARGBControllerDetect
@comment
\*-------------------------------------------------------------------*/
RGBController_RealtekARGB::RGBController_RealtekARGB(RealtekARGBController* controller_ptr)
{
controller = controller_ptr;
name = controller_ptr->get_dev_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 + "ARGB Device";
type = DEVICE_TYPE_LEDSTRIP;
std::fill(std::begin(ready_to_reboot), std::end(ready_to_reboot), false);
SetupModes();
SetupZones();
}
void RGBController_RealtekARGB::SetupModes()
{
int brightness = controller->get_argb_brightness(0) >> 8;
mode Direct;
Direct.name = "Direct";
Direct.value = REALTEK_ARGB_EFF_NULL;
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_ARGB_EFF_ALWAYS_ON;
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_ARGB_EFF_BLINK;
Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Blink.speed_min = REALTEK_ARGB_SPEED_MIN;
Blink.speed_max = REALTEK_ARGB_SPEED_MAX;
Blink.speed = REALTEK_ARGB_SPEED_NORMAL;
Blink.colors_min = 1;
Blink.colors_max = 2;
Blink.color_mode = MODE_COLORS_MODE_SPECIFIC;
Blink.colors.resize(2);
Blink.brightness_min = 0;
Blink.brightness_max = 255;
Blink.brightness = brightness;
modes.push_back(Blink);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = REALTEK_ARGB_EFF_BREATH;
Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Breathing.speed_min = REALTEK_ARGB_SPEED_MIN;
Breathing.speed_max = REALTEK_ARGB_SPEED_MAX;
Breathing.speed = REALTEK_ARGB_SPEED_NORMAL;
Breathing.colors_min = 1;
Breathing.colors_max = 2;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.colors.resize(2);
Breathing.brightness_min = 0;
Breathing.brightness_max = 255;
Breathing.brightness = brightness;
modes.push_back(Breathing);
mode Spectrum;
Spectrum.name = "Spectrum";
Spectrum.value = REALTEK_ARGB_EFF_SPECTRUM;
Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Spectrum.speed_min = REALTEK_ARGB_SPEED_MIN;
Spectrum.speed_max = REALTEK_ARGB_SPEED_MAX;
Spectrum.speed = REALTEK_ARGB_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_ARGB_EFF_SCROLL;
Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Scroll.speed_min = REALTEK_ARGB_SPEED_MIN;
Scroll.speed_max = REALTEK_ARGB_SPEED_MAX;
Scroll.speed = REALTEK_ARGB_SPEED_NORMAL;
Scroll.colors_min = 1;
Scroll.colors_max = 2;
Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC;
Scroll.colors.resize(2);
Scroll.brightness_min = 0;
Scroll.brightness_max = 255;
Scroll.brightness = brightness;
modes.push_back(Scroll);
mode RainbowScroll;
RainbowScroll.name = "Rainbow Scroll";
RainbowScroll.value = REALTEK_ARGB_EFF_RAINBOW_SCROLL;
RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowScroll.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowScroll.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowScroll.speed = REALTEK_ARGB_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_ARGB_EFF_RUNNING_WATER;
RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RunningWater.speed_min = REALTEK_ARGB_SPEED_MIN;
RunningWater.speed_max = REALTEK_ARGB_SPEED_MAX;
RunningWater.speed = REALTEK_ARGB_SPEED_NORMAL;
RunningWater.colors_min = 1;
RunningWater.colors_max = 2;
RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC;
RunningWater.colors.resize(2);
RunningWater.brightness_min = 0;
RunningWater.brightness_max = 255;
RunningWater.brightness = brightness;
modes.push_back(RunningWater);
mode Sliding;
Sliding.name = "Sliding";
Sliding.value = REALTEK_ARGB_EFF_SLIDING;
Sliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
Sliding.speed_min = REALTEK_ARGB_SPEED_MIN;
Sliding.speed_max = REALTEK_ARGB_SPEED_MAX;
Sliding.speed = REALTEK_ARGB_SPEED_NORMAL;
Sliding.colors_min = 1;
Sliding.colors_max = 2;
Sliding.color_mode = MODE_COLORS_MODE_SPECIFIC;
Sliding.colors.resize(2);
Sliding.brightness_min = 0;
Sliding.brightness_max = 255;
Sliding.brightness = brightness;
modes.push_back(Sliding);
mode WideSliding;
WideSliding.name = "Wide Sliding";
WideSliding.value = REALTEK_ARGB_EFF_WIDE_SLIDING;
WideSliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
WideSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
WideSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
WideSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
WideSliding.colors_min = 1;
WideSliding.colors_max = 2;
WideSliding.color_mode = MODE_COLORS_MODE_SPECIFIC;
WideSliding.colors.resize(2);
WideSliding.brightness_min = 0;
WideSliding.brightness_max = 255;
WideSliding.brightness = brightness;
modes.push_back(WideSliding);
mode RainbowSliding;
RainbowSliding.name = "Rainbow Sliding";
RainbowSliding.value = REALTEK_ARGB_EFF_RAINBOW_SLIDING;
RainbowSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR;
RainbowSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
RainbowSliding.color_mode = MODE_COLORS_NONE;
RainbowSliding.brightness_min = 0;
RainbowSliding.brightness_max = 255;
RainbowSliding.brightness = brightness;
modes.push_back(RainbowSliding);
mode RainbowFadeSliding;
RainbowFadeSliding.name = "Rainbow Fade Sliding";
RainbowFadeSliding.value = REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING;
RainbowFadeSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
RainbowFadeSliding.speed_min = REALTEK_ARGB_SPEED_MIN;
RainbowFadeSliding.speed_max = REALTEK_ARGB_SPEED_MAX;
RainbowFadeSliding.speed = REALTEK_ARGB_SPEED_NORMAL;
RainbowFadeSliding.color_mode = MODE_COLORS_NONE;
RainbowFadeSliding.brightness_min = 0;
RainbowFadeSliding.brightness_max = 255;
RainbowFadeSliding.brightness = brightness;
modes.push_back(RainbowFadeSliding);
mode NewtonCradle;
NewtonCradle.name = "Newton Cradle";
NewtonCradle.value = REALTEK_ARGB_EFF_NEWTON_CRADLE;
NewtonCradle.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
NewtonCradle.speed_min = REALTEK_ARGB_SPEED_MIN;
NewtonCradle.speed_max = REALTEK_ARGB_SPEED_MAX;
NewtonCradle.speed = REALTEK_ARGB_SPEED_NORMAL;
NewtonCradle.colors_min = 1;
NewtonCradle.colors_max = 2;
NewtonCradle.color_mode = MODE_COLORS_MODE_SPECIFIC;
NewtonCradle.colors.resize(2);
NewtonCradle.brightness_min = 0;
NewtonCradle.brightness_max = 255;
NewtonCradle.brightness = brightness;
modes.push_back(NewtonCradle);
mode Meteor;
Meteor.name = "Meteor";
Meteor.value = REALTEK_ARGB_EFF_METEOR;
Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED;
Meteor.speed_min = REALTEK_ARGB_SPEED_MIN;
Meteor.speed_max = REALTEK_ARGB_SPEED_MAX;
Meteor.speed = REALTEK_ARGB_SPEED_NORMAL;
Meteor.colors_min = 1;
Meteor.colors_max = 2;
Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC;
Meteor.colors.resize(2);
Meteor.brightness_min = 0;
Meteor.brightness_max = 255;
Meteor.brightness = brightness;
modes.push_back(Meteor);
mode ZigZag;
ZigZag.name = "ZigZag";
ZigZag.value = REALTEK_ARGB_EFF_ZIGZAG;
ZigZag.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR;
ZigZag.speed_min = REALTEK_ARGB_SPEED_MIN;
ZigZag.speed_max = REALTEK_ARGB_SPEED_MAX;
ZigZag.speed = REALTEK_ARGB_SPEED_NORMAL;
ZigZag.colors_min = 1;
ZigZag.colors_max = 2;
ZigZag.color_mode = MODE_COLORS_MODE_SPECIFIC;
ZigZag.colors.resize(2);
ZigZag.brightness_min = 0;
ZigZag.brightness_max = 255;
ZigZag.brightness = brightness;
modes.push_back(ZigZag);
mode StarryNight;
StarryNight.name = "Starry Night";
StarryNight.value = REALTEK_ARGB_EFF_STARRY_NIGHT;
StarryNight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR;
StarryNight.speed_min = REALTEK_ARGB_SPEED_MIN;
StarryNight.speed_max = REALTEK_ARGB_SPEED_MAX;
StarryNight.speed = REALTEK_ARGB_SPEED_NORMAL;
StarryNight.colors_min = 1;
StarryNight.colors_max = 1;
StarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC;
StarryNight.colors.resize(1);
StarryNight.brightness_min = 0;
StarryNight.brightness_max = 255;
StarryNight.brightness = brightness;
modes.push_back(StarryNight);
mode Stack;
Stack.name = "Stack";
Stack.value = REALTEK_ARGB_EFF_STACK;
Stack.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS;
Stack.colors_min = 1;
Stack.colors_max = 2;
Stack.color_mode = MODE_COLORS_MODE_SPECIFIC;
Stack.colors.resize(2);
Stack.brightness_min = 0;
Stack.brightness_max = 255;
Stack.brightness = brightness;
modes.push_back(Stack);
}
RGBController_RealtekARGB::~RGBController_RealtekARGB()
{
delete controller;
}
void RGBController_RealtekARGB::SetupZones()
{
/*-------------------------------------------------*\
| Only set LED count on the first run |
\*-------------------------------------------------*/
bool first_run = false;
int idx = 0;
int argb_num = 0;
int fix_grps = 0;
int num_fixgrp = 0;
int argb_num_fixgrp = 0;
if(zones.size() == 0)
{
first_run = true;
}
/*-------------------------------------------------*\
| Clear any existing color/LED configuration |
\*-------------------------------------------------*/
leds.clear();
colors.clear();
valid_grp.clear();
fix_grps = controller->get_fix_grps();
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(controller->get_zone_enable(grp_num))
{
valid_grp.push_back(grp_num);
if(fix_grps & (0x1 << grp_num))
{
num_fixgrp++;
argb_num_fixgrp += controller->get_argb_num(grp_num);
}
}
}
/*-------------------------------------------------*\
| Set zones and leds |
\*-------------------------------------------------*/
zones.resize(valid_grp.size());
for(int grp_num : valid_grp)
{
argb_num = controller->get_argb_num(grp_num);
zones[idx].name = "strip " + std::to_string(idx + 1);
zones[idx].type = ZONE_TYPE_LINEAR;
if(fix_grps & (0x1 << grp_num))
{
zones[idx].leds_count = argb_num;
zones[idx].leds_min = argb_num;
zones[idx].leds_max = argb_num;
}
else
{
if(first_run)
{
zones[idx].leds_count = 0;
}
else
{
zones[idx].leds_count = argb_num;
}
zones[idx].leds_min = 0;
zones[idx].leds_max = (REALTEK_ARGB_MAX - argb_num_fixgrp) / ((int)valid_grp.size() - num_fixgrp);
}
zones[idx].matrix_map = NULL;
for(unsigned int led_idx = 0; led_idx < zones[idx].leds_count; led_idx++)
{
led myled;
myled.name = zones[idx].name + " led_";
myled.name.append(std::to_string(led_idx + 1));
leds.push_back(myled);
}
idx++;
}
SetupColors();
}
void RGBController_RealtekARGB::ResizeZone(int zone, int new_size)
{
int fix_grps = controller->get_fix_grps();
int orig_num = controller->get_argb_num(valid_grp[zone]);
bool need_reboot = true;
if((size_t) zone >= zones.size())
{
return;
}
if((new_size == orig_num) && zones[zone].leds_count)
{
return;
}
if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max))
{
int total_argb_num = 0;
std::vector<RGBColor> rtk_colors(orig_num, 0);
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(grp_num == valid_grp[zone])
{
total_argb_num += new_size;
}
else
{
total_argb_num += controller->get_argb_num(grp_num);
}
}
if(total_argb_num > REALTEK_ARGB_MAX)
{
return;
}
controller->set_argb_direct(valid_grp[zone], rtk_colors, 0xFF);
controller->set_argb_num(valid_grp[zone], new_size);
ready_to_reboot[valid_grp[zone]] = true;
for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++)
{
if(!ready_to_reboot[grp_num] && !(fix_grps & (0x1 << grp_num)))
{
need_reboot = false;
break;
}
}
if(need_reboot)
{
controller->device_reboot();
controller->device_rescan_trigger();
}
SetupZones();
}
}
void RGBController_RealtekARGB::DeviceUpdateLEDs()
{
for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
if(zones[zone_idx].leds_count > 0)
{
UpdateZoneLEDs((int)zone_idx);
}
}
}
void RGBController_RealtekARGB::UpdateZoneLEDs(int zone)
{
unsigned short brightness = 0xFF;
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> color_buf;
if(curr_mode.color_mode == MODE_COLORS_PER_LED &&
curr_mode.value == REALTEK_ARGB_EFF_NULL) //direct mode
{
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
brightness = curr_mode.brightness;
}
color_buf.resize(zones[zone].leds_count);
for(unsigned int i = 0; i < zones[zone].leds_count; i++)
color_buf[i] = zones[zone].colors[i];
controller->set_argb_direct(valid_grp[zone], color_buf, brightness);
}
else
{
UpdateSingleLED(zone);
}
}
void RGBController_RealtekARGB::UpdateSingleLED(int zone)
{
mode& curr_mode = modes[active_mode];
std::vector<RGBColor> rtk_colors = curr_mode.colors;
struct RealtekARGBControllerSetEffParam param;
param.speed = REALTEK_ARGB_SPEED_NORMAL;
param.brightness = 0xFF;
param.dir = 0;
param.random_color = 0;
if(curr_mode.flags & MODE_FLAG_HAS_SPEED)
{
param.speed = curr_mode.speed;
}
if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS)
{
param.brightness = curr_mode.brightness;
}
if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR)
{
if(curr_mode.direction == MODE_DIRECTION_RIGHT)
{
param.dir = 1;
}
}
if(curr_mode.flags & MODE_FLAG_HAS_RANDOM_COLOR)
{
if(curr_mode.color_mode == MODE_COLORS_RANDOM)
{
param.random_color = 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(valid_grp[zone], curr_mode.value, rtk_colors, &param);
}
void RGBController_RealtekARGB::DeviceUpdateMode()
{
if(modes[active_mode].value != REALTEK_ARGB_EFF_NULL)
{
DeviceUpdateLEDs();
}
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------*\
| RGBController_RealtekARGB.h |
| |
| RGBController for Realtek USB ARGB 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 "RealtekARGBController.h"
class RGBController_RealtekARGB : public RGBController
{
public:
RGBController_RealtekARGB(RealtekARGBController* controller_ptr);
~RGBController_RealtekARGB();
void SetupModes();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int zone);
void DeviceUpdateMode();
private:
RealtekARGBController* controller;
std::vector<int> valid_grp;
bool ready_to_reboot[REALTEK_ARGB_NUM_ARGB_GRP] = {false};
};
@@ -0,0 +1,715 @@
/*---------------------------------------------------------*\
| RealtekARGBController.cpp |
| |
| Controller for Realtek USB ARGB ICs |
| |
| Jerry Fan (JerryFan0612) 13 Mar 2026 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RealtekARGBController.h"
#include "MathUtils.h"
#include "StringUtils.h"
using namespace std::chrono_literals;
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,
};
RealtekARGBController::RealtekARGBController(hid_device* dev, hid_device_info* info)
{
hdev = dev;
hidinfo = info;
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
argbctl_data[i] = (unsigned char*)calloc(REALTEK_ARGB_CTL_DATA_SIZE, 1);
memset(argbctl_data[i], 0, REALTEK_ARGB_CTL_DATA_SIZE);
prev_bright[i] = 0xFFFF;
}
device_init();
keepalive_thread_run = false;
keepalive_thread = NULL;
}
RealtekARGBController::~RealtekARGBController()
{
/*-----------------------------------------------------*\
| Close keepalive thread |
\*-----------------------------------------------------*/
if(keepalive_thread != NULL)
{
keepalive_thread_run = false;
keepalive_thread->join();
delete keepalive_thread;
}
if(hdev)
{
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[i] = REALTEK_ARGB_LED_CTL_FW;
}
}
hid_close(hdev);
hdev = NULL;
}
memset(argbctl_hdr, 0, REALTEK_ARGB_CTL_HDR_SIZE);
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
free(argbctl_data[i]);
}
void RealtekARGBController::KeepaliveThreadFunction()
{
/*-----------------------------------------------------------------*\
| One shot thread to rescan device |
\*-----------------------------------------------------------------*/
while(keepalive_thread_run.load())
{
std::this_thread::sleep_for(2s);
device_rescan();
keepalive_thread_run = false;
}
}
int RealtekARGBController::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(hdev == NULL)
{
free(usb_buf);
return -1;
}
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 RealtekARGBController::usb_hid_get_report(int data_len, int* id)
{
int retlen = 0;
if(data_len <= REALTEK_ARGB_HID_DATALEN_CH2)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH2;
retlen = REALTEK_ARGB_HID_DATALEN_CH2;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH3)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH3;
retlen = REALTEK_ARGB_HID_DATALEN_CH3;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH4)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH4;
retlen = REALTEK_ARGB_HID_DATALEN_CH4;
}
else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH5)
{
*id = REALTEK_ARGB_HID_ID_DATA_CH5;
retlen = REALTEK_ARGB_HID_DATALEN_CH5;
}
else
{
*id = REALTEK_ARGB_HID_ID_DATA_CH1;
retlen = REALTEK_ARGB_HID_DATALEN_CH1;
}
return retlen;
}
void RealtekARGBController::device_init()
{
set_write_unlock();
get_argbctl_hdr();
get_argbctl_data();
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[i] = REALTEK_ARGB_LED_CTL_FW;
}
}
}
int RealtekARGBController::set_write_unlock()
{
int ret = 0;
int buf_len = REALTEK_ARGB_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;
}
unsigned char RealtekARGBController::get_support_openrgb()
{
int buf_len = REALTEK_ARGB_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_ARGB_SYNC_METHOD_OPENRGB;
if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true))
{
is_support = 0;
}
return is_support;
}
std::string RealtekARGBController::get_manu_name()
{
return StringUtils::wchar_to_char(hidinfo->manufacturer_string);
}
std::string RealtekARGBController::get_product_name()
{
return StringUtils::wchar_to_char(hidinfo->product_string);
}
std::string RealtekARGBController::get_sn()
{
return StringUtils::wchar_to_char(hidinfo->serial_number);
}
std::string RealtekARGBController::get_dev_loc()
{
return hidinfo->path;
}
std::string RealtekARGBController::get_fw_ver()
{
std::string ver = "";
struct RealtekARGBControllerFWVersion fw_ver;
int buf_len = REALTEK_ARGB_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;
}
std::string RealtekARGBController::get_ic_uuid()
{
unsigned int uuid = 0;
int buf_len = REALTEK_ARGB_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] = 0xC3;
usb_hid_ioctl(usb_buf, (unsigned char*)&uuid, sizeof(uuid), 0, true);
return StringUtils::u32int_to_hexString(uuid);
}
std::string RealtekARGBController::get_dev_name()
{
bool got_custled = false;
std::string devname = get_product_name();
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
if(custled[5] == REALTEK_ARGB_CUST_DEVNAME_MANU_UUID)
{
devname = get_manu_name() + get_ic_uuid();
}
}
return devname;
}
int RealtekARGBController::get_fix_grps()
{
bool got_custled = false;
static int fix_grps = 0;
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
fix_grps = custled[4];
}
return fix_grps;
}
bool RealtekARGBController::get_zone_enable(int grp_num)
{
bool got_custled = false;
bool en = false;
if(custled[0])
{
got_custled = true;
}
else
{
if(!get_custled(custled, sizeof(custled)))
{
got_custled = true;
}
}
if(got_custled)
{
en = (custled[5] & (0x1 << grp_num)) ? true : false;
}
return en;
}
int RealtekARGBController::get_argb_num(int grp_num)
{
int num_rgb = 0;
memcpy(&num_rgb, &argbctl_hdr[32 + grp_num * 2], 2);
return num_rgb;
}
int RealtekARGBController::get_argb_brightness(int grp_num)
{
int bright = 0;
memcpy(&bright, &argbctl_hdr[42 + grp_num * 2], 2);
return bright;
}
int RealtekARGBController::set_argb_brightness(int grp_num, unsigned short bright)
{
memcpy(&argbctl_hdr[42 + grp_num * sizeof(bright)], &bright, sizeof(bright));
set_argbctl_hdr();
return 0;
}
int RealtekARGBController::set_appctl(unsigned char grp_num, unsigned char ctl_sts)
{
int buf_len = REALTEK_ARGB_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;
usb_buf[0x16] = grp_num;
return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false);
}
int RealtekARGBController::get_custled(unsigned char* cust, unsigned int cust_len)
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC004000;
unsigned int offset = 0x800;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len + offset, 1);// will release in usb_hid_ioctl function
unsigned char* data = (unsigned char*)calloc(buf_len, 1);
memset(data, 0x00, buf_len);
memset(usb_buf, 0x00, buf_len + offset);
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, buf_len, offset, true);
if(!ret)
{
memcpy(cust, &data[70], cust_len);
}
free(data);
return ret;
}
int RealtekARGBController::get_argbctl_hdr()
{
int buf_len = REALTEK_ARGB_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, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, true);
}
int RealtekARGBController::set_argbctl_hdr()
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get 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, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, false);
}
int RealtekARGBController::get_argbctl_data()
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int offset = REALTEK_ARGB_CTL_HDR_SIZE;
unsigned char* usb_buf;
for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++)
{
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get 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));
ret = usb_hid_ioctl(usb_buf, argbctl_data[i], REALTEK_ARGB_CTL_DATA_SIZE, 0, true);
offset += REALTEK_ARGB_CTL_DATA_SIZE;
}
return ret;
}
int RealtekARGBController::set_argbctl_data(unsigned char grp_num)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int offset = REALTEK_ARGB_CTL_HDR_SIZE + REALTEK_ARGB_CTL_DATA_SIZE * grp_num;
unsigned char* usb_buf;
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get 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, argbctl_data[grp_num], REALTEK_ARGB_CTL_DATA_SIZE, 0, false);
}
int RealtekARGBController::set_eff_id(unsigned char grp_num, unsigned short effid)
{
memcpy(argbctl_data[grp_num], &effid, sizeof(effid));
return 0;
}
int RealtekARGBController::set_p_color(unsigned char grp_num, RGBColor color)
{
memcpy(argbctl_data[grp_num] + 8, &color, sizeof(color));
return 0;
}
int RealtekARGBController::set_s_color(unsigned char grp_num, RGBColor color)
{
memcpy(argbctl_data[grp_num] + 12, &color, sizeof(color));
return 0;
}
int RealtekARGBController::set_cycle(unsigned char grp_num, unsigned short cycle)
{
memcpy(argbctl_data[grp_num] + 2, &cycle, sizeof(cycle));
return 0;
}
int RealtekARGBController::set_ramp(unsigned char grp_num, unsigned short ramp)
{
memcpy(argbctl_data[grp_num] + 4, &ramp, sizeof(ramp));
return 0;
}
int RealtekARGBController::set_stable(unsigned char grp_num, unsigned short stable)
{
memcpy(argbctl_data[grp_num] + 6, &stable, sizeof(stable));
return 0;
}
int RealtekARGBController::set_subcmd(unsigned char grp_num, int subcmd)
{
memcpy(argbctl_data[grp_num] + 16, &subcmd, sizeof(subcmd));
return 0;
}
int RealtekARGBController::set_direct(unsigned char* color, int color_num, unsigned char grp_num)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int data_len = color_num * REALTEK_ARGB_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_ARGB_SYNC_METHOD_OPENRGB;
usb_buf[0x16] = grp_num;
memcpy(&usb_buf[0x17], &color_num, sizeof(color_num));
return usb_hid_ioctl(usb_buf, color, data_len, 0, false);
}
int RealtekARGBController::get_flash_argbctl_hdr(unsigned char* data)
{
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC000000;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get 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));
return usb_hid_ioctl(usb_buf, data, buf_len, 0, true);
}
int RealtekARGBController::set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len)
{
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
unsigned int addr = 0xAC000000;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
unsigned char* data = (unsigned char*)calloc(buf_len, 1);
memset(data, 0, buf_len);
ret = get_flash_argbctl_hdr(data);
if(!ret)
{
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x10;
memcpy(&usb_buf[0x17], &addr, sizeof(addr));
ret = usb_hid_ioctl(usb_buf, (unsigned char*)&addr, sizeof(addr), 0, false);
usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x11;
memcpy(data + offset, argbctl_hdr + offset, data_len);
ret = usb_hid_ioctl(usb_buf, data, buf_len, 0, false);
}
free(data);
return ret;
}
int RealtekARGBController::set_argb_direct(int grp_num, 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_ARGB_COLOR_DEPTH;
unsigned char* buf;
std::lock_guard<std::mutex> lock(my_mutex);
if(color_num == 0)
{
goto exit;
}
if(appctl[grp_num] != REALTEK_ARGB_LED_CTL_APP)
{
ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_APP);
if(ret)
{
goto exit;
}
else
{
appctl[grp_num] = REALTEK_ARGB_LED_CTL_APP;
}
}
if(prev_bright[grp_num] != brightness)
{
prev_bright[grp_num] = brightness;
ret = set_argb_brightness(grp_num, brightness << 8);
if(ret)
{
goto exit;
}
}
buf = (uint8_t*)malloc(buf_len);
memset(buf, 0, buf_len);
for(size_t i = 0; i < color_num; i++)
{
buf[i * REALTEK_ARGB_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]);
buf[i * REALTEK_ARGB_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]);
buf[i * REALTEK_ARGB_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]);
}
ret = set_direct(buf, (int)color_num, grp_num);
free(buf);
exit:
return ret;
}
int RealtekARGBController::set_argb_effect(int grp_num, uint8_t mode, std::vector<RGBColor> color_buf, struct RealtekARGBControllerSetEffParam* param)
{
int ret = -1;
int cycle = MathUtils::IntInterpolate(REALTEK_ARGB_CYCLE_MAX, REALTEK_ARGB_CYCLE_MIN, 0, REALTEK_ARGB_SPEED_MAX, param->speed);
std::lock_guard<std::mutex> lock(my_mutex);
if(mode == REALTEK_ARGB_EFF_ALWAYS_ON || mode == REALTEK_ARGB_EFF_STACK)
{
cycle = 0;
}
if(color_buf.size() >= 1)
{
set_p_color(grp_num, color_buf[0]);
}
if(color_buf.size() >= 2)
{
set_s_color(grp_num, color_buf[1]);
}
set_eff_id(grp_num, mode);
set_cycle(grp_num, cycle);
set_ramp(grp_num, 0);
set_stable(grp_num, 0);
set_subcmd(grp_num, (param->dir || param->random_color) ? 0x1 : 0x0);
set_argb_brightness(grp_num, param->brightness << 8);
set_argbctl_data(grp_num);
ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_FW);
if(!ret)
{
appctl[grp_num] = REALTEK_ARGB_LED_CTL_FW;
}
return ret;
}
int RealtekARGBController::set_argb_num(int grp_num, unsigned short new_num)
{
memcpy(&argbctl_hdr[32 + grp_num * sizeof(new_num)], &new_num, sizeof(new_num));
set_flash_argbctl_hdr(32 + grp_num * sizeof(new_num), sizeof(new_num));
return 0;
}
int RealtekARGBController::device_reboot()
{
std::lock_guard<std::mutex> lock(my_mutex);
int ret = 0;
int buf_len = REALTEK_ARGB_HID_DATALEN_CH1;
int reboot_type = 1;
unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function
memset(usb_buf, 0x00, buf_len);
memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet));
usb_buf[0x13] = 0x30;
usb_buf[0x14] = reboot_type;
ret = usb_hid_ioctl(usb_buf, (unsigned char*)&reboot_type, sizeof(reboot_type), 0, false);
hid_close(hdev);
hdev = NULL;
return ret;
}
void RealtekARGBController::device_rescan_trigger()
{
keepalive_thread_run = true;
keepalive_thread = new std::thread(&RealtekARGBController::KeepaliveThreadFunction, this);
}
void RealtekARGBController::device_rescan()
{
hid_device_info* info_full = hid_enumerate(REALTEK_ARGB_VID, REALTEK_ARGB_PID);
hid_device_info* info_temp = info_full;
while(info_temp)
{
if(info_temp->vendor_id == REALTEK_ARGB_VID &&
info_temp->product_id == REALTEK_ARGB_PID &&
info_temp->usage == REALTEK_ARGB_HID2SCSI_USAGE &&
info_temp->usage_page == REALTEK_ARGB_HID2SCSI_PG)
{
hid_device* dev_temp = hid_open_path(info_temp->path);
if(dev_temp)
{
hdev = dev_temp;
if(get_support_openrgb())
{
device_init();
break;
}
else
{
hid_close(hdev);
hdev = NULL;
}
}
}
info_temp = info_temp->next;
}
hid_free_enumeration(info_full);
}
@@ -0,0 +1,182 @@
/*---------------------------------------------------------*\
| RealtekARGBController.h |
| |
| Controller for Realtek USB ARGB 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 <thread>
#include <hidapi.h>
#include "RGBController.h"
#define REALTEK_ARGB_VID 0x0BDA
#define REALTEK_ARGB_PID 0x9209
#define REALTEK_ARGB_HID2SCSI_PG 0xFF00
#define REALTEK_ARGB_HID2SCSI_USAGE 0x0001
#define REALTEK_ARGB_NUM_ARGB_GRP 5
#define REALTEK_ARGB_BRINTF_TYPE_HID 1
#define REALTEK_ARGB_SYNC_METHOD_OPENRGB 6
#define REALTEK_ARGB_COLOR_DEPTH 3
#define REALTEK_ARGB_MAX 400
#define REALTEK_ARGB_CTL_HDR_SIZE 80
#define REALTEK_ARGB_CTL_DATA_SIZE 96
#define REALTEK_ARGB_HID_DATALEN_CH1 4096
#define REALTEK_ARGB_HID_DATALEN_CH2 64
#define REALTEK_ARGB_HID_DATALEN_CH3 512
#define REALTEK_ARGB_HID_DATALEN_CH4 1200
#define REALTEK_ARGB_HID_DATALEN_CH5 2048
#define REALTEK_ARGB_HID_ID_DATA_CH1 0x57
#define REALTEK_ARGB_HID_ID_DATA_CH2 0x59
#define REALTEK_ARGB_HID_ID_DATA_CH3 0x5A
#define REALTEK_ARGB_HID_ID_DATA_CH4 0x5B
#define REALTEK_ARGB_HID_ID_DATA_CH5 0x5C
enum REALTEK_ARGB_LED_CTL
{
REALTEK_ARGB_LED_CTL_FW,
REALTEK_ARGB_LED_CTL_APP,
REALTEK_ARGB_LED_CTL_LAMP,
};
enum REALTEK_ARGB_EFFECT_ID
{
REALTEK_ARGB_EFF_NULL,
REALTEK_ARGB_EFF_ALWAYS_ON,
REALTEK_ARGB_EFF_BLINK,
REALTEK_ARGB_EFF_BREATH,
REALTEK_ARGB_EFF_SPECTRUM,
REALTEK_ARGB_EFF_SCROLL,
REALTEK_ARGB_EFF_RAINBOW_SCROLL,
REALTEK_ARGB_EFF_RUNNING_WATER,
REALTEK_ARGB_EFF_SLIDING,
REALTEK_ARGB_EFF_NEWTON_CRADLE,
REALTEK_ARGB_EFF_METEOR,
REALTEK_ARGB_EFF_RAINBOW_SLIDING,
REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING,
REALTEK_ARGB_EFF_WIDE_SLIDING,
REALTEK_ARGB_EFF_DOT_MATRIX, // reserved: Not supported by OpenRGB
REALTEK_ARGB_EFF_DOT_MATRIX_BREATH, // reserved: Not supported by OpenRGB
REALTEK_ARGB_EFF_ZIGZAG,
REALTEK_ARGB_EFF_STARRY_NIGHT,
REALTEK_ARGB_EFF_STACK,
};
enum REALTEK_ARGB_SPEED
{
REALTEK_ARGB_SPEED_MIN = 1,
REALTEK_ARGB_SPEED_NORMAL = 50,
REALTEK_ARGB_SPEED_MAX = 100,
};
enum REALTEK_ARGB_CYCLE_MS
{
REALTEK_ARGB_CYCLE_MIN = 200,
REALTEK_ARGB_CYCLE_NORMAL = 2000,
REALTEK_ARGB_CYCLE_MAX = 10000,
};
enum REALTEK_ARGB_CUST_DEVNAME
{
REALTEK_ARGB_CUST_DEVNAME_NULL = 0x0,
REALTEK_ARGB_CUST_DEVNAME_MANU_UUID = 0x1,
};
struct RealtekARGBControllerSetEffParam
{
unsigned int speed;
unsigned short brightness;
unsigned char dir;
unsigned char random_color;
};
struct RealtekARGBControllerFWVersion
{
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 RealtekARGBController
{
public:
RealtekARGBController(hid_device* dev, hid_device_info* info);
~RealtekARGBController();
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();
std::string get_ic_uuid();
std::string get_dev_name();
int get_fix_grps();
bool get_zone_enable(int grp_num);
int get_argb_num(int grp_num);
int get_argb_brightness(int grp_num);
int set_argb_brightness(int grp_num, unsigned short bright);
int set_argb_direct(int grp_num, std::vector<RGBColor> color_buf, unsigned short brightness);
int set_argb_effect(int grp_num, uint8_t mode, std::vector<RGBColor> color_buf, struct RealtekARGBControllerSetEffParam* param);
int set_argb_num(int grp_num, unsigned short new_num);
int device_reboot();
void device_rescan_trigger();
private:
hid_device* hdev;
std::mutex my_mutex;
unsigned char argbctl_hdr[REALTEK_ARGB_CTL_HDR_SIZE] = {0};
unsigned char* argbctl_data[REALTEK_ARGB_NUM_ARGB_GRP] = {0};
hid_device_info* hidinfo = NULL;
int appctl[REALTEK_ARGB_NUM_ARGB_GRP] = {REALTEK_ARGB_LED_CTL_FW};
unsigned short prev_bright[REALTEK_ARGB_NUM_ARGB_GRP] = {0};
std::atomic<bool> keepalive_thread_run;
std::thread* keepalive_thread;
unsigned char custled[16] = {0};
void KeepaliveThreadFunction();
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);
void device_init();
int set_write_unlock();
int set_appctl(unsigned char grp_num, unsigned char ctl_sts);
int set_argbctl_data(unsigned char* data, int data_len, int offset);
int get_custled(unsigned char* cust, unsigned int cust_len);
int get_argbctl_hdr();
int set_argbctl_hdr();
int get_argbctl_data();
int set_argbctl_data(unsigned char grp_num);
int set_eff_id(unsigned char grp_num, unsigned short effid);
int set_p_color(unsigned char grp_num, RGBColor color);
int set_s_color(unsigned char grp_num, RGBColor color);
int set_cycle(unsigned char grp_num, unsigned short cycle);
int set_ramp(unsigned char grp_num, unsigned short ramp);
int set_stable(unsigned char grp_num, unsigned short stable);
int set_subcmd(unsigned char grp_num, int subcmd);
int set_direct(unsigned char* color, int color_num, unsigned char grp_num);
int get_flash_argbctl_hdr(unsigned char* data);
int set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len);
void device_rescan();
};
@@ -0,0 +1,52 @@
/*---------------------------------------------------------*\
| RealtekARGBControllerDetect.cpp |
| |
| Detector for Realtek USB ARGB 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.h"
#include "RGBController_RealtekARGB.h"
/******************************************************************************************\
* *
* DetectRealtekARGBControllers *
* *
* Tests the USB address to see if an Realtek ARGB controller exists there *
* *
\******************************************************************************************/
void DetectRealtekARGBControllers(hid_device_info* info, const std::string& /*name*/)
{
RealtekARGBController* controller = NULL;
RGBController_RealtekARGB* rgb_controller = NULL;
hid_device* dev = hid_open_path(info->path);
if(dev)
{
controller = new RealtekARGBController(dev, info);
if(controller->get_support_openrgb())
{
rgb_controller = new RGBController_RealtekARGB(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("RTL9209", DetectRealtekARGBControllers, REALTEK_ARGB_VID, REALTEK_ARGB_PID, REALTEK_ARGB_HID2SCSI_PG, REALTEK_ARGB_HID2SCSI_USAGE);
@@ -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);
@@ -28,6 +28,7 @@
#define REDRAGON_M801_PID 0xFC58
#define REDRAGON_M810_PID 0xFA7E
#define REDRAGON_M987_PID 0xFC69
#define REDRAGON_M921_PID 0xFC40
/******************************************************************************************\
* *
@@ -62,3 +63,4 @@ REGISTER_HID_DETECTOR_IP("Redragon M808 Storm", DetectRedragonMice, RE
REGISTER_HID_DETECTOR_IP("Redragon M801 Sniper", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M801_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M810 Taipan", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M810_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M987 Reaping", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M987_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
REGISTER_HID_DETECTOR_IP("Redragon M921 Azzinoth", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M921_PID, 2, REDRAGON_MOUSE_USAGE_PAGE);
@@ -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";
}

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