# HG changeset patch
# User moel.mich
# Date 1310075589 0
# Node ID 65a1ae21325d115815bafb7fdcec3d34adc2e5ee
# Parent  d882720734bff66cc389b6b07c69c0261dc7893d
Added fan control for Nvidia GPUs based on a patch by Christian Valli?res.

diff -r d882720734bf -r 65a1ae21325d Hardware/Nvidia/NVAPI.cs
--- a/Hardware/Nvidia/NVAPI.cs	Thu Jul 07 20:41:09 2011 +0000
+++ b/Hardware/Nvidia/NVAPI.cs	Thu Jul 07 21:53:09 2011 +0000
@@ -16,10 +16,11 @@
 
   The Initial Developer of the Original Code is 
   Michael Möller <m.moeller@gmx.ch>.
-  Portions created by the Initial Developer are Copyright (C) 2009-2010
+  Portions created by the Initial Developer are Copyright (C) 2009-2011
   the Initial Developer. All Rights Reserved.
 
   Contributor(s):
+  Christian Vallières
 
   Alternatively, the contents of this file may be used under the terms of
   either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -97,17 +98,17 @@
 
   internal enum NvThermalController {
     NONE = 0,
-    GPU_INTERNAL,  
+    GPU_INTERNAL,
     ADM1032,
-    MAX6649,       
-    MAX1617,      
-    LM99,      
-    LM89,         
-    LM64,         
+    MAX6649,
+    MAX1617,
+    LM99,
+    LM89,
+    LM64,
     ADT7473,
     SBMAX6649,
-    VBIOSEVT,  
-    OS,    
+    VBIOSEVT,
+    OS,
     UNKNOWN = -1,
   }
 
@@ -127,14 +128,14 @@
     public uint DefaultMinTemp;
     public uint DefaultMaxTemp;
     public uint CurrentTemp;
-    public NvThermalTarget Target;     
+    public NvThermalTarget Target;
   }
 
   [StructLayout(LayoutKind.Sequential, Pack = 8)]
   internal struct NvGPUThermalSettings {
     public uint Version;
     public uint Count;
-    [MarshalAs(UnmanagedType.ByValArray, 
+    [MarshalAs(UnmanagedType.ByValArray,
       SizeConst = NVAPI.MAX_THERMAL_SENSORS_PER_GPU)]
     public NvSensor[] Sensor;
   }
@@ -202,9 +203,22 @@
   }
 
   [StructLayout(LayoutKind.Sequential, Pack = 8)]
+  internal struct NvLevel {
+    public int Level;
+    public int Policy;
+  }
+
+  [StructLayout(LayoutKind.Sequential, Pack = 8)]
+  internal struct NvGPUCoolerLevels {
+    public uint Version;
+    [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_COOLER_PER_GPU)]
+    public NvLevel[] Levels;
+  }
+
+  [StructLayout(LayoutKind.Sequential, Pack = 8)]
   internal struct NvMemoryInfo {
     public uint Version;
-    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 
+    [MarshalAs(UnmanagedType.ByValArray, SizeConst =
       NVAPI.MAX_MEMORY_VALUES_PER_GPU)]
     public uint[] Values;
   }
@@ -225,7 +239,7 @@
     public const int MAX_PHYSICAL_GPUS = 64;
     public const int SHORT_STRING_MAX = 64;
 
-    public const int MAX_THERMAL_SENSORS_PER_GPU = 3;    
+    public const int MAX_THERMAL_SENSORS_PER_GPU = 3;
     public const int MAX_CLOCKS_PER_GPU = 0x120;
     public const int MAX_PSTATES_PER_GPU = 8;
     public const int MAX_USAGES_PER_GPU = 33;
@@ -242,23 +256,25 @@
       Marshal.SizeOf(typeof(NvUsages)) | 0x10000;
     public static readonly uint GPU_COOLER_SETTINGS_VER = (uint)
       Marshal.SizeOf(typeof(NvGPUCoolerSettings)) | 0x20000;
-    public static readonly uint GPU_MEMORY_INFO_VER = (uint) 
+    public static readonly uint GPU_MEMORY_INFO_VER = (uint)
       Marshal.SizeOf(typeof(NvMemoryInfo)) | 0x20000;
     public static readonly uint DISPLAY_DRIVER_VERSION_VER = (uint)
       Marshal.SizeOf(typeof(NvDisplayDriverVersion)) | 0x10000;
-      
+    public static readonly uint GPU_COOLER_LEVELS_VER = (uint)
+      Marshal.SizeOf(typeof(NvGPUCoolerLevels)) | 0x10000;
+
     private delegate IntPtr nvapi_QueryInterfaceDelegate(uint id);
     private delegate NvStatus NvAPI_InitializeDelegate();
     private delegate NvStatus NvAPI_GPU_GetFullNameDelegate(
       NvPhysicalGpuHandle gpuHandle, StringBuilder name);
 
     public delegate NvStatus NvAPI_GPU_GetThermalSettingsDelegate(
-      NvPhysicalGpuHandle gpuHandle, int sensorIndex, 
+      NvPhysicalGpuHandle gpuHandle, int sensorIndex,
       ref NvGPUThermalSettings nvGPUThermalSettings);
     public delegate NvStatus NvAPI_EnumNvidiaDisplayHandleDelegate(int thisEnum,
       ref NvDisplayHandle displayHandle);
     public delegate NvStatus NvAPI_GetPhysicalGPUsFromDisplayDelegate(
-      NvDisplayHandle displayHandle, [Out] NvPhysicalGpuHandle[] gpuHandles, 
+      NvDisplayHandle displayHandle, [Out] NvPhysicalGpuHandle[] gpuHandles,
       out uint gpuCount);
     public delegate NvStatus NvAPI_EnumPhysicalGPUsDelegate(
       [Out] NvPhysicalGpuHandle[] gpuHandles, out int gpuCount);
@@ -273,6 +289,9 @@
     public delegate NvStatus NvAPI_GPU_GetCoolerSettingsDelegate(
       NvPhysicalGpuHandle gpuHandle, int coolerIndex,
       ref NvGPUCoolerSettings nvGPUCoolerSettings);
+    public delegate NvStatus NvAPI_GPU_SetCoolerLevelsDelegate(
+      NvPhysicalGpuHandle gpuHandle, int coolerIndex,
+      ref NvGPUCoolerLevels NvGPUCoolerLevels);
     public delegate NvStatus NvAPI_GPU_GetMemoryInfoDelegate(
       NvDisplayHandle displayHandle, ref NvMemoryInfo nvMemoryInfo);
     public delegate NvStatus NvAPI_GetDisplayDriverVersionDelegate(
@@ -284,12 +303,12 @@
     private static readonly bool available;
     private static readonly nvapi_QueryInterfaceDelegate nvapi_QueryInterface;
     private static readonly NvAPI_InitializeDelegate NvAPI_Initialize;
-    private static readonly NvAPI_GPU_GetFullNameDelegate 
+    private static readonly NvAPI_GPU_GetFullNameDelegate
       _NvAPI_GPU_GetFullName;
     private static readonly NvAPI_GetInterfaceVersionStringDelegate
       _NvAPI_GetInterfaceVersionString;
 
-    public static readonly NvAPI_GPU_GetThermalSettingsDelegate 
+    public static readonly NvAPI_GPU_GetThermalSettingsDelegate
       NvAPI_GPU_GetThermalSettings;
     public static readonly NvAPI_EnumNvidiaDisplayHandleDelegate
       NvAPI_EnumNvidiaDisplayHandle;
@@ -307,6 +326,8 @@
       NvAPI_GPU_GetUsages;
     public static readonly NvAPI_GPU_GetCoolerSettingsDelegate
       NvAPI_GPU_GetCoolerSettings;
+    public static readonly NvAPI_GPU_SetCoolerLevelsDelegate
+      NvAPI_GPU_SetCoolerLevels;
     public static readonly NvAPI_GPU_GetMemoryInfoDelegate
       NvAPI_GPU_GetMemoryInfo;
     public static readonly NvAPI_GetDisplayDriverVersionDelegate
@@ -345,9 +366,8 @@
       }
     }
 
-    private static void GetDelegate<T>(uint id, out T newDelegate) 
-      where T : class 
-    {
+    private static void GetDelegate<T>(uint id, out T newDelegate)
+      where T : class {
       IntPtr ptr = nvapi_QueryInterface(id);
       if (ptr != IntPtr.Zero) {
         newDelegate =
@@ -357,7 +377,7 @@
       }
     }
 
-    static NVAPI() { 
+    static NVAPI() {
       DllImportAttribute attribute = new DllImportAttribute(GetDllName());
       attribute.CallingConvention = CallingConvention.Cdecl;
       attribute.PreserveSig = true;
@@ -382,6 +402,7 @@
         GetDelegate(0x60DED2ED, out NvAPI_GPU_GetPStates);
         GetDelegate(0x189A1FDF, out NvAPI_GPU_GetUsages);
         GetDelegate(0xDA141340, out NvAPI_GPU_GetCoolerSettings);
+        GetDelegate(0x891FA0AE, out NvAPI_GPU_SetCoolerLevels);
         GetDelegate(0x774AA982, out NvAPI_GPU_GetMemoryInfo);
         GetDelegate(0xF951A4D1, out NvAPI_GetDisplayDriverVersion);
         GetDelegate(0x01053FA5, out _NvAPI_GetInterfaceVersionString);
diff -r d882720734bf -r 65a1ae21325d Hardware/Nvidia/NvidiaGPU.cs
--- a/Hardware/Nvidia/NvidiaGPU.cs	Thu Jul 07 20:41:09 2011 +0000
+++ b/Hardware/Nvidia/NvidiaGPU.cs	Thu Jul 07 21:53:09 2011 +0000
@@ -20,6 +20,7 @@
   the Initial Developer. All Rights Reserved.
 
   Contributor(s):
+  Christian Vallières
 
   Alternatively, the contents of this file may be used under the terms of
   either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -52,12 +53,15 @@
     private readonly Sensor[] loads;
     private readonly Sensor control;
     private readonly Sensor memoryLoad;
+    private readonly Control fanControl;
 
-    public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle, 
-      NvDisplayHandle? displayHandle, ISettings settings) 
-      : base(GetName(handle), new Identifier("nvidiagpu", 
-          adapterIndex.ToString(CultureInfo.InvariantCulture)), settings)
-    {
+    private bool restoreDefaultFanSpeedRequired;
+    private NvLevel initialFanSpeedValue;
+
+    public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle,
+      NvDisplayHandle? displayHandle, ISettings settings)
+      : base(GetName(handle), new Identifier("nvidiagpu",
+          adapterIndex.ToString(CultureInfo.InvariantCulture)), settings) {
       this.adapterIndex = adapterIndex;
       this.handle = handle;
       this.displayHandle = displayHandle;
@@ -103,6 +107,18 @@
       memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings);
 
       control = new Sensor("GPU Fan", 0, SensorType.Control, this, settings);
+
+      NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
+      if (coolerSettings.Count > 0) {
+        fanControl = new Control(control, settings,
+          coolerSettings.Cooler[0].DefaultMin, 
+          coolerSettings.Cooler[0].DefaultMax);
+        fanControl.ControlModeChanged += ControlModeChanged;
+        fanControl.SoftwareControlValueChanged += SoftwareControlValueChanged;
+        ControlModeChanged(fanControl);
+        control.Control = fanControl;
+      }
+      Update();
     }
 
     private static string GetName(NvPhysicalGpuHandle handle) {
@@ -123,12 +139,26 @@
       settings.Version = NVAPI.GPU_THERMAL_SETTINGS_VER;
       settings.Count = NVAPI.MAX_THERMAL_SENSORS_PER_GPU;
       settings.Sensor = new NvSensor[NVAPI.MAX_THERMAL_SENSORS_PER_GPU];
-      if (NVAPI.NvAPI_GPU_GetThermalSettings != null &&
+      if (!(NVAPI.NvAPI_GPU_GetThermalSettings != null &&
         NVAPI.NvAPI_GPU_GetThermalSettings(handle, (int)NvThermalTarget.ALL,
-        ref settings) != NvStatus.OK) {
-        settings.Count = 0;        
+          ref settings) == NvStatus.OK)) 
+      {
+        settings.Count = 0;
+      }       
+      return settings;    
+    }
+
+    private NvGPUCoolerSettings GetCoolerSettings() {
+      NvGPUCoolerSettings settings = new NvGPUCoolerSettings();
+      settings.Version = NVAPI.GPU_COOLER_SETTINGS_VER;
+      settings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU];
+      if (!(NVAPI.NvAPI_GPU_GetCoolerSettings != null &&
+        NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, 
+          ref settings) == NvStatus.OK)) 
+      {
+        settings.Count = 0;
       }
-      return settings;
+      return settings;  
     }
 
     private uint[] GetClocks() {
@@ -144,7 +174,7 @@
 
     public override void Update() {
       NvGPUThermalSettings settings = GetThermalSettings();
-      foreach (Sensor sensor in temperatures) 
+      foreach (Sensor sensor in temperatures)
         sensor.Value = settings.Sensor[sensor.Index].CurrentTemp;
 
       if (fan != null) {
@@ -167,7 +197,7 @@
       NvPStates states = new NvPStates();
       states.Version = NVAPI.GPU_PSTATES_VER;
       states.PStates = new NvPState[NVAPI.MAX_PSTATES_PER_GPU];
-      if (NVAPI.NvAPI_GPU_GetPStates != null && 
+      if (NVAPI.NvAPI_GPU_GetPStates != null &&
         NVAPI.NvAPI_GPU_GetPStates(handle, ref states) == NvStatus.OK) {
         for (int i = 0; i < 3; i++)
           if (states.PStates[i].Present) {
@@ -188,12 +218,9 @@
         }
       }
 
-      NvGPUCoolerSettings coolerSettings = new NvGPUCoolerSettings();
-      coolerSettings.Version = NVAPI.GPU_COOLER_SETTINGS_VER;
-      coolerSettings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU];
-      if (NVAPI.NvAPI_GPU_GetCoolerSettings != null && 
-        NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, ref coolerSettings) ==
-        NvStatus.OK && coolerSettings.Count > 0) {
+
+      NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
+      if (coolerSettings.Count > 0) {
         control.Value = coolerSettings.Cooler[0].CurrentLevel;
         ActivateSensor(control);
       }
@@ -202,9 +229,8 @@
       memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER;
       memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU];
       if (NVAPI.NvAPI_GPU_GetMemoryInfo != null && displayHandle.HasValue &&
-        NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, ref memoryInfo) == 
-        NvStatus.OK) 
-      {
+        NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, ref memoryInfo) ==
+        NvStatus.OK) {
         uint totalMemory = memoryInfo.Values[0];
         uint freeMemory = memoryInfo.Values[4];
         float usedMemory = Math.Max(totalMemory - freeMemory, 0);
@@ -221,9 +247,8 @@
 
       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
       r.AppendFormat("Index: {0}{1}", adapterIndex, Environment.NewLine);
-      
-      if (displayHandle.HasValue && NVAPI.NvAPI_GetDisplayDriverVersion != null) 
-      {
+
+      if (displayHandle.HasValue && NVAPI.NvAPI_GetDisplayDriverVersion != null) {
         NvDisplayDriverVersion driverVersion = new NvDisplayDriverVersion();
         driverVersion.Version = NVAPI.DISPLAY_DRIVER_VERSION_VER;
         if (NVAPI.NvAPI_GetDisplayDriverVersion(displayHandle.Value,
@@ -231,7 +256,7 @@
           r.Append("Driver Version: ");
           r.Append(driverVersion.DriverVersion / 100);
           r.Append(".");
-          r.Append((driverVersion.DriverVersion % 100).ToString("00", 
+          r.Append((driverVersion.DriverVersion % 100).ToString("00",
             CultureInfo.InvariantCulture));
           r.AppendLine();
           r.Append("Driver Branch: ");
@@ -269,7 +294,7 @@
           r.AppendLine(status.ToString());
         }
         r.AppendLine();
-      }      
+      }
 
       if (NVAPI.NvAPI_GPU_GetAllClocks != null) {
         NvClocks allClocks = new NvClocks();
@@ -290,10 +315,10 @@
           r.AppendLine(status.ToString());
         }
         r.AppendLine();
-      }         
-           
+      }
+
       if (NVAPI.NvAPI_GPU_GetTachReading != null) {
-        int tachValue; 
+        int tachValue;
         NvStatus status = NVAPI.NvAPI_GPU_GetTachReading(handle, out tachValue);
 
         r.AppendLine("Tachometer");
@@ -332,7 +357,7 @@
         usages.Version = NVAPI.GPU_USAGES_VER;
         usages.Usage = new uint[NVAPI.MAX_USAGES_PER_GPU];
         NvStatus status = NVAPI.NvAPI_GPU_GetUsages(handle, ref usages);
-     
+
         r.AppendLine("Usages");
         r.AppendLine();
         if (status == NvStatus.OK) {
@@ -394,7 +419,7 @@
         NvMemoryInfo memoryInfo = new NvMemoryInfo();
         memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER;
         memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU];
-        NvStatus status = NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, 
+        NvStatus status = NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value,
           ref memoryInfo);
 
         r.AppendLine("Memory Info");
@@ -412,5 +437,45 @@
 
       return r.ToString();
     }
+
+    private void SoftwareControlValueChanged(IControl control) {
+      SaveDefaultFanSpeed();
+      NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels();
+      coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER;
+      coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU];
+      coolerLevels.Levels[0].Level = (int)control.SoftwareValue;
+      coolerLevels.Levels[0].Policy = 1;
+      NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels);
+    }
+
+    private void SaveDefaultFanSpeed() {
+      if (!restoreDefaultFanSpeedRequired) {
+        NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
+        if (coolerSettings.Count > 0) {
+          restoreDefaultFanSpeedRequired = true;
+          initialFanSpeedValue.Level = coolerSettings.Cooler[0].CurrentLevel;
+          initialFanSpeedValue.Policy = coolerSettings.Cooler[0].CurrentPolicy;
+        }
+      }
+    }
+
+    private void ControlModeChanged(IControl control) {
+      if (control.ControlMode == ControlMode.Default) {
+        RestoreDefaultFanSpeed();
+      } else {
+        SoftwareControlValueChanged(control);
+      }
+    }
+
+    private void RestoreDefaultFanSpeed() {
+      if (restoreDefaultFanSpeedRequired) {
+        NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels();
+        coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER;
+        coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU];
+        coolerLevels.Levels[0] = initialFanSpeedValue;
+        NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels);
+        restoreDefaultFanSpeedRequired = false;
+      }
+    }
   }
 }