Compare commits

...
27 changed files with 9082 additions and 3423 deletions
@@ -471,6 +471,7 @@ REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC WHITE",
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);
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(
@@ -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)
@@ -70,6 +70,12 @@ void MSILaptopController::SetLEDs(std::vector<led> leds, std::vector<RGBColor> c
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);
@@ -22,7 +22,7 @@
#define NA 0xFFFFFFFF
#define MSI_LAPTOP_KLC_MATRIX_HEIGHT 6
#define MSI_LAPTOP_KLC_MATRIX_WIDTH 23
#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 5
#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 3
static const msi_laptop_led msi_raider_a18_klc_leds[] =
{
@@ -140,14 +140,24 @@ static unsigned int msi_raider_a18_klc_matrix_map[MSI_LAPTOP_KLC_MATRIX_HEIGHT][
{ 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 },
{ "Lightbar 4", 0x03 },
{ "Lightbar 5", 0x04 },
{ "Logo", 0x05 },
{ "Logo", 0x03 },
};
static const MSILaptopModel msi_laptop_models[] =
@@ -220,6 +230,6 @@ void DetectMSILaptop(hid_device_info* info, const std::string& name)
}
}
REGISTER_HID_DETECTOR_I("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID, 0);
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,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();
};
@@ -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);
@@ -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);
@@ -120,13 +120,13 @@
#define STEELSERIES_APEX_PRO_TKL_2023_PID 0x1628
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1 0x1630
#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2 0x1632
#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646
#define STEELSERIES_APEX_M750_PID 0x0616
#define STEELSERIES_APEX_OG_PID 0x1202
#define STEELSERIES_APEX_350_PID 0x1206
#define STEELSERIES_APEX_PRO3_PID 0x1640
#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644
#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646
void DetectSteelSeriesAerox3(hid_device_info* info, const std::string& name)
{
@@ -516,10 +516,10 @@ REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL", Dete
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL 2023 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_I ("SteelSeries Apex M750", DetectSteelSeriesApexM, STEELSERIES_VID, STEELSERIES_APEX_M750_PID, 2 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex (OG)/Apex Fnatic", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_OG_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 );
REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 );
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1);
REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1);
@@ -10,6 +10,8 @@
\*---------------------------------------------------------*/
#include "RGBController_ZotacBlackwellGPU.h"
#include "LogManager.h"
#include "pci_ids.h"
/**------------------------------------------------------------------*\
@name ZOTAC RTX 50 series GPU
@@ -20,21 +22,60 @@
@effects :tools:
@detectors DetectZotacBlackwellGPUControllersPCI
@comment
Supports ZOTAC Blackwell (RTX 50 series) GPUs with 3 zones:
Logo, Side Bar, and Infinity Mirror.
Supports ZOTAC Blackwell (RTX 50 series) GPUs. The zone layout
varies per card and is resolved from a static table keyed on
PCI device and sub-device IDs.
The controller uses individual SMBus byte writes (registers
0x20-0x2F) with a 3ms delay between each transaction.
To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`
and detection entries in
To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`,
a zone config entry in `device_zone_configs` below, and a
`REGISTER_I2C_PCI_DETECTOR` line in
`Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp`.
\*-------------------------------------------------------------------*/
RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr)
const RGBController_ZotacBlackwellGPU::DeviceZoneConfig RGBController_ZotacBlackwellGPU::device_zone_configs[] =
{
{ NVIDIA_RTX5080_DEV, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, { "Logo", "Side Bar", "Infinity Mirror" }, 3 },
{ NVIDIA_RTX5090_DEV, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, { "ZOTAC Gaming", "Logo" }, 2 },
{ 0, 0, { nullptr }, 0 }
};
const RGBController_ZotacBlackwellGPU::DeviceZoneConfig* RGBController_ZotacBlackwellGPU::FindZoneConfig(uint16_t device, uint16_t subdevice)
{
for(const DeviceZoneConfig* cfg = device_zone_configs; cfg->zone_count != 0; cfg++)
{
if(cfg->device == device && cfg->subdevice == subdevice)
{
return cfg;
}
}
return nullptr;
}
RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr,
uint16_t device, uint16_t subdevice)
{
controller = controller_ptr;
const DeviceZoneConfig* cfg = FindZoneConfig(device, subdevice);
if(cfg == nullptr)
{
LOG_ERROR("[%s] Unrecognized PCI device/subdevice: %04X/%04X. Falling back to three generic zones.",
controller->GetName().c_str(), device, subdevice);
zone_names.push_back("Zone 0");
zone_names.push_back("Zone 1");
zone_names.push_back("Zone 2");
}
else
{
for(uint8_t z = 0; z < cfg->zone_count; z++)
{
zone_names.push_back(cfg->zones[z]);
}
}
name = controller->GetName();
vendor = "ZOTAC";
description = "ZOTAC RTX 50 series RGB GPU Device (" + controller->GetVersion() + ")";
@@ -252,52 +293,25 @@ RGBController_ZotacBlackwellGPU::~RGBController_ZotacBlackwellGPU()
void RGBController_ZotacBlackwellGPU::SetupZones()
{
/*---------------------------------------------------------*\
| Zone 0: Logo |
| One single-LED zone per name from the device_zone_configs |
| table. The zone's index is its position here, which is |
| written verbatim to the zone register (0x21) on update. |
\*---------------------------------------------------------*/
zone logo_zone;
logo_zone.name = "Logo";
logo_zone.type = ZONE_TYPE_SINGLE;
logo_zone.leds_min = 1;
logo_zone.leds_max = 1;
logo_zone.leds_count = 1;
logo_zone.matrix_map = NULL;
zones.push_back(logo_zone);
for(const std::string& zone_name : zone_names)
{
zone new_zone;
new_zone.name = zone_name;
new_zone.type = ZONE_TYPE_SINGLE;
new_zone.leds_min = 1;
new_zone.leds_max = 1;
new_zone.leds_count = 1;
new_zone.matrix_map = NULL;
zones.push_back(new_zone);
led logo_led;
logo_led.name = "Logo LED";
leds.push_back(logo_led);
/*---------------------------------------------------------*\
| Zone 1: Side Bar |
\*---------------------------------------------------------*/
zone sidebar_zone;
sidebar_zone.name = "Side Bar";
sidebar_zone.type = ZONE_TYPE_SINGLE;
sidebar_zone.leds_min = 1;
sidebar_zone.leds_max = 1;
sidebar_zone.leds_count = 1;
sidebar_zone.matrix_map = NULL;
zones.push_back(sidebar_zone);
led sidebar_led;
sidebar_led.name = "Side Bar LED";
leds.push_back(sidebar_led);
/*---------------------------------------------------------*\
| Zone 2: Infinity Mirror |
\*---------------------------------------------------------*/
zone infinity_zone;
infinity_zone.name = "Infinity Mirror";
infinity_zone.type = ZONE_TYPE_SINGLE;
infinity_zone.leds_min = 1;
infinity_zone.leds_max = 1;
infinity_zone.leds_count = 1;
infinity_zone.matrix_map = NULL;
zones.push_back(infinity_zone);
led infinity_led;
infinity_led.name = "Infinity Mirror LED";
leds.push_back(infinity_led);
led new_led;
new_led.name = zone_name + " LED";
leds.push_back(new_led);
}
SetupColors();
}
@@ -362,7 +376,7 @@ void RGBController_ZotacBlackwellGPU::DeviceUpdateZone(int zone)
void RGBController_ZotacBlackwellGPU::DeviceUpdateMode()
{
for(unsigned int zone_idx = 0; zone_idx < ZOTAC_BLACKWELL_GPU_NUM_ZONES; zone_idx++)
for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++)
{
DeviceUpdateZone(zone_idx);
}
@@ -11,13 +11,17 @@
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#include "RGBController.h"
#include "ZotacBlackwellGPUController.h"
class RGBController_ZotacBlackwellGPU : public RGBController
{
public:
RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr);
RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr,
uint16_t device, uint16_t subdevice);
~RGBController_ZotacBlackwellGPU();
void SetupZones();
@@ -33,4 +37,17 @@ public:
private:
ZotacBlackwellGPUController* controller;
std::vector<std::string> zone_names;
struct DeviceZoneConfig
{
uint16_t device;
uint16_t subdevice;
const char* zones[4];
uint8_t zone_count;
};
static const DeviceZoneConfig device_zone_configs[];
static const DeviceZoneConfig* FindZoneConfig(uint16_t device, uint16_t subdevice);
};
@@ -46,14 +46,6 @@
#define ZOTAC_BLACKWELL_GPU_REG_RELOAD 0x11
#define ZOTAC_BLACKWELL_GPU_REG_COMMIT 0x17
/*---------------------------------------------------------*\
| Zone indices |
\*---------------------------------------------------------*/
#define ZOTAC_BLACKWELL_GPU_ZONE_LOGO 0x00
#define ZOTAC_BLACKWELL_GPU_ZONE_SIDEBAR 0x01
#define ZOTAC_BLACKWELL_GPU_ZONE_INFINITY 0x02
#define ZOTAC_BLACKWELL_GPU_NUM_ZONES 3
/*---------------------------------------------------------*\
| Mode values (from Firestorm V5.0.0.012E reverse eng.) |
\*---------------------------------------------------------*/
@@ -20,13 +20,13 @@
* DetectZotacBlackwellGPUControllersPCI *
* *
* Detect ZOTAC Blackwell (RTX 50 series) RGB controllers on the enumerated *
* I2C busses at address 0x4B. *
* I2C busses at address 0x4B. Zone configuration is resolved inside the *
* RGBController by looking up the PCI device/sub-device IDs in a static table. *
* *
* bus - pointer to i2c_smbus_interface where RGB device is connected *
* dev - I2C address of RGB device *
* *
\******************************************************************************************/
void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name)
{
s32 result = bus->i2c_smbus_read_byte_data(i2c_addr, 0x10);
@@ -34,10 +34,13 @@ void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr
if(result >= 0)
{
ZotacBlackwellGPUController* controller = new ZotacBlackwellGPUController(bus, i2c_addr, name);
RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller);
RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller,
bus->pci_device,
bus->pci_subsystem_device);
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
}
REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5080 AMP Extreme INFINITY", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, 0x4B);
REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5090 SOLID OC", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, 0x4B);
+2
View File
@@ -366,6 +366,7 @@
#define ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING 0x89DF
#define ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE 0x8A2C
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING 0x89E3
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2 0x89E4
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF 0x8A3C
#define ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE 0x8A2E
#define ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING 0x89EC
@@ -999,6 +1000,7 @@
#define ZOTAC_RTX4090_TRINITY_SUB_DEV 0x3675
#define ZOTAC_RTX4090_AMP_SUB_DEV 0x4675
#define ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV 0x1762
#define ZOTAC_RTX5090_SOLID_OC_SUB_DEV 0x1761
/*-----------------------------------------------------*\
| Manli Sub-Device IDs |