Add Controller for CoolerMaster MM712 Mouse

This commit is contained in:
Frans Meulenbroeks 2024-12-09 22:20:56 +01:00 committed by Adam Honse
parent 2652e92461
commit 38732a8fb4
6 changed files with 622 additions and 0 deletions

View file

@ -0,0 +1,192 @@
/*---------------------------------------------------------*\
| CMMM712Controller.cpp |
| |
| Driver for Cooler Master MM712 mouse |
| Derived from CMMM711Controller.cpp |
| |
| Chris M (Dr_No) 14 Feb 2021 |
| Frans Meulenbroeks 08 Dec 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include <cstring>
#include "CMMM712Controller.h"
#include "StringUtils.h"
#define CM_MM712_PACKET_SIZE 65
#define CM_MM712_INTERRUPT_TIMEOUT 250
#define HID_MAX_STR 255
enum
{
CM_MM712_MODE_BYTE = 4,
CM_MM712_BRIGHTNESS_BYTE = 6,
CM_MM712_SPEED_BYTE = 7,
CM_MM712_RED_BYTE = 8,
CM_MM712_GREEN_BYTE = 9,
CM_MM712_BLUE_BYTE = 10,
};
CMMM712Controller::CMMM712Controller(hid_device* dev_handle, char *_path)
{
dev = dev_handle;
location = _path;
/*---------------------------------------------------------*\
| Get device name from HID manufacturer and product strings |
\*---------------------------------------------------------*/
wchar_t name_string[HID_MAX_STR];
hid_get_manufacturer_string(dev, name_string, HID_MAX_STR);
device_name = StringUtils::wstring_to_string(name_string);
hid_get_product_string(dev, name_string, HID_MAX_STR);
device_name.append(" ").append(StringUtils::wstring_to_string(name_string));
SendInitPacket();
GetModeStatus();
GetColorStatus(current_mode);
}
CMMM712Controller::~CMMM712Controller()
{
hid_close(dev);
}
void CMMM712Controller::SendBuffer(uint8_t *buffer, uint8_t buffer_size)
{
hid_write(dev, buffer, buffer_size);
hid_read_timeout(dev, buffer, buffer_size, CM_MM712_INTERRUPT_TIMEOUT);
}
void CMMM712Controller::GetColorStatus(uint8_t mode)
{
uint8_t buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x03, mode };
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
initial_color = ToRGBColor(buffer[CM_MM712_RED_BYTE - 2], buffer[CM_MM712_GREEN_BYTE - 2], buffer[CM_MM712_BLUE_BYTE - 2]);
}
void CMMM712Controller::GetModeStatus()
{
uint8_t buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x07 };
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
current_mode = buffer[CM_MM712_MODE_BYTE - 1];
SetMode(current_mode);
}
std::string CMMM712Controller::GetDeviceName()
{
return(device_name);
}
std::string CMMM712Controller::GetSerial()
{
wchar_t serial_string[HID_MAX_STR];
int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR);
if(ret != 0)
{
return("");
}
return(StringUtils::wstring_to_string(serial_string));
}
std::string CMMM712Controller::GetLocation()
{
return("HID: " + location);
}
unsigned char CMMM712Controller::GetMode()
{
return(current_mode);
}
RGBColor CMMM712Controller::GetInitialLedColor()
{
return initial_color;
}
void CMMM712Controller::SetLedsDirect(RGBColor color)
{
unsigned char buffer[CM_MM712_PACKET_SIZE] =
{
0x00, 0x5A, 0x81, 0x03,
(unsigned char)RGBGetRValue(color),
(unsigned char)RGBGetGValue(color),
(unsigned char)RGBGetBValue(color)
};
if(current_mode!=CM_MM712_MODE_DIRECT)
{
SetDirectMode(true);
}
hid_write(dev, buffer, CM_MM712_PACKET_SIZE);
// SendBuffer(buffer, CM_MM712_PACKET_SIZE);
}
void CMMM712Controller::SendUpdate(uint8_t mode, uint8_t speed, RGBColor color, uint8_t brightness)
{
unsigned char buffer[CM_MM712_PACKET_SIZE] =
{
0x00, 0x4C, 0x81, 0x04, mode, 0xFF, brightness, speed,
(unsigned char)RGBGetRValue(color),
(unsigned char)RGBGetGValue(color),
(unsigned char)RGBGetBValue(color),
0xFF
};
if(current_mode==CM_MM712_MODE_DIRECT)
{
SetDirectMode(false);
SendInitPacket();
}
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
SetMode(mode);
}
void CMMM712Controller::SendInitPacket()
{
unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x44, 0x81, 0x02 };
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
}
void CMMM712Controller::SetDirectMode(bool onoff)
{
unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x5a, 0x81, (unsigned char)(0x01+onoff) };
hid_write(dev, buffer, CM_MM712_PACKET_SIZE);
}
void CMMM712Controller::SetMode(uint8_t mode)
{
unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x08, mode};
if(current_mode==CM_MM712_MODE_DIRECT)
{
SendInitPacket();
}
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
current_mode = mode;
}
void CMMM712Controller::SetProfile(uint8_t profile)
{
unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x44, 0x81, 0x01, profile};
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
}
void CMMM712Controller::SaveStatus()
{
unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x54, 0x81, 1};
SendBuffer(buffer, CM_MM712_PACKET_SIZE);
}

View file

@ -0,0 +1,69 @@
/*---------------------------------------------------------*\
| CMMM712Controller.h |
| |
| Driver for Cooler Master MM712 mouse |
| Derived from CMMM711Controller.h |
| |
| Chris M (Dr_No) 14 Feb 2021 |
| Frans Meulenbroeks 08 Dec 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include <hidapi.h>
#include "RGBController.h"
enum
{
CM_MM712_MODE_STATIC = 0, //Static Mode
CM_MM712_MODE_BREATHING = 1, //Breathing Mode
CM_MM712_MODE_SPECTRUM_CYCLE = 2, //Spectrum Cycle Mode
CM_MM712_MODE_OFF = 3, //Turn Off
CM_MM712_MODE_DIRECT = 4, //Direct LED Control
};
enum
{
CM_MM712_SPEED_SLOWEST = 0x0, //Slowest speed
CM_MM712_SPEED_NORMAL = 0x2, //Normal speed
CM_MM712_SPEED_FASTEST = 0x4, //Fastest speed
};
class CMMM712Controller
{
public:
CMMM712Controller(hid_device* dev_handle, char *_path);
~CMMM712Controller();
std::string GetDeviceName();
std::string GetSerial();
std::string GetLocation();
uint8_t GetMode();
RGBColor GetInitialLedColor();
void SendUpdate(uint8_t mode, uint8_t speed, RGBColor color, uint8_t brightness);
void SetMode(uint8_t mode);
void SetDirectMode(bool onoff);
void SetLedsDirect(RGBColor color);
void SaveStatus();
private:
std::string device_name;
std::string serial;
std::string location;
hid_device* dev;
uint8_t current_mode;
RGBColor initial_color;
void GetColorStatus(uint8_t mode);
void GetModeStatus();
void SendInitPacket();
void SetProfile(uint8_t profile);
void SendBuffer(uint8_t *buffer, uint8_t buffer_size);
};

View file

@ -0,0 +1,108 @@
Analysis of the MM712 protocol
By Frans Meulenbroeks
PID 0x2516, VID 0x0169
We must use interface 3
C = Command, R = Response
Init:
=====
C: 0x00 0x44 0x81 0x02
R: 0x45 0x81 0x02 0x02 0x01
First byte is second byte of command+1, 2nd and 3rd byte are the 3rd and 4th byte of the command
No idea what the last two bytes are.
This init command inits to normal state.
After that one can submit all normal commands. These give a response.
C: 0x00 0x5a 0x81 0x02
R: 0x5b 0x81 0x02
This init command inits to direct state.
After that one can submit all direct state commands. These give no response
Note that you can always change between the two states by giving the appropriate init command.
I have also seen
C: 0x00, 0x46, 0x81
0x46 is command code
R: 47 81 50 03 00 00 f2 9b 1e 00 64 02 00 00 00 ff 03 06 00 ...
No idea what the response data is
This also brought the device to type-4 state.
NORMAL COMMANDS
===============
Query stored colors:
====================
C: 0x00 0x4c 0x81 0x03 0
R: 0x4d 0x81 0x03 0x06 0xff 0x00 0xff 0xff 0x00
Brig Spee Red Gree Blue Speed is not really relevant
These are the settings for the static mode
C: 0x00 0x4c 0x81 0x03 1
R: 0x4d 0x81 0x03 0xff 0xff 0x04 0xff 0x00 0x00 0xff
Brig Spee Red Gree Blue
These are the settings for the breathing mode
C: 0x00 0x4c 0x81 0x03 2
R: 0x81 0x03 0x06 0x7f 0x02
Brig Spee
These are the settings for the cycling mode
C: 0x00 0x4c 0x81 0x03 3
R: 0x4d 0x81 0x03 0x06 0x00
This is the response for the off mode
Other/higher numbers also return this value
Detecting the mode:
===================
C: 0x00 0x4c 0x81 0x07
R: 0x4d 0x81 0x07 0x01
^ actual mode
Setting the mode:
=================
C: 0x00 0x4c 0x81 0x08 0x01
^ new mode 0=static,1=breathing,2=cycling,3 or higher=off
R: 0x4d 0x81 0x07 0x01
can't explain the 0x07; later calls returned 0x08 in this field
C: 0x00 0x4c 0x81 0x08 0x02
R: 0x4d 0x81 0x08 0x02
Setting the color:
==================
C: 0x00 0x4c 0x81 0x04 0x00 0xff 0xff 0x02 0x00 0xff 0x00
cmd mode ???? brig spee red gree blue Speed is only relevant for breathing and cyclic, color is not relevant for cyclic
This sets the color, speed and brightness for the specific mode.
Note that this does not imply a mode switch
The first 0xff byte does not seem to do anything. I've changed to a different number but saw no result
R: 0x47 0x81 0x50 0x03 0x00 0x00 0xf2 0x9b 0x1e 0x00 0x64 0x02 0x00 0x00 0x00 0xff 0x01 0xff 0x00 0x00
No idea what this means, Reply seems independent of color set
Saving values:
==============
C: 0x00 0x54 0x81 0x01
This saves the actual color for all modes and the mode itself to internal flash
R: 0x55 0x81 0x01 0x00
Change Profile
==============
C: 0x00 0x44 0x81 0x01 0x02
^ new profile must be in [0..4] otherwise this is a no-op
R: 0x45 0x81 0x01 0x02 0x01
^ it is unclear what this value is, values 0, 1 and 2 are observed
DIRECT COMMANDS
==============
C: 0x00 0x5a 0x81 0x01
Leave direct state (return to normal, note that a new init for normal is needed)
C: 0x00 0x5a 0x81 0x03 0xff 0x00 0x25
red gree blue This changes the color right away. No response is generated.
CoolerMaster MasterPlus software uses this to dynamically generate animations
Final notes:
It is possible to change the mode on the mouse (see mouse doc).
This also changes the value in flash
It is also possible to change the colors in mode 0 and 1 using the mouse.
8 different colors can be selected. I did not find a way to define these colors from software so I suspect these are hardcoded

View file

@ -0,0 +1,199 @@
/*---------------------------------------------------------*\
| RGBController_CMMM712Controller.cpp |
| |
| RGBController for Cooler Master MM712 mouse |
| Derived from RGBController_CMMM712Controller.cpp |
| |
| Chris M (Dr_No) 14 Feb 2021 |
| Frans Meulenbroeks 08 Dec 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include "RGBController_CMMM712Controller.h"
#define applyBrightness(c, bright) ((RGBColor) ((RGBGetBValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 16 | (RGBGetGValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 8 | (RGBGetRValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT)))
/**------------------------------------------------------------------*\
@name Coolermaster Master Mouse
@category Mouse
@type USB
@save :robot:
@direct :white_check_mark:
@effects :white_check_mark:
@detectors DetectCoolerMasterMouse
@comment
\*-------------------------------------------------------------------*/
RGBController_CMMM712Controller::RGBController_CMMM712Controller(CMMM712Controller* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "Cooler Master";
type = DEVICE_TYPE_MOUSE;
description = controller->GetDeviceName();
serial = controller->GetSerial();
location = controller->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = CM_MM712_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN;
Direct.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Direct.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
mode Static;
Static.name = "Static";
Static.value = CM_MM712_MODE_STATIC;
Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Static.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN;
Static.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Static.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Static.colors_min = 1;
Static.colors_max = 1;
Static.colors.resize(Static.colors_max);
Static.speed_min = CM_MM712_SPEED_SLOWEST;
Static.speed_max = CM_MM712_SPEED_FASTEST;
Static.color_mode = MODE_COLORS_MODE_SPECIFIC;
Static.speed = CM_MM712_SPEED_NORMAL;
modes.push_back(Static);
mode Breathing;
Breathing.name = "Breathing";
Breathing.value = CM_MM712_MODE_BREATHING;
Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Breathing.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN;
Breathing.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Breathing.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT;
Breathing.colors_min = 1;
Breathing.colors_max = 1;
Breathing.colors.resize(Breathing.colors_max);
Breathing.speed_min = CM_MM712_SPEED_SLOWEST;
Breathing.speed_max = CM_MM712_SPEED_FASTEST;
Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC;
Breathing.speed = CM_MM712_SPEED_NORMAL;
modes.push_back(Breathing);
mode Spectrum_Cycle;
Spectrum_Cycle.name = "Spectrum Cycle";
Spectrum_Cycle.value = CM_MM712_MODE_SPECTRUM_CYCLE;
Spectrum_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE;
Spectrum_Cycle.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN;
Spectrum_Cycle.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM;
Spectrum_Cycle.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM;
Spectrum_Cycle.speed_min = CM_MM712_SPEED_SLOWEST;
Spectrum_Cycle.speed_max = CM_MM712_SPEED_FASTEST;
Spectrum_Cycle.color_mode = MODE_COLORS_NONE;
Spectrum_Cycle.speed = CM_MM712_SPEED_NORMAL;
modes.push_back(Spectrum_Cycle);
mode Off;
Off.name = "Off";
Off.value = CM_MM712_MODE_OFF;
Off.flags = MODE_FLAG_MANUAL_SAVE;
Off.color_mode = MODE_COLORS_NONE;
modes.push_back(Off);
Init_Controller(); //Only processed on first run
SetupZones();
uint8_t temp_mode = controller->GetMode();
for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++)
{
if(modes[mode_index].value == temp_mode)
{
active_mode = mode_index;
break;
}
}
colors[0] = controller->GetInitialLedColor();
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC)
{
modes[active_mode].colors[0] = colors[0];
}
}
RGBController_CMMM712Controller::~RGBController_CMMM712Controller()
{
delete controller;
}
void RGBController_CMMM712Controller::Init_Controller()
{
zone mouse_zone;
mouse_zone.name = name;
mouse_zone.type = ZONE_TYPE_SINGLE;
mouse_zone.leds_min = 1;
mouse_zone.leds_max = 1;
mouse_zone.leds_count = 1;
mouse_zone.matrix_map = NULL;
zones.push_back(mouse_zone);
led logo_led;
logo_led.name = "Logo LED";
logo_led.value = 0;
leds.push_back(logo_led);
}
void RGBController_CMMM712Controller::SetupZones()
{
SetupColors();
}
void RGBController_CMMM712Controller::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_CMMM712Controller::DeviceUpdateLEDs()
{
modes[active_mode].brightness=255;
RGBColor logo = applyBrightness(colors[0], modes[active_mode].brightness);
controller->SetLedsDirect(logo);
}
void RGBController_CMMM712Controller::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_CMMM712Controller::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_CMMM712Controller::DeviceUpdateMode()
{
if(modes[active_mode].value==CM_MM712_MODE_DIRECT)
{
controller->SetDirectMode(true);
}
else
{
controller->SetDirectMode(false);
RGBColor colour = 0;
if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC )
{
colour = modes[active_mode].colors[0];
}
controller->SendUpdate(modes[active_mode].value, modes[active_mode].speed, colour, modes[active_mode].brightness);
}
}
void RGBController_CMMM712Controller::DeviceSaveMode()
{
DeviceUpdateMode();
controller->SaveStatus();
}

View file

@ -0,0 +1,42 @@
/*---------------------------------------------------------*\
| RGBController_CMMM712Controller.h |
| |
| RGBController for Cooler Master M712 mouse |
| Derived from RGBController_CMMM712Controller.h |
| |
| Chris M (Dr_No) 14 Feb 2021 |
| Frans Meulenbroeks 08 Dec 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "CMMM712Controller.h"
#define CM_MM_ARGB_BRIGHTNESS_MIN 0x00
#define CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT 0xFF
#define CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM 0x7F
class RGBController_CMMM712Controller : public RGBController
{
public:
RGBController_CMMM712Controller(CMMM712Controller* controller_ptr);
~RGBController_CMMM712Controller();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
void DeviceSaveMode();
private:
void Init_Controller();
CMMM712Controller* controller;
};

View file

@ -19,6 +19,7 @@
\*-----------------------------------------------------*/
#include "RGBController_CMMMController.h"
#include "RGBController_CMMM711Controller.h"
#include "RGBController_CMMM712Controller.h"
#include "RGBController_CMMP750Controller.h"
#include "RGBController_CMARGBController.h"
#include "RGBController_CMSmallARGBController.h"
@ -59,6 +60,7 @@
#define COOLERMASTER_MM530_PID 0x0065
#define COOLERMASTER_MM531_PID 0x0097
#define COOLERMASTER_MM711_PID 0x0101
#define COOLERMASTER_MM712_PID 0x0169
#define COOLERMASTER_MM720_PID 0x0141
#define COOLERMASTER_MM730_PID 0x0165
@ -231,6 +233,15 @@ void DetectCoolerMasterMouse(hid_device_info* info, const std::string& name)
}
break;
case COOLERMASTER_MM712_PID:
{
CMMM712Controller* controller = new CMMM712Controller(dev, info->path);
RGBController_CMMM712Controller* rgb_controller = new RGBController_CMMM712Controller(controller);
// Constructor sets the name
ResourceManager::get()->RegisterRGBController(rgb_controller);
}
break;
default:
LOG_DEBUG("[%s] Controller not created as the product ID %04X is missing from detector switch", name.c_str(), info->product_id);
}
@ -328,6 +339,7 @@ REGISTER_HID_DETECTOR_IPU("Cooler Master Small ARGB", DetectCooler
REGISTER_HID_DETECTOR_IPU("Cooler Master MM530", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM530_PID, 1, 0xFF00, 1);
//REGISTER_HID_DETECTOR_IPU("Cooler Master MM531", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM531_PID, 1, 0xFF00, 1);
REGISTER_HID_DETECTOR_IPU("Cooler Master MM711", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM711_PID, 1, 0xFF00, 1);
REGISTER_HID_DETECTOR_IPU("Cooler Master MM712", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM712_PID, 3, 0xFF0A, 2);
REGISTER_HID_DETECTOR_IPU("Cooler Master MM720", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM720_PID, 1, 0xFF00, 1);
REGISTER_HID_DETECTOR_IPU("Cooler Master MM730", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM730_PID, 1, 0xFF00, 1);