diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Identification.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Identification.cs index 6ff360d..4b76e17 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Identification.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Identification.cs @@ -52,6 +52,8 @@ internal class Identification return Manufacturer.FIC; case var _ when name.Equals("Foxconn", StringComparison.OrdinalIgnoreCase): return Manufacturer.Foxconn; + case var _ when name.StartsWith("Framework", StringComparison.OrdinalIgnoreCase): + return Manufacturer.Framework; case var _ when name.StartsWith("Fujitsu", StringComparison.OrdinalIgnoreCase): return Manufacturer.Fujitsu; case var _ when name.StartsWith("Gigabyte", StringComparison.OrdinalIgnoreCase): @@ -726,6 +728,64 @@ internal class Identification return Model.PROART_B760_CREATOR_D4; case var _ when name.Equals("TUF GAMING B450-PLUS II", StringComparison.OrdinalIgnoreCase): return Model.TUF_GAMING_B450_PLUS_II; + case var _ when name.Equals("FRANBMCP03", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP03; + case var _ when name.Equals("FRANBMCP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP06; + case var _ when name.Equals("FRANBMCP08", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP08; + case var _ when name.Equals("FRANBMCP0A", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP0A; + case var _ when name.Equals("FRANBMCP0B", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP0B; + case var _ when name.Equals("FRANBMCP0C", StringComparison.OrdinalIgnoreCase): + return Model.FRANBMCP0C; + case var _ when name.Equals("FRANGACP04", StringComparison.OrdinalIgnoreCase): + return Model.FRANGACP04; + case var _ when name.Equals("FRANGACP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANGACP06; + case var _ when name.Equals("FRANGACP08", StringComparison.OrdinalIgnoreCase): + return Model.FRANGACP08; + case var _ when name.Equals("FRANMACP04", StringComparison.OrdinalIgnoreCase): + return Model.FRANMACP04; + case var _ when name.Equals("FRANMACP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANMACP06; + case var _ when name.Equals("FRANMACP08", StringComparison.OrdinalIgnoreCase): + return Model.FRANMACP08; + case var _ when name.Equals("FRANMCCP04", StringComparison.OrdinalIgnoreCase): + return Model.FRANMCCP04; + case var _ when name.Equals("FRANMCCP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANMCCP06; + case var _ when name.Equals("FRANMCCP07", StringComparison.OrdinalIgnoreCase): + return Model.FRANMCCP07; + case var _ when name.Equals("FRANMDCP05", StringComparison.OrdinalIgnoreCase): + return Model.FRANMDCP05; + case var _ when name.Equals("FRANMDCP07", StringComparison.OrdinalIgnoreCase): + return Model.FRANMDCP07; + case var _ when name.Equals("FRANMECP02", StringComparison.OrdinalIgnoreCase): + return Model.FRANMECP02; + case var _ when name.Equals("FRANMECP05", StringComparison.OrdinalIgnoreCase): + return Model.FRANMECP05; + case var _ when name.Equals("FRANMECP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANMECP06; + case var _ when name.Equals("FRANMZCP07", StringComparison.OrdinalIgnoreCase): + return Model.FRANMZCP07; + case var _ when name.Equals("FRANMZCP09", StringComparison.OrdinalIgnoreCase): + return Model.FRANMZCP09; + case var _ when name.Equals("FRANMFCP02", StringComparison.OrdinalIgnoreCase): + return Model.FRANMFCP02; + case var _ when name.Equals("FRANMFCP06", StringComparison.OrdinalIgnoreCase): + return Model.FRANMFCP06; + case var _ when name.Equals("FRAPMACP03", StringComparison.OrdinalIgnoreCase): + return Model.FRAPMACP03; + case var _ when name.Equals("FRAPMACP05", StringComparison.OrdinalIgnoreCase): + return Model.FRAPMACP05; + case var _ when name.Equals("FRANMGCP05", StringComparison.OrdinalIgnoreCase): + return Model.FRANMGCP05; + case var _ when name.Equals("FRANMGCP07", StringComparison.OrdinalIgnoreCase): + return Model.FRANMGCP07; + case var _ when name.Equals("FRANMGCP09", StringComparison.OrdinalIgnoreCase): + return Model.FRANMGCP09; case var _ when name.Equals("Base Board Product Name", StringComparison.OrdinalIgnoreCase): case var _ when name.Equals("To be filled by O.E.M.", StringComparison.OrdinalIgnoreCase): return Model.Unknown; diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedController.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedController.cs new file mode 100644 index 0000000..afa712f --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedController.cs @@ -0,0 +1,96 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +using System.Collections.Generic; + +namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc.EC; + +public class ChromeOSEmbeddedController : EmbeddedController +{ + private const byte EC_FAN_SPEED_ENTRIES = 4; + private const ushort EC_FAN_SPEED_NOT_PRESENT = 0xffff; + private const byte EC_MEMMAP_FAN = 0x10; + private const byte EC_MEMMAP_TEMP_SENSOR = 0x00; + private const byte EC_MEMMAP_TEMP_SENSOR_B = 0x18; + private const byte EC_TEMP_SENSOR_B_ENTRIES = 8; + private const byte EC_TEMP_SENSOR_ENTRIES = 16; + private const byte EC_TEMP_SENSOR_NOT_PRESENT = 0xff; + + /* + * The offset of temperature value stored in mapped memory. This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ + private const byte EC_TEMP_SENSOR_OFFSET = 200; + + public ChromeOSEmbeddedController(IEnumerable sources, ISettings settings) : base(sources, settings) + { } + + public static ChromeOSEmbeddedController Create(ISettings settings) + { + List sources = []; + + using ChromeOSEmbeddedControllerIO embeddedControllerIO = new(); + + // Copy the first 0x20 bytes of the EC memory map + byte[] data = embeddedControllerIO.ReadMemmap(0x00, 0x20); + + for (int i = 0; i < EC_TEMP_SENSOR_ENTRIES; i++) + { + byte temp = data[EC_MEMMAP_TEMP_SENSOR + i]; + if (temp == EC_TEMP_SENSOR_NOT_PRESENT) + { + break; + } + + string sensorName = embeddedControllerIO.TempSensorGetName((byte)i); + + sources.Add(new EmbeddedControllerSource(sensorName, + SensorType.Temperature, + (ushort)(EC_MEMMAP_TEMP_SENSOR + i), + offset: EC_TEMP_SENSOR_OFFSET - 273, + blank: EC_TEMP_SENSOR_NOT_PRESENT)); + } + + for (int i = 0; i < EC_FAN_SPEED_ENTRIES; i++) + { + ushort fan = (ushort)(data[EC_MEMMAP_FAN + i * 2] | (data[EC_MEMMAP_FAN + i * 2 + 1] << 8)); + if (fan == EC_FAN_SPEED_NOT_PRESENT) + { + break; + } + + sources.Add(new EmbeddedControllerSource("Fan " + (i + 1), + SensorType.Fan, + (ushort)(EC_MEMMAP_FAN + i * 2), + 2, + blank: EC_FAN_SPEED_NOT_PRESENT, + isLittleEndian: true)); + } + + for (int i = 0; i < EC_TEMP_SENSOR_B_ENTRIES; i++) + { + byte temp = data[EC_MEMMAP_TEMP_SENSOR_B + i]; + if (temp == EC_TEMP_SENSOR_NOT_PRESENT) + { + break; + } + + string sensorName = embeddedControllerIO.TempSensorGetName((byte)(i + EC_TEMP_SENSOR_ENTRIES)); + + sources.Add(new EmbeddedControllerSource(sensorName, + SensorType.Temperature, + (ushort)(EC_MEMMAP_TEMP_SENSOR_B + i), + offset: EC_TEMP_SENSOR_OFFSET - 273, + blank: EC_TEMP_SENSOR_NOT_PRESENT)); + } + + return new ChromeOSEmbeddedController(sources, settings); + } + + protected override IEmbeddedControllerIO AcquireIOInterface() + { + return new ChromeOSEmbeddedControllerIO(); + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedControllerIO.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedControllerIO.cs new file mode 100644 index 0000000..879a120 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/ChromeOSEmbeddedControllerIO.cs @@ -0,0 +1,74 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +using System.Diagnostics; +using System.Text; +using LibreHardwareMonitor.PawnIo; + +namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc.EC; + +public class ChromeOSEmbeddedControllerIO : IEmbeddedControllerIO +{ + private const short EC_CMD_TEMP_SENSOR_GET_INFO = 0x0070; + + private readonly LpcCrOSEc _pawnModule; + private bool _disposed; + + public ChromeOSEmbeddedControllerIO() + { + _pawnModule = new LpcCrOSEc(); + + if (!Mutexes.WaitEc(10)) + { + throw new BusMutexLockingFailedException(); + } + } + + public void Read(ushort[] registers, byte[] data) + { + Trace.Assert(registers.Length <= data.Length, + "data buffer length has to be greater or equal to the registers array length"); + + for (int i = 0; i < registers.Length; i++) + { + data[i] = ReadMemmap((byte)registers[i], 1)[0]; + } + } + + public byte[] ReadMemmap(byte offset, byte bytes) + { + return _pawnModule.ReadMemmap(offset, bytes); + } + + public string TempSensorGetName(byte index) + { + try + { + byte[] resp = _pawnModule.EcCommand(0, EC_CMD_TEMP_SENSOR_GET_INFO, 1, 33, [index]); + //byte sensorType = resp[33]; + return Encoding.ASCII.GetString(resp, 0, 32).TrimEnd('\0'); + } + catch (System.Exception) + { + return "Temp " + index; + } + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + Mutexes.ReleaseEc(); + } + } + + public class BusMutexLockingFailedException : EmbeddedController.IOException + { + public BusMutexLockingFailedException() + : base("could not lock ISA bus mutex") + { } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedController.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedController.cs index 47043dd..73a11b4 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedController.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedController.cs @@ -285,6 +285,48 @@ public abstract class EmbeddedController : Hardware ECSensor.TempCPUPackage, ECSensor.TempMB, ECSensor.TempVrm), + new(Model.FRANMDCP05, + BoardFamily.CrOS, + null), + new(Model.FRANMDCP07, + BoardFamily.CrOS, + null), + new(Model.FRANMECP02, + BoardFamily.CrOS, + null), + new(Model.FRANMECP05, + BoardFamily.CrOS, + null), + new(Model.FRANMECP06, + BoardFamily.CrOS, + null), + new(Model.FRANMZCP07, + BoardFamily.CrOS, + null), + new(Model.FRANMZCP09, + BoardFamily.CrOS, + null), + new(Model.FRANMFCP02, + BoardFamily.CrOS, + null), + new(Model.FRANMFCP06, + BoardFamily.CrOS, + null), + new(Model.FRAPMACP03, + BoardFamily.CrOS, + null), + new(Model.FRAPMACP05, + BoardFamily.CrOS, + null), + new(Model.FRANMGCP05, + BoardFamily.CrOS, + null), + new(Model.FRANMGCP07, + BoardFamily.CrOS, + null), + new(Model.FRANMGCP09, + BoardFamily.CrOS, + null) }; private static readonly Dictionary> _knownSensors = new() @@ -454,6 +496,12 @@ public abstract class EmbeddedController : Hardware } BoardInfo board = boards[0]; + + if (board.Family == BoardFamily.CrOS) + { + return ChromeOSEmbeddedController.Create(settings); + } + IEnumerable sources = board.Sensors.Select(ecs => _knownSensors[board.Family][ecs]); return Environment.OSVersion.Platform switch @@ -474,16 +522,19 @@ public abstract class EmbeddedController : Hardware int readRegister = 0; for (int si = 0; si < _sensors.Count; ++si) { + int littleEndian = _sources[si].IsLittleEndian ? 1 : 0; + int bigEndian = _sources[si].IsLittleEndian ? 0 : 1; + int val = _sources[si].Size switch { 1 => _sources[si].Type switch { SensorType.Temperature => unchecked((sbyte)_data[readRegister]), _ => _data[readRegister] }, - 2 => unchecked((short)((_data[readRegister] << 8) + _data[readRegister + 1])), + 2 => unchecked((short)((_data[readRegister + littleEndian] << 8) + _data[readRegister + bigEndian])), _ => 0 }; readRegister += _sources[si].Size; - _sensors[si].Value = val != _sources[si].Blank ? val * _sources[si].Factor : null; + _sensors[si].Value = val != _sources[si].Blank ? (val * _sources[si].Factor) + _sources[si].Offset : null; } } @@ -612,7 +663,8 @@ public abstract class EmbeddedController : Hardware Intel300, Intel400, Intel600, - Intel700 + Intel700, + CrOS } private struct BoardInfo diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedControllerSource.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedControllerSource.cs index bd0574a..c963a07 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedControllerSource.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Lpc/EC/EmbeddedControllerSource.cs @@ -5,27 +5,21 @@ namespace LibreHardwareMonitor.Hardware.Motherboard.Lpc.EC; -public class EmbeddedControllerSource +public class EmbeddedControllerSource(string name, SensorType type, ushort register, byte size = 1, float factor = 1.0f, float offset = 0.0f, int blank = int.MaxValue, bool isLittleEndian = false) { - public EmbeddedControllerSource(string name, SensorType type, ushort register, byte size = 1, float factor = 1.0f, int blank = int.MaxValue) - { - Name = name; + public int Blank { get; } = blank; - Register = register; - Size = size; - Type = type; - Factor = factor; - Blank = blank; - } + public float Factor { get; } = factor; - public string Name { get; } - public ushort Register { get; } - public byte Size { get; } - public float Factor { get; } + public bool IsLittleEndian { get; } = isLittleEndian; - public int Blank { get; } + public string Name { get; } = name; - public EmbeddedControllerReader Reader { get; } + public float Offset { get; } = offset; - public SensorType Type { get; } -} \ No newline at end of file + public ushort Register { get; } = register; + + public byte Size { get; } = size; + + public SensorType Type { get; } = type; +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Manufacturer.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Manufacturer.cs index da427ba..be017a4 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Manufacturer.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Manufacturer.cs @@ -30,6 +30,7 @@ public enum Manufacturer EVGA, FIC, Foxconn, + Framework, Fujitsu, Gateway, Gigabyte, diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Model.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Model.cs index 4ca3080..5f3aea4 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Model.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Model.cs @@ -314,6 +314,37 @@ public enum Model //Supermicro X11SWN_E, + // Framework + FRANBMCP03, // Intel Core i5-1135G7 + FRANBMCP06, // Intel Core i7-1165G7 + FRANBMCP08, // Intel Core i7-1185G7 + FRANBMCP0A, // Intel Core i5-1135G7 + FRANBMCP0B, // Intel Core i7-1165G7 + FRANBMCP0C, // Intel Core i7-1185G7 + FRANGACP04, // Intel Core i5-1240P + FRANGACP06, // Intel Core i7-1260P + FRANGACP08, // Intel Core i7-1280P + FRANMACP04, // Intel Core i5-1240P + FRANMACP06, // Intel Core i7-1260P + FRANMACP08, // Intel Core i7-1280P + FRANMCCP04, // Intel Core i5-1340P + FRANMCCP06, // Intel Core i7-1360P + FRANMCCP07, // Intel Core i7-1370P + FRANMDCP05, // FW13 AMD Ryzen 5 7640U + FRANMDCP07, // FW13 AMD Ryzen 7 7840U + FRANMECP02, // FW13 Intel Core Ultra 5 125H + FRANMECP05, // FW13 Intel Core Ultra 7 155H + FRANMECP06, // FW13 Intel Core Ultra 7 165H + FRANMZCP07, // FW16 AMD Ryzen 7 7840HS + FRANMZCP09, // FW16 AMD Ryzen 9 7940HS + FRANMFCP02, // Desktop AMD RYZEN AI MAX 385 + FRANMFCP06, // Desktop AMD RYZEN AI MAX+ 395 + FRAPMACP03, // FW12 Intel Core i3-1315U + FRAPMACP05, // FW12 Intel Core i5-1334U + FRANMGCP05, // FW13 AMD Ryzen AI 5 340 + FRANMGCP07, // FW13 AMD Ryzen AI 7 350 + FRANMGCP09, // FW13 AMD Ryzen AI 9 HX 370 + // Unknown Unknown } diff --git a/LibreHardwareMonitorLib/PawnIo/LpcCrOS.cs b/LibreHardwareMonitorLib/PawnIo/LpcCrOS.cs new file mode 100644 index 0000000..19f5536 --- /dev/null +++ b/LibreHardwareMonitorLib/PawnIo/LpcCrOS.cs @@ -0,0 +1,42 @@ +using System; + +namespace LibreHardwareMonitor.PawnIo; + +public class LpcCrOSEc +{ + private readonly PawnIo _pawnIO = PawnIo.LoadModuleFromResource(typeof(LpcCrOSEc).Assembly, $"{nameof(LibreHardwareMonitor)}.Resources.PawnIo.LpcCrOSEC.bin"); + + public byte[] EcCommand(int version, int command, int outsize, int insize, byte[] data) + { + long[] inArray = new long[38]; + inArray[0] = version; + inArray[1] = command; + inArray[2] = outsize; + inArray[3] = insize; + + // Start packing data into inArray at the 4th long (8 bytes) + Buffer.BlockCopy(data, 0, inArray, 4 * 8, data.Length); + + long[] outArray = _pawnIO.Execute("ioctl_ec_command", inArray, 1 + (int)Math.Ceiling(insize / 8.0)); + if (outArray[0] < 0) + { + throw new Exception("EC returned error code " + -outArray[0]); + } + + byte[] retArray = new byte[insize]; + // Unpack the data skipping the first long + Buffer.BlockCopy(outArray, 8, retArray, 0, insize); + return retArray; + } + + public byte[] ReadMemmap(byte offset, byte bytes) + { + long[] inArray = [offset, bytes]; + long[] outArray = _pawnIO.Execute("ioctl_ec_readmem", inArray, (int)Math.Ceiling(bytes / 8.0)); + byte[] retArray = new byte[bytes]; + Buffer.BlockCopy(outArray, 0, retArray, 0, bytes); + return retArray; + } + + public void Close() => _pawnIO.Close(); +} diff --git a/LibreHardwareMonitorLib/Resources/PawnIo/LpcCrOSEC.bin b/LibreHardwareMonitorLib/Resources/PawnIo/LpcCrOSEC.bin new file mode 100644 index 0000000..75562b5 Binary files /dev/null and b/LibreHardwareMonitorLib/Resources/PawnIo/LpcCrOSEC.bin differ