/*---------------------------------------------*\ | MSIMysticLight185Controller.cpp | | | | Driver for MSI Mystic Light (185-byte) | | USB lighting controller | | | | T-bond 3/4/2020 | | Adam Honse 3/6/2021 | | | | The direct mode part has been implemented | | based on the mystic-why project provided by | | Aleksandr Garashchenko | | (https://github.com/garashchenko/mystic-why) | \*---------------------------------------------*/ #include "MSIMysticLight185Controller.h" #include #include #include using namespace std::chrono_literals; #define BITSET(val, bit, pos) ((unsigned char)std::bitset<8>(val).set((pos), (bit)).to_ulong()) #define SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT 40 #define SYNC_PER_LED_MODE_CORSAIR_LED_COUNT 120 #define JRAINBOW1_MAX_LED_COUNT 200 #define JRAINBOW2_MAX_LED_COUNT 240 #define JCORSAIR_MAX_LED_COUNT 240 #define MSI_DIRECT_MODE 0x25 #define PER_LED_BASIC_SYNC_MODE (0x80 | SYNC_SETTING_ONBOARD | SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2) #define PER_LED_FULL_SYNC_MODE (PER_LED_BASIC_SYNC_MODE | SYNC_SETTING_JRAINBOW1 | SYNC_SETTING_JRAINBOW2 | SYNC_SETTING_JCORSAIR) struct Config { unsigned short pid; // PID of the board int numof_onboard_leds; // number of onboard leds int numof_pipe1_leds; // number of pipe 1 leds (used in per LED mode only) int numof_pipe2_leds; // number of pipe 2 leds (used in per LED mode only) int numof_JRGBs; // number of supported JRGB headers (used in per LED mode only) const std::vector* supported_zones; // pointer to vector of supported zones MSIMysticLight185Controller::DIRECT_MODE per_led_mode; // type of direct mode support }; const std::vector all_zones = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RGB_2, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR, MSI_ZONE_J_PIPE_1, MSI_ZONE_J_PIPE_2, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set0 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RGB_2, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set1 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR }; const std::vector zones_set2 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set3 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR, MSI_ZONE_J_PIPE_1, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set4 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR, MSI_ZONE_J_PIPE_1, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set5 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1 }; const std::vector zones_set6 = { MSI_ZONE_J_RAINBOW_1 }; const std::vector zones_set7 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RGB_2, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_PIPE_1, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set8 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR, MSI_ZONE_J_PIPE_1, MSI_ZONE_J_PIPE_2 }; const std::vector zones_set9 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_CORSAIR, MSI_ZONE_J_PIPE_1 }; const std::vector zones_set10 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_RAINBOW_3 }; const std::vector zones_set11 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2 }; const std::vector zones_set12 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_RAINBOW_3, MSI_ZONE_J_PIPE_1, MSI_ZONE_ON_BOARD_LED_0 }; const std::vector zones_set13 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RGB_2, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2 }; const std::vector zones_set14 = { MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2 }; const std::vector zones_set15 = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_RAINBOW_2, MSI_ZONE_J_RAINBOW_3, MSI_ZONE_J_PIPE_1, MSI_ZONE_J_PIPE_2, MSI_ZONE_ON_BOARD_LED_0 }; /*---------------------------------------------------------------------------------------------------------------------------------*\ | Definition of the board sepcific configurations (number of onboard LEDs and supported zones). | | | | Only tested boards are listed here (refer to MSIMysticLightControllerDetect.cpp). If more boards | | are tested the list must be extended here. Otherwise the default settings will be used (6 onboard LEDs, 2 JRGB, no direct mode). | | Boards with yet unknown supported zones are configured to support all zones. | \*---------------------------------------------------------------------------------------------------------------------------------*/ #define NUMOF_CONFIGS (sizeof(board_configs) / sizeof(Config)) static const Config board_configs[] = { { 0x7B93, 6, 1, 0, 1, &zones_set4, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MPG X570 GAMING PRO CARBON WIFI { 0x7C34, 0, 1, 1, 1, &zones_set8, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MEG X570 GODLIKE { 0x7C35, 0, 1, 0, 1, &zones_set9, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MEG X570 ACE { 0x7C36, 6, 1, 0, 1, &zones_set4, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // PRESTIGE X570 CREATION { 0x7C37, 1, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG X570 GAMING PLUS { 0x7C56, 6, 0, 0, 1, &zones_set2, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B550 GAMING PLUS { 0x7C59, 0, 8, 0, 1, &zones_set9, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // CREATOR TRX40 { 0x7C60, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // TRX40-A PRO { 0x7C67, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MAG B365M MORTAR { 0x7C71, 6, 6, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z490 ACE { 0x7C73, 6, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING CARBON WIFI { 0x7C75, 6, 0, 0, 1, &zones_set2, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING PLUS { 0x7C76, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG Z490M GAMING EDGE { 0x7C77, 0, 0, 0, 0, &zones_set14, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z490I UNIFY { 0x7C79, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING EDGE WIFI { 0x7C80, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z490 TOMAHAWK { 0x7C81, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B460 TOMAHAWK { 0x7C83, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // B460M PRO-VDH WIFI { 0x7C84, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MAG X570 TOMAHAWK WIFI { 0x7C86, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG B460I GAMING EDGE { 0x7C90, 6, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B550 GAMING CARBON WIFI { 0x7C91, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B550 TOMAHAWK { 0x7C92, 6, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG B550I GAMING EDGE WIFI { 0x7C94, 6, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B550M MORTAR { 0x7C95, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B550M PRO-VDH WIFI { 0x7C98, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // Z490 PLUS { 0x7D06, 4, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG Z590 GAMING FORCE { 0x7D07, 4, 5, 0, 2, &zones_set7, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z590 GAMING EDGE WIFI { 0x7D08, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z590 TOMAHAWK { 0x7D09, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // Z590-A PRO WIFI { 0x7D13, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MEG B550 UNIFY { 0x7D15, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560 TOMAHAWK WIFI { 0x7D17, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M MORTAR { 0x7D18, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M PRO-VDH { 0x7D19, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B560I GAMIING EDGE WIFI { 0x7D20, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M PRO { 0x7D25, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z690-A WIFI DDR4 { 0x7D27, 6, 0, 0, 2, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z690 ACE { 0x7D28, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z690 UNIFY-X { 0x7D29, 6, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MEG Z690I UNIFY { 0x7D30, 6, 6, 0, 2, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z690 CARBON WIFI { 0x7D31, 4, 8, 0, 2, &zones_set12, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG EDGE WIFI DDR4 { 0x7D32, 1, 0, 0, 1, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z690 TOMAHAWK WIFI DDR4 { 0x7D36, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z690-P DDR4 { 0x7D38, 0, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z590 UNIFY-X { 0x7D41, 6, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660M TOMAHAWK WIFI DDR4 { 0x7D42, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660 MORTAR WIFI DDR4 { 0x7D43, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B660M-A WIFI DDR4 { 0x7D50, 6, 12, 0, 1, &zones_set4, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X570S ACE MAX { 0x7D51, 6, 0, 0, 2, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X570S UNIFY-X MAX { 0x7D52, 6, 14, 0, 1, &zones_set4, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG X570S CARBON EK X { 0x7D53, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG X570S EDGE MAX WIFI { 0x7D54, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X570S TOMAHAWK MAX WIFI { 0x7D59, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B660-A DDR4 { 0x7D69, 9, 2, 4, 1, &zones_set15, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X670E ACE { 0x7D91, 1, 0, 0, 1, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z790 TOMAHAWK WIFI { 0x7E06, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z790-P WIFI DDR4 { 0x7D77, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B650M-A WIFI }; FeaturePacket_185 enable_per_led_msg; Color* per_led_onboard_leds; Color* per_led_jpipe1; Color* per_led_jpipe2; Color* per_led_jrgb; Color* per_led_jrainbow1_sync; Color* per_led_jrainbow2_sync; Color* per_led_jcorsair_sync; MSIMysticLight185Controller::MSIMysticLight185Controller ( hid_device* handle, const char *path, unsigned short pid ) { dev = handle; if(dev) { location = path; ReadName(); ReadSerial(); ReadFwVersion(); ReadSettings(); } /*---------------------------------------------*\ | Initialize save flag and some static settings | \*---------------------------------------------*/ data.save_data = 0; data.on_board_led.colorFlags = 0x80 | SYNC_SETTING_ONBOARD; // always enable onboard sync flag to have expected zone control const Config* board_config = nullptr; for(std::size_t i = 0; i < NUMOF_CONFIGS; ++i) { if(board_configs[i].pid == pid) { board_config = &board_configs[i]; break; } } if(board_config != nullptr) { numof_onboard_leds = board_config->numof_onboard_leds; numof_pipe1_leds = board_config->numof_pipe1_leds; numof_pipe2_leds = board_config->numof_pipe2_leds; numof_JRGBs = board_config->numof_JRGBs; supported_zones = board_config->supported_zones; per_led_mode = board_config->per_led_mode; } else { numof_onboard_leds = 6; numof_pipe1_leds = 1; numof_pipe2_leds = 1; numof_JRGBs = 2; supported_zones = &all_zones; per_led_mode = DIRECT_MODE_DISABLED; } /*-----------------------------------------*\ | Initialize per-LED data | \*-----------------------------------------*/ std::memset(per_led_data_onboard_and_sync.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); std::memset(per_led_data_jrainbow1.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); per_led_data_jrainbow1.hdr1 = 4; std::memset(per_led_data_jrainbow2.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); per_led_data_jrainbow2.hdr1 = 4; per_led_data_jrainbow2.hdr2 = 1; std::memset(per_led_data_jcorsair.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); per_led_data_jcorsair.hdr1 = 5; per_led_onboard_leds = per_led_data_onboard_and_sync.leds; per_led_jpipe1 = per_led_onboard_leds + numof_onboard_leds; per_led_jpipe2 = per_led_jpipe1 + numof_pipe1_leds; per_led_jrgb = per_led_jpipe2 + numof_pipe2_leds; per_led_jrainbow1_sync = per_led_jrgb + numof_JRGBs; per_led_jrainbow2_sync = per_led_jrainbow1_sync + SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; per_led_jcorsair_sync = per_led_jrainbow2_sync + SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; no_onboards = true; no_jrainbow1 = true; no_jrainbow2 = true; no_jcorsair = true; for(std::size_t i = 0; i < supported_zones->size(); ++i) { switch((*supported_zones)[i]) { case MSI_ZONE_ON_BOARD_LED_0: no_onboards = false; break; case MSI_ZONE_J_RAINBOW_1: no_jrainbow1 = false; break; case MSI_ZONE_J_RAINBOW_2: no_jrainbow2 = false; break; case MSI_ZONE_J_CORSAIR: case MSI_ZONE_J_RAINBOW_3: no_jcorsair = false; break; default: break; } } /*---------------------------------------------------------------------------------------------------------*\ | Set up per-LED switching message for synchronized mode. | | Static initialization made problems with some compilers. Therefore this is done programmatically here. | \*---------------------------------------------------------------------------------------------------------*/ enable_per_led_msg.j_rgb_1.speedAndBrightnessFlags = 0x08; enable_per_led_msg.j_rgb_1.colorFlags = 0x80; enable_per_led_msg.j_pipe_1.speedAndBrightnessFlags = 0x2A; enable_per_led_msg.j_pipe_1.colorFlags = 0x80; enable_per_led_msg.j_pipe_2.speedAndBrightnessFlags = 0x2A; enable_per_led_msg.j_pipe_2.colorFlags = 0x80; enable_per_led_msg.j_rainbow_1.speedAndBrightnessFlags = 0x29; enable_per_led_msg.j_rainbow_1.colorFlags = 0x80; enable_per_led_msg.j_rainbow_1.cycle_or_led_num = 0x28; enable_per_led_msg.j_rainbow_2.speedAndBrightnessFlags = 0x29; enable_per_led_msg.j_rainbow_2.colorFlags = 0x80; enable_per_led_msg.j_rainbow_2.cycle_or_led_num = 0x28; enable_per_led_msg.j_corsair.fan_flags = 0x29; enable_per_led_msg.j_corsair.corsair_quantity = 0x00; enable_per_led_msg.j_corsair.padding[2] = 0x82; enable_per_led_msg.j_corsair.is_individual = 0x78; enable_per_led_msg.j_corsair_outerll120.speedAndBrightnessFlags = 0x28; enable_per_led_msg.j_corsair_outerll120.colorFlags = 0x80; enable_per_led_msg.on_board_led.effect = MSI_DIRECT_MODE; enable_per_led_msg.on_board_led.speedAndBrightnessFlags = 0x29 | SYNC_SETTING_JRGB; enable_per_led_msg.on_board_led.colorFlags = PER_LED_FULL_SYNC_MODE; enable_per_led_msg.on_board_led_1.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_1.colorFlags = 0x80; enable_per_led_msg.on_board_led_2.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_2.colorFlags = 0x80; enable_per_led_msg.on_board_led_3.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_3.colorFlags = 0x80; enable_per_led_msg.on_board_led_4.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_4.colorFlags = 0x80; enable_per_led_msg.on_board_led_5.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_5.colorFlags = 0x80; enable_per_led_msg.on_board_led_6.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_6.colorFlags = 0x80; enable_per_led_msg.on_board_led_7.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_7.colorFlags = 0x80; enable_per_led_msg.on_board_led_8.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_8.colorFlags = 0x80; enable_per_led_msg.on_board_led_9.speedAndBrightnessFlags = 0x28; enable_per_led_msg.on_board_led_9.colorFlags = 0x80; enable_per_led_msg.j_rgb_2.speedAndBrightnessFlags = 0x2A; enable_per_led_msg.j_rgb_2.colorFlags = 0x80; /*-----------------------------------------*\ | Initialize zone based per LED data | \*-----------------------------------------*/ zone_based_per_led_data.j_rgb_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_rgb_1.colorFlags = BITSET(zone_based_per_led_data.j_rgb_1.colorFlags, true, 7u); zone_based_per_led_data.j_pipe_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_pipe_1.colorFlags = BITSET(zone_based_per_led_data.j_pipe_1.colorFlags, true, 7u); zone_based_per_led_data.j_pipe_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_pipe_2.colorFlags = BITSET(zone_based_per_led_data.j_pipe_2.colorFlags, true, 7u); zone_based_per_led_data.j_rainbow_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_rainbow_1.colorFlags = BITSET(zone_based_per_led_data.j_rainbow_1.colorFlags, true, 7u); zone_based_per_led_data.j_rainbow_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_rainbow_2.colorFlags = BITSET(zone_based_per_led_data.j_rainbow_2.colorFlags, true, 7u); zone_based_per_led_data.on_board_led.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led.colorFlags = BITSET(zone_based_per_led_data.on_board_led.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_1.colorFlags = BITSET(zone_based_per_led_data.on_board_led_1.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_2.colorFlags = BITSET(zone_based_per_led_data.on_board_led_2.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_3.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_3.colorFlags = BITSET(zone_based_per_led_data.on_board_led_3.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_4.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_4.colorFlags = BITSET(zone_based_per_led_data.on_board_led_4.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_5.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_5.colorFlags = BITSET(zone_based_per_led_data.on_board_led_5.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_6.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2 << 2; zone_based_per_led_data.on_board_led_6.colorFlags = BITSET(zone_based_per_led_data.on_board_led_6.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_7.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100; zone_based_per_led_data.on_board_led_7.colorFlags = BITSET(zone_based_per_led_data.on_board_led_7.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_8.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_8.colorFlags = BITSET(zone_based_per_led_data.on_board_led_8.colorFlags, true, 7u); zone_based_per_led_data.on_board_led_9.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.on_board_led_9.colorFlags = BITSET(zone_based_per_led_data.on_board_led_9.colorFlags, true, 7u); zone_based_per_led_data.j_rgb_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; zone_based_per_led_data.j_rgb_2.colorFlags = BITSET(zone_based_per_led_data.j_rgb_2.colorFlags, true, 7u); zone_based_per_led_data.save_data = 0; direct_mode = false; sync_direct_mode = true; } MSIMysticLight185Controller::~MSIMysticLight185Controller() { hid_close(dev); } void MSIMysticLight185Controller::SetMode ( MSI_ZONE zone, MSI_MODE mode, MSI_SPEED speed, MSI_BRIGHTNESS brightness, bool rainbow_color ) { if((per_led_mode == DIRECT_MODE_ZONE_BASED) && (zone > MSI_ZONE_ON_BOARD_LED_0)) { return; } ZoneData* zone_data = GetZoneData(data, zone); if(zone_data == nullptr) { return; } zone_data->effect = mode; zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); zone == MSI_ZONE_J_RAINBOW_3 ? zone_data->padding = 4 : zone_data->padding = 0; ZoneData* on_board_zone = GetZoneData(data, MSI_ZONE_ON_BOARD_LED_0); if(no_onboards && ((zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2))) { on_board_zone->effect = zone_data->effect; on_board_zone->speedAndBrightnessFlags = zone_data->speedAndBrightnessFlags; on_board_zone->colorFlags = zone_data->colorFlags; on_board_zone->colorFlags |= SYNC_SETTING_ONBOARD; on_board_zone->padding = 0x00; } if(mode > MSI_MODE_LIGHTNING) { on_board_zone->speedAndBrightnessFlags |= SYNC_SETTING_JRGB; on_board_zone->colorFlags |= (SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2); } else { on_board_zone->speedAndBrightnessFlags &= ~SYNC_SETTING_JRGB; on_board_zone->colorFlags &= ~(SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2); } if((zone == MSI_ZONE_ON_BOARD_LED_0) && (mode <= MSI_MODE_LIGHTNING)) { for(int i = 0; i < numof_onboard_leds; ++i) { zone_data = GetZoneData(data, (MSI_ZONE)((int)MSI_ZONE_ON_BOARD_LED_1 + i)); if(zone_data != nullptr) { zone_data->effect = mode; zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); zone_data->padding = 0x00; } } } } std::string MSIMysticLight185Controller::GetDeviceName() { return name; } std::string MSIMysticLight185Controller::GetFWVersion() { return std::string("AP/LD ").append(version_APROM).append(" / ").append(version_LDROM); } std::string MSIMysticLight185Controller::GetDeviceLocation() { return("HID: " + location); } std::string MSIMysticLight185Controller::GetSerial() { return chip_id; } bool MSIMysticLight185Controller::ReadSettings() { /*-----------------------------------------------------*\ | Read packet from hardware, return true if successful | \*-----------------------------------------------------*/ return (hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data); } bool MSIMysticLight185Controller::Update ( bool save ) { /*-----------------------------------------------------*\ | Send packet to hardware, return true if successful | \*-----------------------------------------------------*/ if(direct_mode) { if(per_led_mode == DIRECT_MODE_PER_LED) { if(sync_direct_mode) { return (hid_send_feature_report(dev, (unsigned char*)&per_led_data_onboard_and_sync, sizeof(per_led_data_onboard_and_sync)) == sizeof(per_led_data_onboard_and_sync)); } else { if(!no_jrainbow1) { (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jrainbow1, sizeof(per_led_data_jrainbow1)); std::this_thread::sleep_for(13ms); } if(!no_jrainbow2) { (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jrainbow2, sizeof(per_led_data_jrainbow2)); std::this_thread::sleep_for(13ms); } if(!no_jcorsair) { (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jcorsair, sizeof(per_led_data_jcorsair)); std::this_thread::sleep_for(13ms); } return (hid_send_feature_report(dev, (unsigned char*)&per_led_data_onboard_and_sync, sizeof(per_led_data_onboard_and_sync)) == sizeof(per_led_data_onboard_and_sync)); } } else { return (hid_send_feature_report(dev, (unsigned char*)&zone_based_per_led_data, sizeof(zone_based_per_led_data)) == sizeof(zone_based_per_led_data)); } } else { data.save_data = save; return (hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof(data)); } } void MSIMysticLight185Controller::SetZoneColor ( MSI_ZONE zone, unsigned char red1, unsigned char grn1, unsigned char blu1, unsigned char red2, unsigned char grn2, unsigned char blu2 ) { if((per_led_mode == DIRECT_MODE_ZONE_BASED) && (zone > MSI_ZONE_ON_BOARD_LED_0)) { return; } ZoneData* zone_data = GetZoneData(data, zone); if(zone_data == nullptr) { return; } zone_data->color.R = red1; zone_data->color.G = grn1; zone_data->color.B = blu1; zone_data->color2.R = red2; zone_data->color2.G = grn2; zone_data->color2.B = blu2; if(no_onboards && ((zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2))) { ZoneData* on_board_zone = GetZoneData(data, MSI_ZONE_ON_BOARD_LED_0); on_board_zone->color.R = red1; on_board_zone->color.G = grn1; on_board_zone->color.B = blu1; on_board_zone->color2.R = red2; on_board_zone->color2.G = grn2; on_board_zone->color2.B = blu2; } if(zone == MSI_ZONE_ON_BOARD_LED_0) { for(int i = 0; i < numof_onboard_leds; ++i) { zone_data = GetZoneData(data, (MSI_ZONE)((int)MSI_ZONE_ON_BOARD_LED_1 + i)); if(zone_data != nullptr) { zone_data->color.R = red1; zone_data->color.G = grn1; zone_data->color.B = blu1; zone_data->color2.R = red2; zone_data->color2.G = grn2; zone_data->color2.B = blu2; } } } } void MSIMysticLight185Controller::SetLedColor ( MSI_ZONE zone, int index, unsigned char red, unsigned char grn, unsigned char blu ) { if(per_led_mode == DIRECT_MODE_PER_LED) { Color* zone_data = GetPerLedZoneData(zone); if(zone_data != nullptr) { int maxSize = (int)GetMaxDirectLeds(zone); if(sync_direct_mode) { switch(zone) { case MSI_ZONE_J_RAINBOW_1: case MSI_ZONE_J_RAINBOW_2: maxSize = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; break; case MSI_ZONE_J_RAINBOW_3: case MSI_ZONE_J_CORSAIR: maxSize = SYNC_PER_LED_MODE_CORSAIR_LED_COUNT; break; default: break; } } if(index < maxSize) { zone_data[index].R = red; zone_data[index].G = grn; zone_data[index].B = blu; } } } else { if(((zone == MSI_ZONE_J_RAINBOW_1) || (zone == MSI_ZONE_J_RAINBOW_2) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2)) && (index != 0)) { return; } if(zone >= MSI_ZONE_ON_BOARD_LED_0) { zone = (MSI_ZONE)((int)zone + index + 1); } ZoneData *zone_data = GetZoneData(zone_based_per_led_data, zone); if(zone_data == nullptr) { return; } zone_data->color.R = red; zone_data->color.G = grn; zone_data->color.B = blu; zone_data->color2.R = red; zone_data->color2.G = grn; zone_data->color2.B = blu; } } ZoneData *MSIMysticLight185Controller::GetZoneData ( FeaturePacket_185& data_packet, MSI_ZONE zone ) { switch(zone) { case MSI_ZONE_J_RGB_1: return &data_packet.j_rgb_1; case MSI_ZONE_J_RGB_2: return &data_packet.j_rgb_2; case MSI_ZONE_J_RAINBOW_1: return &data_packet.j_rainbow_1; case MSI_ZONE_J_RAINBOW_2: return &data_packet.j_rainbow_2; case MSI_ZONE_J_RAINBOW_3: return (ZoneData*)&data_packet.j_corsair; case MSI_ZONE_J_PIPE_1: return &data_packet.j_pipe_1; case MSI_ZONE_J_PIPE_2: return &data_packet.j_pipe_2; case MSI_ZONE_ON_BOARD_LED_0: return &data_packet.on_board_led; case MSI_ZONE_ON_BOARD_LED_1: return &data_packet.on_board_led_1; case MSI_ZONE_ON_BOARD_LED_2: return &data_packet.on_board_led_2; case MSI_ZONE_ON_BOARD_LED_3: return &data_packet.on_board_led_3; case MSI_ZONE_ON_BOARD_LED_4: return &data_packet.on_board_led_4; case MSI_ZONE_ON_BOARD_LED_5: return &data_packet.on_board_led_5; case MSI_ZONE_ON_BOARD_LED_6: return &data_packet.on_board_led_6; case MSI_ZONE_ON_BOARD_LED_7: return &data_packet.on_board_led_7; case MSI_ZONE_ON_BOARD_LED_8: return &data_packet.on_board_led_8; case MSI_ZONE_ON_BOARD_LED_9: return &data_packet.on_board_led_9; case MSI_ZONE_J_CORSAIR_OUTERLL120: return &data_packet.j_corsair_outerll120; case MSI_ZONE_J_CORSAIR: return (ZoneData*)&data_packet.j_corsair; default: break; } return nullptr; } Color *MSIMysticLight185Controller::GetPerLedZoneData ( MSI_ZONE zone ) { switch(zone) { case MSI_ZONE_J_RAINBOW_1: if(sync_direct_mode) { return per_led_jrainbow1_sync; } else { return per_led_data_jrainbow1.leds; } case MSI_ZONE_J_RAINBOW_2: if(sync_direct_mode) { return per_led_jrainbow2_sync; } else { return per_led_data_jrainbow2.leds; } case MSI_ZONE_ON_BOARD_LED_0: return per_led_onboard_leds; case MSI_ZONE_J_RAINBOW_3: case MSI_ZONE_J_CORSAIR: if(sync_direct_mode) { return per_led_jcorsair_sync; } else { return per_led_data_jcorsair.leds; } case MSI_ZONE_J_RGB_1: return per_led_jrgb; case MSI_ZONE_J_RGB_2: return per_led_jrgb + 1; case MSI_ZONE_J_PIPE_1: return per_led_jpipe1; case MSI_ZONE_J_PIPE_2: return per_led_jpipe2; default: break; } return nullptr; } RainbowZoneData *MSIMysticLight185Controller::GetRainbowZoneData ( MSI_ZONE zone ) { switch(zone) { case MSI_ZONE_J_RAINBOW_1: return &data.j_rainbow_1; case MSI_ZONE_J_RAINBOW_2: return &data.j_rainbow_2; case MSI_ZONE_J_RAINBOW_3: return (RainbowZoneData*)&data.j_corsair; case MSI_ZONE_J_CORSAIR: default: return nullptr; } } bool MSIMysticLight185Controller::ReadFwVersion() { unsigned char request[64]; unsigned char response[64]; int ret_val = 64; /*-----------------------------------------------------*\ | First read the APROM | | Checksum also available at report ID 180, with MSB | | stored at index 0x08 and LSB at 0x09 | \*-----------------------------------------------------*/ /*-----------------------------------------------------*\ | Zero out buffers | \*-----------------------------------------------------*/ memset(request, 0x00, sizeof(request)); memset(response, 0x00, sizeof(response)); /*-----------------------------------------------------*\ | Set up APROM Firmware Version Request packet | \*-----------------------------------------------------*/ request[0x00] = 0x01; request[0x01] = 0xB0; /*-----------------------------------------------------*\ | Fill request from 0x02 to 0x61 with 0xCC | \*-----------------------------------------------------*/ memset(&request[0x02], 0xCC, sizeof(request) - 2); /*-----------------------------------------------------*\ | Send request and receive response packets | \*-----------------------------------------------------*/ ret_val &= hid_write(dev, request, 64); ret_val &= hid_read(dev, response, 64); /*-----------------------------------------------------*\ | Extract high and low values from response | \*-----------------------------------------------------*/ unsigned char highValue = response[2] >> 4; unsigned char lowValue = response[2] & 0x0F; /*-----------------------------------------------------*\ | Build firmware string . | \*-----------------------------------------------------*/ version_APROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); /*-----------------------------------------------------*\ | First read the LDROM | | Checksum also available at report ID 184, with MSB | | stored at index 0x08 and LSB at 0x09 | \*-----------------------------------------------------*/ /*-----------------------------------------------------*\ | Set up LDROM Firmware Version Request packet | \*-----------------------------------------------------*/ request[0x00] = 0x01; request[0x01] = 0xB6; /*-----------------------------------------------------*\ | Send request and receive response packets | \*-----------------------------------------------------*/ ret_val &= hid_write(dev, request, 64); ret_val &= hid_read(dev, response, 64); /*-----------------------------------------------------*\ | Extract high and low values from response | \*-----------------------------------------------------*/ highValue = response[2] >> 4; lowValue = response[2] & 0x0F; /*-----------------------------------------------------*\ | Build firmware string . | \*-----------------------------------------------------*/ version_LDROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); /*-----------------------------------------------------*\ | If return value is zero it means an HID transfer | | failed | \*-----------------------------------------------------*/ return (ret_val > 0); } void MSIMysticLight185Controller::ReadSerial() { wchar_t serial[256]; /*-----------------------------------------------------*\ | Get the serial number string from HID | \*-----------------------------------------------------*/ hid_get_serial_number_string(dev, serial, 256); /*-----------------------------------------------------*\ | Convert wchar_t into std::wstring into std::string | \*-----------------------------------------------------*/ std::wstring wserial = std::wstring(serial); chip_id = std::string(wserial.begin(), wserial.end()); } void MSIMysticLight185Controller::ReadName() { wchar_t tname[256]; /*-----------------------------------------------------*\ | Get the manufacturer string from HID | \*-----------------------------------------------------*/ hid_get_manufacturer_string(dev, tname, 256); /*-----------------------------------------------------*\ | Convert wchar_t into std::wstring into std::string | \*-----------------------------------------------------*/ std::wstring wname = std::wstring(tname); name = std::string(wname.begin(), wname.end()); /*-----------------------------------------------------*\ | Get the product string from HID | \*-----------------------------------------------------*/ hid_get_product_string(dev, tname, 256); /*-----------------------------------------------------*\ | Append the product string to the manufacturer string | \*-----------------------------------------------------*/ wname = std::wstring(tname); name.append(" ").append(std::string(wname.begin(), wname.end())); } MSI_MODE MSIMysticLight185Controller::GetMode() { if(data.on_board_led.effect == MSI_DIRECT_MODE) { direct_mode = true; return MSI_MODE_DIRECT_DUMMY; } else { return (MSI_MODE)data.j_rainbow_1.effect; } } void MSIMysticLight185Controller::GetMode ( MSI_ZONE zone, MSI_MODE &mode, MSI_SPEED &speed, MSI_BRIGHTNESS &brightness, bool &rainbow_color, unsigned int &color ) { /*-----------------------------------------------------*\ | Get data for given zone | \*-----------------------------------------------------*/ ZoneData *zone_data = GetZoneData(data, zone); /*-----------------------------------------------------*\ | Return if zone is invalid | \*-----------------------------------------------------*/ if(!zone_data) { return; } /*-----------------------------------------------------*\ | Update pointers with data | \*-----------------------------------------------------*/ mode = (MSI_MODE)zone_data->effect; speed = (MSI_SPEED)(zone_data->speedAndBrightnessFlags & 0x03); brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> 2) & 0x1F); rainbow_color = (zone_data->colorFlags & 0x80) == 0 ? true : false; color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); } void MSIMysticLight185Controller::SetCycleCount ( MSI_ZONE zone, unsigned char cycle_num ) { RainbowZoneData *requested_zone = GetRainbowZoneData(zone); if(!requested_zone) { return; } requested_zone->cycle_or_led_num = cycle_num; SelectPerLedProtocol(); } void MSIMysticLight185Controller::SetDirectMode ( bool mode ) { direct_mode = mode; SelectPerLedProtocol(); } size_t MSIMysticLight185Controller::GetMaxDirectLeds ( MSI_ZONE zone ) { switch(zone) { case MSI_ZONE_J_RGB_1: case MSI_ZONE_J_RGB_2: return 1; case MSI_ZONE_J_PIPE_1: return numof_pipe1_leds; case MSI_ZONE_J_PIPE_2: return numof_pipe2_leds; case MSI_ZONE_J_RAINBOW_1: if(per_led_mode == DIRECT_MODE_PER_LED) { return JRAINBOW1_MAX_LED_COUNT; } else { return 1; } case MSI_ZONE_J_RAINBOW_2: case MSI_ZONE_J_RAINBOW_3: if(per_led_mode == DIRECT_MODE_PER_LED) { return JRAINBOW2_MAX_LED_COUNT; } else { return 1; } case MSI_ZONE_J_CORSAIR: if(per_led_mode == DIRECT_MODE_PER_LED) { return JCORSAIR_MAX_LED_COUNT; } else { return 1; } case MSI_ZONE_ON_BOARD_LED_0: return numof_onboard_leds; default: return 1; } } void MSIMysticLight185Controller::SelectPerLedProtocol() { unsigned char jrainbow1_size = 0; unsigned char jrainbow2_size = 0; unsigned char jrainbow3_size = 0; RainbowZoneData* zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_1); if(zone_data != nullptr) { jrainbow1_size = zone_data->cycle_or_led_num; } zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_2); if(zone_data != nullptr) { jrainbow2_size = zone_data->cycle_or_led_num; } zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_3); if(zone_data != nullptr) { jrainbow3_size = zone_data->cycle_or_led_num; } sync_direct_mode = true; if((jrainbow1_size > SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT) || (jrainbow2_size > SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT) || (jrainbow3_size > SYNC_PER_LED_MODE_CORSAIR_LED_COUNT)) { sync_direct_mode = false; } if(sync_direct_mode) { enable_per_led_msg.j_rainbow_1.effect = MSI_MODE_STATIC; enable_per_led_msg.j_rainbow_1.cycle_or_led_num = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; enable_per_led_msg.j_rainbow_2.effect = MSI_MODE_STATIC; enable_per_led_msg.j_rainbow_2.cycle_or_led_num = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; enable_per_led_msg.j_corsair.effect = MSI_MODE_STATIC; enable_per_led_msg.j_corsair.is_individual = SYNC_PER_LED_MODE_CORSAIR_LED_COUNT; enable_per_led_msg.on_board_led.colorFlags = PER_LED_FULL_SYNC_MODE; } else { if(!no_jrainbow1) { enable_per_led_msg.j_rainbow_1.effect = MSI_DIRECT_MODE; enable_per_led_msg.j_rainbow_1.cycle_or_led_num = JRAINBOW1_MAX_LED_COUNT; } if(!no_jrainbow2) { enable_per_led_msg.j_rainbow_2.effect = MSI_DIRECT_MODE; enable_per_led_msg.j_rainbow_2.cycle_or_led_num = JRAINBOW2_MAX_LED_COUNT; } if(!no_jcorsair) { enable_per_led_msg.j_corsair.effect = MSI_DIRECT_MODE; enable_per_led_msg.j_corsair.is_individual = JCORSAIR_MAX_LED_COUNT; } enable_per_led_msg.on_board_led.colorFlags = PER_LED_BASIC_SYNC_MODE; } if(direct_mode) { if(per_led_mode == DIRECT_MODE_PER_LED) { hid_send_feature_report(dev, (unsigned char*)&enable_per_led_msg, sizeof(enable_per_led_msg)); } } }