Skip to content

Commit 2b7cb7d

Browse files
committed
Added base support Asus ROG Crosshair X670E Hero and PSU MSI MEG Ai1300P
* Added base support matherboard Asus ROG Crosshair X670E Hero * Added base support PSU MSI MEG Ai1300P * Refactoring MSI PSU lib
1 parent a31c5df commit 2b7cb7d

5 files changed

Lines changed: 256 additions & 0 deletions

File tree

OpenHardwareMonitorLib/Hardware/Computer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using OpenHardwareMonitor.Hardware.Network;
1818
using OpenHardwareMonitor.Hardware.Psu.Corsair;
1919
using OpenHardwareMonitor.Hardware.Storage;
20+
using OpenHardwareMonitor.Hardware.Psu.Msi;
2021

2122
namespace OpenHardwareMonitor.Hardware;
2223

@@ -245,10 +246,12 @@ public bool IsPsuEnabled
245246
if (value)
246247
{
247248
Add(new CorsairPsuGroup(_settings));
249+
Add(new MsiPsuGroup(_settings));
248250
}
249251
else
250252
{
251253
RemoveType<CorsairPsuGroup>();
254+
RemoveType<MsiPsuGroup>();
252255
}
253256
}
254257

@@ -543,7 +546,10 @@ private void AddGroups()
543546
Add(new NetworkGroup(_settings));
544547

545548
if (_psuEnabled)
549+
{
546550
Add(new CorsairPsuGroup(_settings));
551+
Add(new MsiPsuGroup(_settings));
552+
}
547553

548554
if (_batteryEnabled)
549555
Add(new BatteryGroup(_settings));
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System.Collections.Generic;
2+
using HidSharp;
3+
4+
namespace OpenHardwareMonitor.Hardware.Psu.Msi;
5+
6+
internal sealed class MsiPsu : Hardware
7+
{
8+
private readonly HidDevice _device;
9+
private readonly List<PsuSensor> _sensors = [];
10+
11+
public MsiPsu(HidDevice device, ISettings settings, int index)
12+
: base("MSI PSU", new Identifier("psu", "msi", index.ToString()), settings)
13+
{
14+
_device = device;
15+
using HidStream stream = device.Open();
16+
UsbApi.FirmwareInfo fwInfo = UsbApi.FwInfo(stream);
17+
Name = $"{fwInfo.Vendor} {fwInfo.Product}";
18+
19+
AddSensors(settings);
20+
}
21+
22+
public override HardwareType HardwareType => HardwareType.Psu;
23+
24+
public override void Update()
25+
{
26+
using HidStream stream = _device.Open();
27+
float[] info = UsbApi.InfoList(stream);
28+
_sensors.ForEach(s => s.Update(info));
29+
}
30+
31+
private void AddSensors(ISettings settings)
32+
{
33+
int sensorIndex = 0;
34+
35+
_sensors.Add(new PsuSensor("Case", sensorIndex++, SensorType.Fan, this, settings, UsbApi.IndexInfo.FAN_RPM));
36+
_sensors.Add(new PsuSensor("Case", sensorIndex++, SensorType.Temperature, this, settings, UsbApi.IndexInfo.TEMP));
37+
38+
_sensors.Add(new PsuSensor("+12V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_12));
39+
_sensors.Add(new PsuSensor("+12V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_12));
40+
41+
_sensors.Add(new PsuSensor("+5V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_5));
42+
_sensors.Add(new PsuSensor("+5V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_5));
43+
44+
_sensors.Add(new PsuSensor("+3.3V", sensorIndex++, SensorType.Voltage, this, settings, UsbApi.IndexInfo.VOLTS_3V3));
45+
_sensors.Add(new PsuSensor("+3.3V", sensorIndex++, SensorType.Current, this, settings, UsbApi.IndexInfo.AMPS_3V3));
46+
47+
_sensors.Add(new PsuSensor("PSU Efficiency", sensorIndex++, SensorType.Level, this, settings, UsbApi.IndexInfo.EFFICIENCY));
48+
_sensors.Add(new PsuSensor("PSU Out", sensorIndex++, SensorType.Power, this, settings, UsbApi.IndexInfo.PSU_OUT));
49+
_sensors.Add(new PsuSensor("Total Runtime", sensorIndex++, SensorType.TimeSpan, this, settings, UsbApi.IndexInfo.RUNTIME, true));
50+
}
51+
52+
private class PsuSensor : Sensor
53+
{
54+
private readonly UsbApi.IndexInfo _indexInfo;
55+
56+
public PsuSensor(string name, int index, SensorType type, MsiPsu hardware, ISettings settings, UsbApi.IndexInfo indexInfo, bool noHistory = false)
57+
: base(name, index, false, type, hardware, null, settings, noHistory)
58+
{
59+
_indexInfo = indexInfo;
60+
hardware.ActivateSensor(this);
61+
}
62+
63+
public void Update(float[] info)
64+
{
65+
Value = info[(int)_indexInfo];
66+
}
67+
}
68+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text;
4+
using HidSharp;
5+
6+
namespace OpenHardwareMonitor.Hardware.Psu.Msi;
7+
8+
public class MsiPsuGroup : IGroup
9+
{
10+
private static readonly int[] _productIds =
11+
{
12+
0x56d4, // MEG Ai1300P
13+
};
14+
15+
private static readonly ushort _vendorId = 0x0db0;
16+
private readonly List<IHardware> _hardware;
17+
private readonly StringBuilder _report;
18+
19+
public MsiPsuGroup(ISettings settings)
20+
{
21+
_report = new StringBuilder();
22+
_report.AppendLine("MSI Ai series PSU Hardware");
23+
_report.AppendLine();
24+
25+
_hardware = new List<IHardware>();
26+
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(_vendorId))
27+
{
28+
if (_productIds.Contains(dev.ProductID))
29+
{
30+
var device = new MsiPsu(dev, settings, _hardware.Count);
31+
_hardware.Add(device);
32+
_report.AppendLine($"Device name: {device.Name}");
33+
_report.AppendLine();
34+
}
35+
}
36+
37+
if (_hardware.Count == 0)
38+
{
39+
_report.AppendLine("No MSI PSU Hardware found.");
40+
_report.AppendLine();
41+
}
42+
}
43+
44+
public IReadOnlyList<IHardware> Hardware => _hardware;
45+
46+
public void Close()
47+
{
48+
foreach (IHardware iHardware in _hardware)
49+
{
50+
if (iHardware is Hardware hardware)
51+
hardware.Close();
52+
}
53+
}
54+
55+
public string GetReport()
56+
{
57+
return _report.ToString();
58+
}
59+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text;
4+
using HidSharp;
5+
6+
namespace OpenHardwareMonitor.Hardware.Psu.Msi;
7+
8+
internal static class UsbApi
9+
{
10+
public enum IndexInfo : int
11+
{
12+
VOLTS_12V1 = 0,
13+
VOLTS_12V2 = 2,
14+
VOLTS_12V3 = 4,
15+
VOLTS_12V4 = 6,
16+
VOLTS_12V5 = 8,
17+
VOLTS_12 = 10,
18+
VOLTS_5 = 12,
19+
VOLTS_3V3 = 14,
20+
AMPS_12V1 = 1,
21+
AMPS_12V2 = 3,
22+
AMPS_12V3 = 5,
23+
AMPS_12V4 = 7,
24+
AMPS_12V5 = 9,
25+
AMPS_12 = 11,
26+
AMPS_5 = 13,
27+
AMPS_3V3 = 15,
28+
29+
PSU_OUT = 16,
30+
EFFICIENCY = 17,
31+
TEMP = 18,
32+
FAN_RPM = 19,
33+
RUNTIME = 20
34+
}
35+
36+
public static FirmwareInfo FwInfo(HidStream stream)
37+
{
38+
if (!Request(stream, new byte[2] { 0xFA, 0x51 }, out byte[] productArr, 1))
39+
throw new ProtocolError(stream.Device, "Can't read product name");
40+
41+
static string ArrayToString(byte[] ar)
42+
{
43+
return Encoding.ASCII.GetString(ar.TakeWhile(x => x != 0).ToArray());
44+
}
45+
46+
return new FirmwareInfo { Vendor = "MSI", Product = ArrayToString(productArr) };
47+
}
48+
49+
private static float Linear11ToFloat32(ushort val)
50+
{
51+
int exp = (short)val >> 11;
52+
int mant = ((short)(val & 0x7ff) << 5) >> 5;
53+
return mant * (float)Math.Pow(2, exp);
54+
}
55+
56+
private static bool Request(HidStream stream, byte[] command, out byte[] response, int offset = 0)
57+
{
58+
byte[] buffer = new byte[64];
59+
Array.Copy(command, 0, buffer, 1, 2);
60+
stream.Write(buffer);
61+
62+
byte[] reply = stream.Read();
63+
response = new byte[42];
64+
Array.Copy(reply, 2 + offset, response, 0, 42);
65+
66+
return reply[0] == buffer[0] && reply[1] == buffer[1];
67+
}
68+
69+
public static float[] InfoList(HidStream stream)
70+
{
71+
int length = Enum.GetNames(typeof(IndexInfo)).Length;
72+
float[] info = new float[length];
73+
74+
if (!Request(stream, new byte[2] { 0x51, 0xE0 }, out byte[] basic))
75+
throw new ProtocolError(stream.Device, "Can't read basic info");
76+
77+
// basic has information only about the first 20 sensors
78+
for (int i = 0; i < 20; i++)
79+
{
80+
byte[] replyData = new byte[4];
81+
Array.Copy(basic, (i * 2) + 1, replyData, 0, 2);
82+
info[i] = Linear11ToFloat32((ushort)BitConverter.ToInt32(replyData, 0));
83+
}
84+
85+
// runtime info
86+
Request(stream, new byte[2] { 0x51, 0xD1 }, out byte[] runtime);
87+
info[(int)IndexInfo.RUNTIME] = BitConverter.ToInt32(runtime, 0) / 100;
88+
89+
return info;
90+
}
91+
92+
public struct FirmwareInfo
93+
{
94+
public string Vendor;
95+
public string Product;
96+
}
97+
}

OpenHardwareMonitorLib/Interop/Kernel32.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ internal static T CreateStruct<T>()
7979
return result;
8080
}
8181

82+
/// <summary>
83+
/// Convert Win32 error code to HRESULT.
84+
/// </summary>
85+
/// <remarks>
86+
/// HRESULT_FROM_WIN32 macro equivalent.
87+
/// </remarks>
88+
/// <param name="errorCode">Win32 error code</param>
89+
/// <returns>HRESULT</returns>
90+
internal static int HResultFromWin32(int errorCode) => errorCode <= 0
91+
? errorCode
92+
: (int)((errorCode & 0x0000FFFF) | 0x80070000);
93+
8294
internal static SafeFileHandle OpenDevice(string devicePath)
8395
{
8496
SafeFileHandle hDevice = CreateFile(devicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
@@ -306,6 +318,20 @@ internal static extern bool DeviceIoControl
306318
out uint lpBytesReturned,
307319
IntPtr lpOverlapped);
308320

321+
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
322+
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
323+
[return: MarshalAs(UnmanagedType.Bool)]
324+
internal static extern bool DeviceIoControl
325+
(
326+
SafeFileHandle hDevice,
327+
uint dwIoControlCode,
328+
[In] byte[] lpInBuffer,
329+
uint nInBufferSize,
330+
[Out] byte[] lpOutBuffer,
331+
uint nOutBufferSize,
332+
ref uint lpBytesReturned,
333+
[In][Optional] IntPtr lpOverlapped);
334+
309335
[DllImport(DllName, CallingConvention = CallingConvention.Winapi)]
310336
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
311337
internal static extern IntPtr LocalAlloc(uint uFlags, ulong uBytes);

0 commit comments

Comments
 (0)