# HG changeset patch
# User moel.mich
# Date 1295559114 0
# Node ID 6dc755f1970e70a5182762203d0aee5d29719047
# Parent  59024371cd504a8f5ae980b65eb943be1e867808
Added a minimal control interface to allow manual fan control and implemented the interface for ATI GPUs.

diff -r 59024371cd50 -r 6dc755f1970e GUI/MainForm.cs
--- a/GUI/MainForm.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/GUI/MainForm.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -461,6 +461,34 @@
             };
             sensorContextMenu.MenuItems.Add(item);
           }
+          if (node.Sensor.Control != null) {
+            IControl control = node.Sensor.Control;
+            MenuItem controlItem = new MenuItem("Control");
+            MenuItem defaultItem = new MenuItem("Default");
+            defaultItem.Checked = control.ControlMode == ControlMode.Default;
+            controlItem.MenuItems.Add(defaultItem);
+            defaultItem.Click += delegate(object obj, EventArgs args) {
+              control.SetDefault();
+            };
+            MenuItem manualItem = new MenuItem("Manual");            
+            controlItem.MenuItems.Add(manualItem);
+            manualItem.Checked = control.ControlMode == ControlMode.Software;
+            for (int i = 0; i <= 100; i += 5) {
+              if (i <= control.MaxSoftwareValue &&
+                  i >= control.MinSoftwareValue) 
+              {
+                MenuItem item = new MenuItem(i + " %");
+                manualItem.MenuItems.Add(item);
+                item.Checked = control.ControlMode == ControlMode.Software &&
+                  Math.Round(control.SoftwareValue) == i;
+                int softwareValue = i;
+                item.Click += delegate(object obj, EventArgs args) {
+                  control.SetSoftware(softwareValue);
+                };
+              }
+            }
+            sensorContextMenu.MenuItems.Add(controlItem);
+          }
 
           sensorContextMenu.Show(treeView, new Point(m.X, m.Y));
         }
diff -r 59024371cd50 -r 6dc755f1970e Hardware/ATI/ADL.cs
--- a/Hardware/ATI/ADL.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/ATI/ADL.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -122,6 +122,7 @@
     public const int ADL_DL_FANCTRL_SUPPORTS_PERCENT_WRITE = 2;
     public const int ADL_DL_FANCTRL_SUPPORTS_RPM_READ = 4;
     public const int ADL_DL_FANCTRL_SUPPORTS_RPM_WRITE = 8;
+    public const int ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED = 1;
 
     public const int ATI_VENDOR_ID1 = 1002;
     public const int ATI_VENDOR_ID2 = 0x1002;
@@ -149,6 +150,10 @@
     public delegate int ADL_Overdrive5_FanSpeedInfo_GetDelegate(
       int adapterIndex, int thermalControllerIndex,
       ref ADLFanSpeedInfo fanSpeedInfo);
+    public delegate int ADL_Overdrive5_FanSpeedToDefault_SetDelegate(
+      int adapterIndex, int thermalControllerIndex);
+    public delegate int ADL_Overdrive5_FanSpeed_SetDelegate(int adapterIndex,
+      int thermalControllerIndex, ref	ADLFanSpeedValue fanSpeedValue);
 
     private static ADL_Main_Control_CreateDelegate
       _ADL_Main_Control_Create;
@@ -173,6 +178,10 @@
       ADL_Overdrive5_FanSpeed_Get;
     public static ADL_Overdrive5_FanSpeedInfo_GetDelegate
       ADL_Overdrive5_FanSpeedInfo_Get;
+    public static ADL_Overdrive5_FanSpeedToDefault_SetDelegate
+      ADL_Overdrive5_FanSpeedToDefault_Set;
+    public static ADL_Overdrive5_FanSpeed_SetDelegate
+      ADL_Overdrive5_FanSpeed_Set;
 
     private static string dllName;
 
@@ -215,6 +224,10 @@
         out ADL_Overdrive5_FanSpeed_Get);
       GetDelegate("ADL_Overdrive5_FanSpeedInfo_Get",
         out ADL_Overdrive5_FanSpeedInfo_Get);
+      GetDelegate("ADL_Overdrive5_FanSpeedToDefault_Set",
+        out ADL_Overdrive5_FanSpeedToDefault_Set);
+      GetDelegate("ADL_Overdrive5_FanSpeed_Set",
+        out ADL_Overdrive5_FanSpeed_Set);
     }
 
     static ADL() {
diff -r 59024371cd50 -r 6dc755f1970e Hardware/ATI/ATIGPU.cs
--- a/Hardware/ATI/ATIGPU.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/ATI/ATIGPU.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -51,7 +51,10 @@
     private readonly Sensor memoryClock;
     private readonly Sensor coreVoltage;
     private readonly Sensor coreLoad;
-    private readonly Sensor fanControl;
+    private readonly Sensor controlSensor;
+    private readonly Control fanControl;
+
+    private ADLFanSpeedValue initialFanSpeedValue;
 
     public ATIGPU(string name, int adapterIndex, int busNumber, 
       int deviceNumber, ISettings settings) 
@@ -61,16 +64,61 @@
       this.busNumber = busNumber;
       this.deviceNumber = deviceNumber;
 
+      this.initialFanSpeedValue = new ADLFanSpeedValue();
+      this.initialFanSpeedValue.SpeedType = 
+        ADL.ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
+      ADL.ADL_Overdrive5_FanSpeed_Get(adapterIndex, 0, 
+        ref this.initialFanSpeedValue);
+
       this.temperature = new Sensor("GPU Core", 0, SensorType.Temperature, this, settings);
       this.fan = new Sensor("GPU Fan", 0, SensorType.Fan, this, settings);
       this.coreClock = new Sensor("GPU Core", 0, SensorType.Clock, this, settings);
       this.memoryClock = new Sensor("GPU Memory", 1, SensorType.Clock, this, settings);
       this.coreVoltage = new Sensor("GPU Core", 0, SensorType.Voltage, this, settings);
       this.coreLoad = new Sensor("GPU Core", 0, SensorType.Load, this, settings);
-      this.fanControl = new Sensor("GPU Fan", 0, SensorType.Control, this, settings);
+      this.controlSensor = new Sensor("GPU Fan", 0, SensorType.Control, this, settings);
+
+      ADLFanSpeedInfo afsi = new ADLFanSpeedInfo();
+      if (ADL.ADL_Overdrive5_FanSpeedInfo_Get(adapterIndex, 0, ref afsi)
+        != ADL.ADL_OK) 
+      {
+        afsi.MaxPercent = 100;
+        afsi.MinPercent = 0;
+      }
+
+      this.fanControl = new Control(controlSensor, settings, afsi.MinPercent, 
+        afsi.MaxPercent);
+      this.fanControl.ControlModeChanged += ControlModeChanged;
+      this.fanControl.SoftwareControlValueChanged += 
+        SoftwareControlValueChanged;
+      ControlModeChanged(fanControl);
+      this.controlSensor.Control = fanControl;
       Update();                   
     }
 
+    private void SoftwareControlValueChanged(IControl control) {
+      if (control.ControlMode == ControlMode.Software) {
+        ADLFanSpeedValue adlf = new ADLFanSpeedValue();
+        adlf.SpeedType = ADL.ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
+        adlf.Flags = ADL.ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED;
+        adlf.FanSpeed = (int)control.SoftwareValue;
+        ADL.ADL_Overdrive5_FanSpeed_Set(adapterIndex, 0, ref adlf);
+      }
+    }
+
+    private void ControlModeChanged(IControl control) {
+      if (control.ControlMode == ControlMode.Default) {
+        ADL.ADL_Overdrive5_FanSpeed_Set(adapterIndex, 0,
+          ref this.initialFanSpeedValue);
+      } else {
+        ADLFanSpeedValue adlf = new ADLFanSpeedValue();
+        adlf.SpeedType = ADL.ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
+        adlf.Flags = ADL.ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED;
+        adlf.FanSpeed = (int)control.SoftwareValue;
+        ADL.ADL_Overdrive5_FanSpeed_Set(adapterIndex, 0, ref adlf);
+      }
+    }
+
     public int BusNumber { get { return busNumber; } }
 
     public int DeviceNumber { get { return deviceNumber; } }
@@ -116,10 +164,10 @@
       adlf.SpeedType = ADL.ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
       if (ADL.ADL_Overdrive5_FanSpeed_Get(adapterIndex, 0, ref adlf)
         == ADL.ADL_OK) {
-        fanControl.Value = adlf.FanSpeed;
-        ActivateSensor(fanControl);
+        controlSensor.Value = adlf.FanSpeed;
+        ActivateSensor(controlSensor);
       } else {
-        fanControl.Value = null;
+        controlSensor.Value = null;
       }
 
       ADLPMActivity adlp = new ADLPMActivity();
@@ -150,5 +198,14 @@
         coreLoad.Value = null;
       }
     }
+
+    public void Close() {
+      this.fanControl.ControlModeChanged -= ControlModeChanged;
+      this.fanControl.SoftwareControlValueChanged -=
+        SoftwareControlValueChanged;
+
+      ADL.ADL_Overdrive5_FanSpeed_Set(adapterIndex, 0,
+        ref this.initialFanSpeedValue);
+    }
   }
 }
diff -r 59024371cd50 -r 6dc755f1970e Hardware/ATI/ATIGroup.cs
--- a/Hardware/ATI/ATIGroup.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/ATI/ATIGroup.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -144,6 +144,8 @@
 
     public void Close() {
       try {
+        foreach (ATIGPU gpu in hardware)
+          gpu.Close();
         ADL.ADL_Main_Control_Destroy();
       } catch (Exception) { }
     }
diff -r 59024371cd50 -r 6dc755f1970e Hardware/Control.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Hardware/Control.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -0,0 +1,143 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  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
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Globalization;
+
+namespace OpenHardwareMonitor.Hardware {
+
+  internal delegate void ControlEventHandler(Control control);
+
+  internal class Control : IControl {
+
+    private readonly Identifier identifier;
+    private readonly ISettings settings;
+    private ControlMode mode;
+    private float softwareValue;
+    private float minSoftwareValue;
+    private float maxSoftwareValue;
+
+    public Control(ISensor sensor, ISettings settings, float minSoftwareValue,
+      float maxSoftwareValue) 
+    {
+      this.identifier = new Identifier(sensor.Identifier, "control");
+      this.settings = settings;
+      this.minSoftwareValue = minSoftwareValue;
+      this.maxSoftwareValue = maxSoftwareValue;
+
+      if (!float.TryParse(settings.GetValue(
+          new Identifier(identifier, "value").ToString(), "0"),
+        NumberStyles.Float, CultureInfo.InvariantCulture,
+        out this.softwareValue)) 
+      {
+        this.softwareValue = 0;
+      }
+      int mode;
+      if (!int.TryParse(settings.GetValue(
+          new Identifier(identifier, "mode").ToString(),
+          ((int)ControlMode.Default).ToString(CultureInfo.InvariantCulture)),
+        NumberStyles.Integer, CultureInfo.InvariantCulture,
+        out mode)) 
+      {
+        this.mode = ControlMode.Default;
+      } else {
+        this.mode = (ControlMode)mode;
+      }
+    }
+
+    public Identifier Identifier {
+      get {
+        return identifier;
+      }
+    }
+
+    public ControlMode ControlMode {
+      get {
+        return mode;
+      }
+      private set {
+        if (mode != value) {
+          mode = value;
+          if (ControlModeChanged != null)
+            ControlModeChanged(this);
+          this.settings.SetValue(new Identifier(identifier, "mode").ToString(),
+            ((int)mode).ToString(CultureInfo.InvariantCulture));
+        }
+      }
+    }
+
+    public float SoftwareValue {
+      get {
+        return softwareValue;
+      }
+      private set {
+        if (softwareValue != value) {
+          softwareValue = value;
+          if (SoftwareControlValueChanged != null)
+            SoftwareControlValueChanged(this);
+          this.settings.SetValue(new Identifier(identifier,
+            "value").ToString(),
+            value.ToString(CultureInfo.InvariantCulture));
+        }
+      }
+    }
+
+    public void SetDefault() {
+      ControlMode = ControlMode.Default;
+    }
+
+    public float MinSoftwareValue {
+      get {
+        return minSoftwareValue;
+      }
+    }
+
+    public float MaxSoftwareValue {
+      get {
+        return maxSoftwareValue;
+      }
+    }
+
+    public void SetSoftware(float value) {
+      ControlMode = ControlMode.Software;
+      SoftwareValue = value;
+    }
+
+    internal event ControlEventHandler ControlModeChanged;
+    internal event ControlEventHandler SoftwareControlValueChanged;
+  }
+}
diff -r 59024371cd50 -r 6dc755f1970e Hardware/Hardware.cs
--- a/Hardware/Hardware.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/Hardware.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -55,13 +55,13 @@
       get { return active.ToArray(); }
     }
 
-    protected void ActivateSensor(Sensor sensor) {
+    protected void ActivateSensor(ISensor sensor) {
       if (active.Add(sensor)) 
         if (SensorAdded != null)
           SensorAdded(sensor);
     }
 
-    protected void DeactivateSensor(Sensor sensor) {
+    protected void DeactivateSensor(ISensor sensor) {
       if (active.Remove(sensor))
         if (SensorRemoved != null)
           SensorRemoved(sensor);     
diff -r 59024371cd50 -r 6dc755f1970e Hardware/IControl.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Hardware/IControl.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -0,0 +1,61 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  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
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+namespace OpenHardwareMonitor.Hardware {
+
+  public enum ControlMode {
+    Default,
+    Software
+  }
+
+  public interface IControl {
+
+    Identifier Identifier { get; }
+
+    ControlMode ControlMode { get; }
+
+    float SoftwareValue { get; }
+
+    void SetDefault();
+
+    float MinSoftwareValue { get; }
+    float MaxSoftwareValue { get; }
+
+    void SetSoftware(float value);
+
+  }
+}
diff -r 59024371cd50 -r 6dc755f1970e Hardware/ISensor.cs
--- a/Hardware/ISensor.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/ISensor.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -87,6 +87,8 @@
     void ResetMax();
 
     IEnumerable<SensorValue> Values { get; }
+
+    IControl Control { get; }
   }
 
 }
diff -r 59024371cd50 -r 6dc755f1970e Hardware/LPC/IT87XX.cs
--- a/Hardware/LPC/IT87XX.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/LPC/IT87XX.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -82,6 +82,12 @@
       return value;
     }
 
+    private bool WriteByte(byte register, byte value) {
+      Ring0.WriteIoPort(addressReg, register);
+      Ring0.WriteIoPort(dataReg, value);
+      return register == Ring0.ReadIoPort(addressReg);
+    }
+
     public byte? ReadGPIO(int index) {
       if (index >= gpioCount)
         return null;
diff -r 59024371cd50 -r 6dc755f1970e Hardware/Sensor.cs
--- a/Hardware/Sensor.cs	Wed Dec 08 19:23:13 2010 +0000
+++ b/Hardware/Sensor.cs	Thu Jan 20 21:31:54 2011 +0000
@@ -57,6 +57,7 @@
     private readonly Queue<SensorValue> values =
       new Queue<SensorValue>(MAX_MINUTES * 15);
     private readonly ISettings settings;
+    private IControl control;
     
     private float sum;
     private int count;
@@ -186,5 +187,14 @@
       foreach (IParameter parameter in parameters)
         parameter.Accept(visitor);
     }
+
+    public IControl Control {
+      get {
+        return control;
+      }
+      internal set {
+        this.control = value;
+      }
+    }
   }
 }
diff -r 59024371cd50 -r 6dc755f1970e OpenHardwareMonitorLib.csproj
--- a/OpenHardwareMonitorLib.csproj	Wed Dec 08 19:23:13 2010 +0000
+++ b/OpenHardwareMonitorLib.csproj	Thu Jan 20 21:31:54 2011 +0000
@@ -59,6 +59,8 @@
     <Compile Include="Hardware\ATI\ADL.cs" />
     <Compile Include="Hardware\ATI\ATIGPU.cs" />
     <Compile Include="Hardware\ATI\ATIGroup.cs" />
+    <Compile Include="Hardware\Control.cs" />
+    <Compile Include="Hardware\IControl.cs" />
     <Compile Include="Hardware\IOControlCode.cs" />
     <Compile Include="Hardware\Computer.cs" />
     <Compile Include="Hardware\CPU\AMDCPU.cs" />