diff --git a/FanControl/FanControl.csproj b/FanControl/FanControl.csproj new file mode 100644 index 0000000..1b245fc --- /dev/null +++ b/FanControl/FanControl.csproj @@ -0,0 +1,22 @@ + + + + Exe + net6.0-windows + enable + enable + True + FanControl.Program + + + + + + + + + PreserveNewest + + + + diff --git a/FanControl/InpOut.cs b/FanControl/InpOut.cs new file mode 100644 index 0000000..87275b8 --- /dev/null +++ b/FanControl/InpOut.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace FanControl +{ + internal class InpOut + { + [DllImport("inpoutx64.dll", EntryPoint = "MapPhysToLin", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr MapPhysToLin(IntPtr pbPhysAddr, uint dwPhysSize, out IntPtr pPhysicalMemoryHandle); + + [DllImport("inpoutx64.dll", EntryPoint = "UnmapPhysicalMemory", CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle, IntPtr pbLinAddr); + + [DllImport("inpoutx64.dll", EntryPoint = "DlPortReadPortUchar", CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern byte DlPortReadPortUchar(ushort port); + + [DllImport("inpoutx64.dll", EntryPoint = "DlPortWritePortUchar", CallingConvention = CallingConvention.StdCall)] + public static extern byte DlPortWritePortUchar(ushort port, byte vlaue); + + public static byte[] ReadMemory(IntPtr baseAddress, uint size) + { + IntPtr pdwLinAddr = MapPhysToLin(baseAddress, size, out IntPtr pPhysicalMemoryHandle); + if (pdwLinAddr != IntPtr.Zero) + { + byte[] bytes = new byte[size]; + Marshal.Copy(pdwLinAddr, bytes, 0, bytes.Length); + UnmapPhysicalMemory(pPhysicalMemoryHandle, pdwLinAddr); + + return bytes; + } + + return null; + } + + public static bool WriteMemory(IntPtr baseAddress, byte[] data) + { + IntPtr pdwLinAddr = MapPhysToLin(baseAddress, (uint)data.Length, out IntPtr pPhysicalMemoryHandle); + if (pdwLinAddr != IntPtr.Zero) + { + Marshal.Copy(data, 0, pdwLinAddr, data.Length); + UnmapPhysicalMemory(pPhysicalMemoryHandle, pdwLinAddr); + + return true; + } + + return false; + } + } +} diff --git a/FanControl/Program.cs b/FanControl/Program.cs new file mode 100644 index 0000000..3886619 --- /dev/null +++ b/FanControl/Program.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LibreHardwareMonitor.Hardware; + +namespace FanControl +{ + internal class Program + { + public class UpdateVisitor : IVisitor + { + public void VisitComputer(IComputer computer) + { + computer.Traverse(this); + } + public void VisitHardware(IHardware hardware) + { + hardware.Update(); + foreach (IHardware subHardware in hardware.SubHardware) subHardware.Accept(this); + } + public void VisitSensor(ISensor sensor) { } + public void VisitParameter(IParameter parameter) { } + } + + public static void Monitor() + { + Computer computer = new Computer + { + IsCpuEnabled = true, + IsGpuEnabled = true, + IsMemoryEnabled = true, + IsMotherboardEnabled = true, + IsControllerEnabled = true, + IsNetworkEnabled = true, + IsStorageEnabled = true + }; + + computer.Open(); + computer.Accept(new UpdateVisitor()); + + foreach (IHardware hardware in computer.Hardware) + { + Console.WriteLine("Hardware: {0}", hardware.Name); + + foreach (IHardware subhardware in hardware.SubHardware) + { + Console.WriteLine("\tSubhardware: {0}", subhardware.Name); + + foreach (ISensor sensor in subhardware.Sensors) + { + Console.WriteLine("\t\tSensor: {0}, value: {1}", sensor.Name, sensor.Value); + } + } + + foreach (ISensor sensor in hardware.Sensors) + { + Console.WriteLine("\tSensor: {0}, value: {1}", sensor.Name, sensor.Value); + } + } + + computer.Close(); + } + + static void Main(string[] args) + { + // Monitor(); + + while (true) + { + Thread.Sleep(300); + + Vlv0100.SetFanControl(false); + Vlv0100.SetFanDesiredRPM(6000); + + Console.WriteLine("Fan RPM: {0}", Vlv0100.GetFanRPM()); + Console.WriteLine("Fan Desired RPM: {0}", Vlv0100.GetFanDesiredRPM()); + } + } + } +} diff --git a/FanControl/Vlv0100.cs b/FanControl/Vlv0100.cs new file mode 100644 index 0000000..cc83939 --- /dev/null +++ b/FanControl/Vlv0100.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace FanControl +{ + internal class Vlv0100 + { + // Those addresses are taken from DSDT for VLV0100 + // and might change at any time with a BIOS update + static IntPtr FSLO_FSHI = new IntPtr(0xFE700B00 + 0x92); + static IntPtr GNLO_GNHI = new IntPtr(0xFE700B00 + 0x95); + static IntPtr FRPR = new IntPtr(0xFE700B00 + 0x97); + static IntPtr FNRL_FNRH = new IntPtr(0xFE700300 + 0xB0); + static IntPtr FNCK = new IntPtr(0xFE700300 + 0x9F); + static ushort IO6C = 0x6C; + + public static ushort GetFanDesiredRPM() + { + byte[] data = InpOut.ReadMemory(FSLO_FSHI, 2); + return BitConverter.ToUInt16(data); + } + + public static ushort GetFanRPM() + { + byte[] data = InpOut.ReadMemory(FNRL_FNRH, 2); + return BitConverter.ToUInt16(data); + } + public static void SetFanControl(Boolean userControlled) + { + SetGain(10); + SetRampRate(userControlled ? (byte)10 : (byte)20); + + InpOut.DlPortWritePortUchar(IO6C, userControlled ? (byte)0xCC : (byte)0xCD); + } + + public static void SetFanDesiredRPM(ushort rpm) + { + if (rpm > 0x1C84) + rpm = 0x1C84; + + byte[] data = BitConverter.GetBytes(rpm); + InpOut.WriteMemory(FSLO_FSHI, data); + } + + public static bool GetFanCheck() + { + byte[] data = InpOut.ReadMemory(FNCK, 1); + return (data[0] & 0x1) != 0; + } + + private static void SetGain(ushort gain) + { + byte[] data = BitConverter.GetBytes(gain); + InpOut.WriteMemory(GNLO_GNHI, data); + } + private static void SetRampRate(byte rampRate) + { + byte[] data = BitConverter.GetBytes(rampRate); + InpOut.WriteMemory(FRPR, data); + } + } +} diff --git a/FanControl/inpoutx64.dll b/FanControl/inpoutx64.dll new file mode 100644 index 0000000..82c343f Binary files /dev/null and b/FanControl/inpoutx64.dll differ diff --git a/SteamDeckTools.sln b/SteamDeckTools.sln new file mode 100644 index 0000000..5f8a00a --- /dev/null +++ b/SteamDeckTools.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32901.215 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FanControl", "FanControl\FanControl.csproj", "{A0D9B170-89BA-4F7A-9331-BE6A721E8D5B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A0D9B170-89BA-4F7A-9331-BE6A721E8D5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0D9B170-89BA-4F7A-9331-BE6A721E8D5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0D9B170-89BA-4F7A-9331-BE6A721E8D5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0D9B170-89BA-4F7A-9331-BE6A721E8D5B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FCAAD784-C54A-40A3-AE4A-ED9DD2299E6E} + EndGlobalSection +EndGlobal