/*---------------------------------------------------------*\ | MSIMotherboard112Controller.cpp | | | | Driver for MSI Mystic Light 112-byte motherboard | | | | thombo 17 Dec 2022 | | | | This file is part of the OpenRGB project | | SPDX-License-Identifier: GPL-2.0-or-later | \*---------------------------------------------------------*/ #include #include #include #include "MSIMotherboard112Controller.h" #include "StringUtils.h" #define BITSET(val, bit, pos) ((unsigned char)std::bitset<8>(val).set((pos), (bit)).to_ulong()) struct Config { unsigned short pid; // PID of the board size_t numof_onboard_leds; // number of onboard leds const std::vector* supported_zones; // pointer to vector of supported zones }; const std::vector zones_set = { MSI_ZONE_J_RGB_1, MSI_ZONE_J_RAINBOW_1, MSI_ZONE_J_CORSAIR, MSI_ZONE_ON_BOARD_LED_0 }; MSIMotherboard112Controller::MSIMotherboard112Controller(hid_device* handle, const char* path, std::string dev_name) { dev = handle; location = path; name = dev_name; /*-----------------------------------------------------*\ | Perform initial reads - get the controller's firmware | | version and then read the controller's active | | configuration data | \*-----------------------------------------------------*/ ReadFwVersion(); ReadSettings(); /*-----------------------------------------------------*\ | The last byte of the packet is the save flag. This | | byte is stored as a zero in the data flash, but is | | set to 1 at the protocol level to tell the firmware | | to save the active configuration to flash. | | Also set onboard zone to act as sync master for all | | of the onboard LEDs. | \*-----------------------------------------------------*/ data.save_data = 0; data.on_board_led.colorFlags = MSI_COLOR_FLAG_USE_CUSTOM_COLOR | MSI_COLOR_FLAG_SYNC_SETTING_ONBOARD; /*-----------------------------------------------------*\ | Initialize zone based per LED data | \*-----------------------------------------------------*/ numof_onboard_leds = 7; supported_zones = &zones_set; zone_based_per_led_data.j_rgb_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.j_rgb_1.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.j_rainbow_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.j_rainbow_1.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_1.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_2.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_3.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_3.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_4.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_4.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_5.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_5.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.on_board_led_6.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT; zone_based_per_led_data.on_board_led_6.colorFlags |= MSI_COLOR_FLAG_USE_CUSTOM_COLOR; zone_based_per_led_data.save_data = 0; direct_mode = false; } MSIMotherboard112Controller::~MSIMotherboard112Controller() { hid_close(dev); } void MSIMotherboard112Controller::SetMode ( MSI_ZONE zone, MSI_MODE mode, MSI_SPEED speed, MSI_BRIGHTNESS brightness, bool rainbow_color ) { /*-----------------------------------------------------*\ | Get the zone data pointer within the configuration | | data buffer | \*-----------------------------------------------------*/ ZoneData* zone_data = GetZoneData(data, zone); /*-----------------------------------------------------*\ | Return if zone is invalid | \*-----------------------------------------------------*/ if(zone_data == nullptr) { return; } if(zone <= MSI_ZONE_ON_BOARD_LED_0) { zone_data->effect = mode; zone_data->speedAndBrightnessFlags = ((brightness << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_MASK) | ((speed << MSI_SPEED_BRIGHTNESS_FLAG_SPEED_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_SPEED_MASK); zone_data->colorFlags = (rainbow_color ? (zone_data->colorFlags & ~MSI_COLOR_FLAG_USE_CUSTOM_COLOR) : (zone_data->colorFlags | MSI_COLOR_FLAG_USE_CUSTOM_COLOR)); zone_data->padding = 0x00; if(mode > MSI_MODE_DOUBLE_FLASHING) { zone_data->speedAndBrightnessFlags |= MSI_SPEED_BRIGHTNESS_FLAG_SYNC_SETTING_JRGB; zone_data->colorFlags |= MSI_COLOR_FLAG_SYNC_SETTING_ONBOARD; } else { zone_data->speedAndBrightnessFlags &= ~MSI_SPEED_BRIGHTNESS_FLAG_SYNC_SETTING_JRGB; zone_data->colorFlags &= ~MSI_COLOR_FLAG_SYNC_SETTING_ONBOARD; } } if((zone >= MSI_ZONE_ON_BOARD_LED_0) && (mode <= MSI_MODE_DOUBLE_FLASHING)) { zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); if(zone_data != nullptr) { zone_data->effect = mode; zone_data->speedAndBrightnessFlags = ((brightness << MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_MASK) | ((speed << MSI_SPEED_BRIGHTNESS_FLAG_SPEED_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_SPEED_MASK); zone_data->colorFlags = (rainbow_color ? (zone_data->colorFlags & ~MSI_COLOR_FLAG_USE_CUSTOM_COLOR) : (zone_data->colorFlags | MSI_COLOR_FLAG_USE_CUSTOM_COLOR)); zone_data->padding = 0x00; } } } std::string MSIMotherboard112Controller::GetDeviceName() { return name; } std::string MSIMotherboard112Controller::GetFWVersion() { std::string firmware_version; firmware_version = "APROM: " + version_APROM + ", LDROM: " + version_LDROM; return firmware_version; } std::string MSIMotherboard112Controller::GetDeviceLocation() { return("HID: " + location); } std::string MSIMotherboard112Controller::GetSerial() { 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)); } bool MSIMotherboard112Controller::ReadSettings() { /*-----------------------------------------------------*\ | Read packet from hardware, return true if successful | \*-----------------------------------------------------*/ return(hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data); } bool MSIMotherboard112Controller::Update ( bool save ) { /*-----------------------------------------------------*\ | Send packet to hardware, return true if successful | \*-----------------------------------------------------*/ if(direct_mode) { 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 MSIMotherboard112Controller::SetZoneColor ( MSI_ZONE zone, unsigned char red1, unsigned char grn1, unsigned char blu1, unsigned char red2, unsigned char grn2, unsigned char blu2 ) { /*-----------------------------------------------------*\ | Get the zone data pointer within the configuration | | data buffer | \*-----------------------------------------------------*/ ZoneData* zone_data = GetZoneData(data, zone); /*-----------------------------------------------------*\ | Return if zone is invalid | \*-----------------------------------------------------*/ if(zone_data == nullptr) { return; } if(zone <= MSI_ZONE_ON_BOARD_LED_0) { 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(zone >= MSI_ZONE_ON_BOARD_LED_0) { zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); 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 MSIMotherboard112Controller::SetLedColor ( MSI_ZONE zone, unsigned char red, unsigned char grn, unsigned char blu ) { if(zone >= MSI_ZONE_ON_BOARD_LED_0) { zone = (MSI_ZONE)((int)zone + 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 *MSIMotherboard112Controller::GetZoneData ( FeaturePacket_112& data_packet, MSI_ZONE zone ) { switch(zone) { case MSI_ZONE_J_RGB_1: return &data_packet.j_rgb_1; case MSI_ZONE_J_RAINBOW_1: return &data_packet.j_rainbow_1; 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_J_CORSAIR: return &data_packet.j_corsair_1; default: break; } return nullptr; } bool MSIMotherboard112Controller::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); } MSI_MODE MSIMotherboard112Controller::GetMode() { return (MSI_MODE)data.on_board_led.effect; } void MSIMotherboard112Controller::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 == nullptr) { return; } /*-----------------------------------------------------*\ | Update pointers with data | \*-----------------------------------------------------*/ mode = (MSI_MODE) zone_data->effect; speed = (MSI_SPEED) ((zone_data->speedAndBrightnessFlags >> MSI_SPEED_BRIGHTNESS_FLAG_SPEED_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_SPEED_MASK); brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_SHIFT) & MSI_SPEED_BRIGHTNESS_FLAG_BRIGHTNESS_MASK); rainbow_color = ((zone_data->colorFlags & MSI_COLOR_FLAG_USE_CUSTOM_COLOR) == 0 ? false : true); color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); } void MSIMotherboard112Controller::SetDirectMode ( bool mode ) { direct_mode = mode; } size_t MSIMotherboard112Controller::GetMaxOnboardLeds() { return numof_onboard_leds; }