/*-----------------------------------------*\ | GigabyteRGBFusion2USBController.cpp | | | | Driver for Gigabyte Aorus RGB Fusion 2.0 | | USB lighting controller | | | | Author: jackun 1/8/2020 | | Maintainer: Chris M (Dr_No) | \*-----------------------------------------*/ #include "GigabyteRGBFusion2USBController.h" #include "ResourceManager.h" #include "SettingsManager.h" /*-------------------------------------------------------------------------*\ | This is stored as a uint32_t in the chip so is trasmitted LSB to MSB | | Therefore the numbers represent the index where the controller will find | | respective colour in a regular packet | \*-------------------------------------------------------------------------*/ static RGBCalibration GigabyteCalibrationsLookup { { "BGR", { 0x00, 0x01, 0x02, 0x00} }, { "BRG", { 0x01, 0x00, 0x02, 0x00} }, { "GRB", { 0x02, 0x00, 0x01, 0x00} }, { "GBR", { 0x00, 0x02, 0x01, 0x00} }, { "RGB", { 0x02, 0x01, 0x00, 0x00} }, { "RBG", { 0x01, 0x02, 0x00, 0x00} } }; static calibration GigabyteBoardCalibration { { "D_LED1", "GRB" }, { "D_LED2", "GRB" }, { "Mainboard", "BGR" }, { "Spare", "BGR" } }; static LEDCount LedCountToEnum(unsigned int c) { if(c <= 32) { return(LEDS_32); } else if(c <= 64) { return(LEDS_64); } else if(c <= 256) { return(LEDS_256); } else if(c <= 512) { return(LEDS_512); } else { return(LEDS_1024); } } RGBFusion2USBController::RGBFusion2USBController(hid_device* handle, const char *path, std::string mb_name) : dev(handle) { int res = 0; char text[64] = { 0x00 }; unsigned char buffer[64] = { 0x00 }; if(dev) { SetCalibration(); name = mb_name; /*---------------------------------------------------------*\ | HID report read needs 0x60 packet or it gives IO error | \*---------------------------------------------------------*/ SendPacket(0x60, 0x00); buffer[0] = report_id; res = hid_get_feature_report(dev, buffer, 64); if(res > 0) { report = *reinterpret_cast(buffer); description = std::string(report.str_product, 32); description.erase(std::find(description.begin(), description.end(), '\0'), description.end()); snprintf(text, 11, "0x%08X", report.fw_ver); version = text; snprintf(text, 11, "0x%08X", report.chip_id); chip_id = text; D_LED1_count = LedCountToEnum(report.total_leds & 0x0F); D_LED2_count = LedCountToEnum(report.total_leds & 0xF0); } location = path; EnableBeat(false); } } RGBFusion2USBController::~RGBFusion2USBController() { hid_close(dev); } void RGBFusion2USBController::SetMode(int m) { mode = m; } RGBA RGBFusion2USBController::GetCalibration(std::string rgb_order) { /*-------------------------------------------------*\ | Check for RGB order string in calibration table | | If not found return the "BGR" calibration | \*-------------------------------------------------*/ if(GigabyteCalibrationsLookup.count(rgb_order)) { return GigabyteCalibrationsLookup.find(rgb_order)->second; } else { return GigabyteCalibrationsLookup.find("BGR")->second; } } void RGBFusion2USBController::SetCalibrationBuffer(std::string rgb_order, uint8_t* buffer, uint8_t offset) { RGBA rgb_cal; int raw_size = sizeof(rgb_cal.raw) / sizeof(rgb_cal.raw[0]); rgb_cal = GetCalibration(rgb_order); for(int i = 0; i < raw_size; i++) { buffer[offset + i] = rgb_cal.raw[i]; } } /*---------------------------------------------------------------------------------------------*\ | Sets RGB color mapping to LED pins. | | "Custom" RGB packets don't seem to get remapped so use report.byteorderN and do it manually. | | Of course it all depends how we send data to the controller, but bios/rgb fusion 2 itself | | set it up like this. | \*---------------------------------------------------------------------------------------------*/ void RGBFusion2USBController::SetCalibration() { const std::string detector_name = "Gigabyte RGB Fusion 2 USB"; const std::string json_cal = "Calibration"; SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); json device_settings = settings_manager->GetSettings(detector_name); /*---------------------------------------------------------*\ | Get Layouts from the settings manager | | If Calibration settings are not found then write them out | | Calibration will only be executed if it is explicitly | | enabled by the user | \*---------------------------------------------------------*/ if(!device_settings.contains(json_cal)) { device_settings[json_cal]["Enabled"] = false; device_settings[json_cal]["Data"] = GigabyteBoardCalibration; settings_manager->SetSettings(detector_name, device_settings); settings_manager->SaveSettings(); } else if(device_settings[json_cal]["Enabled"]) { GigabyteBoardCalibration["D_LED1"] = device_settings[json_cal]["Data"]["D_LED1"]; GigabyteBoardCalibration["D_LED2"] = device_settings[json_cal]["Data"]["D_LED2"]; GigabyteBoardCalibration["Mainboard"] = device_settings[json_cal]["Data"]["Mainboard"]; GigabyteBoardCalibration["Spare"] = device_settings[json_cal]["Data"]["Spare"]; uint8_t buffer[64] = { 0x00 }; buffer[0] = report_id; buffer[1] = 0x33; SetCalibrationBuffer( GigabyteBoardCalibration.find("D_LED1")->second, buffer, 2); SetCalibrationBuffer( GigabyteBoardCalibration.find("D_LED2")->second, buffer, 6); SetCalibrationBuffer( GigabyteBoardCalibration.find("Mainboard")->second, buffer, 10); SetCalibrationBuffer( GigabyteBoardCalibration.find("Spare")->second, buffer, 14); SendPacket(buffer); } } void RGBFusion2USBController::SetLedCount(unsigned int led, unsigned int count) { /*-----------------------------------------------------------------*\ | Check which Digital LED we're setting then send the value of both | \*-----------------------------------------------------------------*/ if(led == HDR_D_LED1) { D_LED1_count = LedCountToEnum(count); } else { D_LED2_count = LedCountToEnum(count); } SendPacket(0x34, D_LED1_count | (D_LED2_count << 4)); } bool RGBFusion2USBController::DisableBuiltinEffect(int enable_bit, int mask) { if(effect_disabled & enable_bit) { return(true); } effect_disabled &= ~mask; effect_disabled |= enable_bit; int res = SendPacket(0x32, effect_disabled); /*-----------------------------------------------------------------*\ | Sometimes effect doesn't apply at first, delay a little and let | | MCU react, if this packet is the cause | \*-----------------------------------------------------------------*/ std::this_thread::sleep_for(std::chrono::milliseconds(50)); return res; } bool RGBFusion2USBController::EnableBeat(bool e) { return SendPacket(0x31, e ? 1 : 0); } std::string RGBFusion2USBController::GetDeviceName() { return(name); } std::string RGBFusion2USBController::GetDeviceDescription() { return(description); } std::string RGBFusion2USBController::GetFWVersion() { return(version); } std::string RGBFusion2USBController::GetDeviceLocation() { return("HID: " + location); } std::string RGBFusion2USBController::GetSerial() { return(chip_id); } void RGBFusion2USBController::SetStripColors ( unsigned int hdr, RGBColor * colors, unsigned int num_colors, int single_led ) { PktRGB pkt; pkt.Init(hdr, report_id); /*-------------------------------------------------------------------------*\ | FIXME assuming that LED strips ports are 0x58/0x59 for all boards | \*-------------------------------------------------------------------------*/ uint32_t byteorder; if(hdr == HDR_D_LED1_RGB) { byteorder = report.byteorder0; } else { byteorder = report.byteorder1; } unsigned char bo_r = byteorder >> 16; unsigned char bo_g = byteorder >> 8; unsigned char bo_b = byteorder & 0xFF; int res; int leds_left = num_colors; int sent_data = 0; int k = 0; int leds_in_pkt = sizeof(pkt.s.leds) / sizeof(*pkt.s.leds); /* 19 */ /*-------------------------------------------------------------------------*\ | Other leds stay at whatever the builtin effect was doing at that moment | | if breathing/pulse effect faded out then they stay dark | \*-------------------------------------------------------------------------*/ if(single_led > -1) { leds_left = 1; k = single_led; sent_data = k * 3; leds_in_pkt = 1; } while(leds_left > 0) { leds_in_pkt = (std::min)(leds_in_pkt, leds_left); leds_left -= leds_in_pkt; pkt.s.bcount = leds_in_pkt * 3; pkt.s.boffset = sent_data; sent_data += pkt.s.bcount; for(int i = 0; i < leds_in_pkt; i++) { RGBColor color = colors[k]; unsigned char red = RGBGetRValue(color); unsigned char grn = RGBGetGValue(color); unsigned char blu = RGBGetBValue(color); pkt.buffer[5 + i * 3 + bo_r] = red; pkt.buffer[5 + i * 3 + bo_g] = grn; pkt.buffer[5 + i * 3 + bo_b] = blu; k++; } res = SendPacket(pkt.buffer); if(res < 0) { return; } } if(hdr == HDR_D_LED1_RGB) { DisableBuiltinEffect(0x01, 0x01); } else { DisableBuiltinEffect(0x02, 0x02); } } static const std::array< std::array, 5> speeds = { { {1600, 1600, 200}, {1200, 1200, 200}, {800, 800, 200}, {400, 400, 200}, {200, 200, 200}, }, }; void RGBFusion2USBController::SetLEDEffect(unsigned int led, int mode, unsigned int speed, bool random, unsigned char r, unsigned char g, unsigned char b) { PktEffect pkt; pkt.Init(led, report_id); pkt.e.effect_type = mode; pkt.e.color0 = r << 16 | g << 8 | b; if(speed < speeds.size()) { const std::array& s = speeds[speed]; pkt.e.period0 = s[0]; pkt.e.period1 = s[1]; pkt.e.period2 = s[2]; } switch(mode) { case 0: break; // Static case 1: break; // Breathing case 2: // Blink case 3: if(random) { pkt.e.effect_param0 = 7; // cycle through up to 7 (max?) colors } break; // Color Cycle case 4: pkt.e.effect_param0 = 7; // cycle through up to 7 (max?) colors break; // "Fake" effects case 10: // flashing, flashing color cycle pkt.e.period0 = 200; pkt.e.period1 = 200; pkt.e.period2 = 5000 - 1000 * speed; // time between flashing, doesn't seem to be affected by period0/period1 pkt.e.effect_type = 3; pkt.e.effect_param2 = 2; // flash twice if (random) { pkt.e.effect_param0 = 7; } break; } SendPacket(pkt.buffer); } bool RGBFusion2USBController::ApplyEffect() { return SendPacket(0x28, 0xFF); } bool RGBFusion2USBController::SendPacket(uint8_t a, uint8_t b, uint8_t c) { unsigned char buffer[64] {}; buffer[0] = report_id; buffer[1] = a; buffer[2] = b; buffer[3] = c; return(SendPacket(buffer) == 64); } int RGBFusion2USBController::SendPacket(unsigned char* packet) { return hid_send_feature_report(dev, packet, 64); }