Files
OpenRGB/Controllers/MSIMysticLightController/MSIMysticLight162Controller.cpp
2022-11-11 17:25:15 +00:00

554 lines
20 KiB
C++

/*-----------------------------------------*\
| MSIMysticLight162Controller.cpp |
| |
| Driver for MSI Mystic Light (162-byte) |
| USB lighting controller |
| |
| T-bond 3/4/2020 |
| Adam Honse 3/6/2021 |
\*-----------------------------------------*/
#include "MSIMysticLight162Controller.h"
#include <algorithm>
#include <array>
#include <bitset>
#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<MSI_ZONE>* supported_zones; // pointer to vector of supported zones
};
const std::vector<MSI_ZONE> zones_set0 =
{
MSI_ZONE_J_RGB_1,
MSI_ZONE_J_RGB_2,
MSI_ZONE_J_RAINBOW_1,
MSI_ZONE_J_CORSAIR,
MSI_ZONE_ON_BOARD_LED_0
};
const std::vector<MSI_ZONE> zones_set1 =
{
MSI_ZONE_J_RGB_1,
MSI_ZONE_J_RGB_2,
MSI_ZONE_J_RAINBOW_1,
MSI_ZONE_ON_BOARD_LED_0
};
const std::vector<MSI_ZONE> zones_set2 =
{
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
};
/*-----------------------------------------------------------------------------------------------------------------------------*\
| 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 (7 onboard LEDs, all zones supported). |
| 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[] =
{
{ 0x1720, 10, &zones_set0 }, // MPG Z390 GAMING EDGE AC
{ 0x7B12, 10, &zones_set0 }, // MEG Z390 ACE
{ 0x7B17, 10, &zones_set0 }, // MPG Z390 GAMING PRO CARBON
{ 0x7B18, 6, &zones_set1 }, // MAG Z390 TOMAHAWK
{ 0x7B50, 6, &zones_set2 }, // MPG Z390M GAMING EDGE AC
{ 0x7B85, 7, &zones_set0 }, // B450 GAMING PRO CARBON
};
MSIMysticLight162Controller::MSIMysticLight162Controller
(
hid_device* handle,
const char *path,
unsigned short pid
)
{
dev = handle;
if(dev)
{
location = path;
ReadName();
ReadSerial();
ReadFwVersion();
ReadSettings();
}
/*-----------------------------------------*\
| Initialize save flag |
\*-----------------------------------------*/
data.save_data = 0;
data.on_board_led.colorFlags = 0x81; // force MS bit of color flags to 1 to have expectd zone control
/*-----------------------------------------*\
| Initialize zone based per LED data |
\*-----------------------------------------*/
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;
supported_zones = board_config->supported_zones;
}
else
{
numof_onboard_leds = 10;
supported_zones = &zones_set0;
}
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_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.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.on_board_led_10.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2;
zone_based_per_led_data.on_board_led_10.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;
}
MSIMysticLight162Controller::~MSIMysticLight162Controller()
{
hid_close(dev);
}
void MSIMysticLight162Controller::SetMode
(
MSI_ZONE zone,
MSI_MODE mode,
MSI_SPEED speed,
MSI_BRIGHTNESS brightness,
bool rainbow_color
)
{
ZoneData* zone_data = GetZoneData(data, zone);
if(zone_data == nullptr)
{
return;
}
if (zone <= MSI_ZONE_ON_BOARD_LED_0)
{
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;
if(mode > MSI_MODE_DOUBLE_FLASHING)
{
zone_data->speedAndBrightnessFlags |= SYNC_SETTING_JRGB;
zone_data->colorFlags |= SYNC_SETTING_ONBOARD;
}
else
{
zone_data->speedAndBrightnessFlags &= ~SYNC_SETTING_JRGB;
zone_data->colorFlags &= ~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 << 2) | (speed & 0x03);
zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u);
zone_data->padding = 0x00;
}
}
}
std::string MSIMysticLight162Controller::GetDeviceName()
{
return name;
}
std::string MSIMysticLight162Controller::GetFWVersion()
{
std::string firmware_version;
firmware_version = "APROM: " + version_APROM + ", LDROM: " + version_LDROM;
return firmware_version;
}
std::string MSIMysticLight162Controller::GetDeviceLocation()
{
return("HID: " + location);
}
std::string MSIMysticLight162Controller::GetSerial()
{
return chip_id;
}
bool MSIMysticLight162Controller::ReadSettings()
{
/*-----------------------------------------------------*\
| Read packet from hardware, return true if successful |
\*-----------------------------------------------------*/
return(hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data);
}
bool MSIMysticLight162Controller::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 MSIMysticLight162Controller::SetZoneColor
(
MSI_ZONE zone,
unsigned char red1,
unsigned char grn1,
unsigned char blu1,
unsigned char red2,
unsigned char grn2,
unsigned char blu2
)
{
ZoneData* zone_data = GetZoneData(data, zone);
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 MSIMysticLight162Controller::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 *MSIMysticLight162Controller::GetZoneData
(
FeaturePacket_162& 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.on_board_led_10;
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_ON_BOARD_LED_10:
return &data_packet.on_board_led_10;
case MSI_ZONE_J_CORSAIR:
return &data_packet.j_corsair_1;
default:
break;
}
return nullptr;
}
bool MSIMysticLight162Controller::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 <high>.<low> |
\*-----------------------------------------------------*/
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 <high>.<low> |
\*-----------------------------------------------------*/
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 MSIMysticLight162Controller::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 MSIMysticLight162Controller::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 MSIMysticLight162Controller::GetMode()
{
return (MSI_MODE)data.on_board_led.effect;
}
void MSIMysticLight162Controller::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 & 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 MSIMysticLight162Controller::SetDirectMode
(
bool mode
)
{
direct_mode = mode;
}
size_t MSIMysticLight162Controller::GetMaxOnboardLeds()
{
return numof_onboard_leds;
}