moel@1: /*
moel@1:   
moel@1:   Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@1: 
moel@1:   The contents of this file are subject to the Mozilla Public License Version
moel@1:   1.1 (the "License"); you may not use this file except in compliance with
moel@1:   the License. You may obtain a copy of the License at
moel@1:  
moel@1:   http://www.mozilla.org/MPL/
moel@1: 
moel@1:   Software distributed under the License is distributed on an "AS IS" basis,
moel@1:   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@1:   for the specific language governing rights and limitations under the License.
moel@1: 
moel@1:   The Original Code is the Open Hardware Monitor code.
moel@1: 
moel@1:   The Initial Developer of the Original Code is 
moel@1:   Michael Möller <m.moeller@gmx.ch>.
moel@1:   Portions created by the Initial Developer are Copyright (C) 2009-2010
moel@1:   the Initial Developer. All Rights Reserved.
moel@1: 
paulwerelds@204:   Contributor(s): Paul Werelds
moel@1: 
moel@1:   Alternatively, the contents of this file may be used under the terms of
moel@1:   either the GNU General Public License Version 2 or later (the "GPL"), or
moel@1:   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@1:   in which case the provisions of the GPL or the LGPL are applicable instead
moel@1:   of those above. If you wish to allow use of your version of this file only
moel@1:   under the terms of either the GPL or the LGPL, and not to allow others to
moel@1:   use your version of this file under the terms of the MPL, indicate your
moel@1:   decision by deleting the provisions above and replace them with the notice
moel@1:   and other provisions required by the GPL or the LGPL. If you do not delete
moel@1:   the provisions above, a recipient may use your version of this file under
moel@1:   the terms of any one of the MPL, the GPL or the LGPL.
moel@1:  
moel@1: */
moel@1: 
moel@1: using System;
moel@1: using System.Collections.Generic;
moel@205: using System.Globalization;
paulwerelds@204: using System.Text;
moel@1: 
moel@1: namespace OpenHardwareMonitor.Hardware.HDD {
moel@165:   internal class HDDGroup : IGroup {
moel@1: 
moel@1:     private const int MAX_DRIVES = 32;
moel@1: 
moel@195:     private readonly List<HDD> hardware = new List<HDD>();
moel@1: 
moel@165:     public HDDGroup(ISettings settings) {
paulwerelds@204:       int p = (int)Environment.OSVersion.Platform;
paulwerelds@204:       if (p == 4 || p == 128) return;
moel@1: 
paulwerelds@204:       for (int drive = 0; drive < MAX_DRIVES; drive++) {
paulwerelds@204:         IntPtr handle = SMART.OpenPhysicalDrive(drive);
moel@1: 
paulwerelds@204:         if (handle == SMART.INVALID_HANDLE_VALUE)
paulwerelds@204:           continue;
paulwerelds@204: 
paulwerelds@204:         if (!SMART.EnableSmart(handle, drive)) {
paulwerelds@204:           SMART.CloseHandle(handle);
paulwerelds@204:           continue;
paulwerelds@204:         }
paulwerelds@204: 
paulwerelds@204:         string name = SMART.ReadName(handle, drive);
paulwerelds@204:         if (name == null) {
paulwerelds@204:           SMART.CloseHandle(handle);
paulwerelds@204:           continue;
paulwerelds@204:         }
paulwerelds@204: 
paulwerelds@233:         SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
paulwerelds@218:         
paulwerelds@233:         if (attributes.Length < 1) {
moel@205:           SMART.CloseHandle(handle);
moel@205:           continue;
moel@205:         }
paulwerelds@204: 
moel@231:         SMART.AttributeID ssdLifeID = GetSSDLifeID(attributes);
moel@231:         if (ssdLifeID == SMART.AttributeID.None) {
paulwerelds@218:           SMART.AttributeID temperatureID = GetTemperatureIndex(attributes);
paulwerelds@204: 
moel@231:           if (temperatureID != SMART.AttributeID.None) {
moel@231:             hardware.Add(new HDD(name, handle, drive, temperatureID, 
moel@231:               SMART.AttributeID.None, settings));
paulwerelds@218:             continue;
moel@205:           }
paulwerelds@218:         } else {
moel@231:           hardware.Add(new HDD(name, handle, drive, SMART.AttributeID.None, 
moel@231:             ssdLifeID, settings));
moel@205:           continue;
paulwerelds@204:         }
paulwerelds@218:         
paulwerelds@204:         SMART.CloseHandle(handle);
moel@1:       }
moel@1:     }
moel@1: 
paulwerelds@233:     private SMART.AttributeID GetSSDLifeID(SMART.DriveAttribute[] attributes) {
paulwerelds@233:       // ID E9 is present on Intel, JM, SF and Samsung (different meanings)
paulwerelds@218:       // ID D2 is present on Indilinx
paulwerelds@218:       // Neither ID has been found on a mechanical hard drive (yet),
paulwerelds@233:       // so this seems like a good way to check if it's an SSD.
moel@231:       bool isKnownSSD = (
paulwerelds@233:         Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xE9)) ||
paulwerelds@233:         Array.Exists(attributes, attr => attr.ID == new SMART.AttributeID(0xD2))
paulwerelds@218:       );
paulwerelds@218: 
moel@231:       if (!isKnownSSD) return SMART.AttributeID.None;
paulwerelds@218: 
paulwerelds@218:       // We start with a traditional loop, because there are 4 unique ID's
paulwerelds@218:       // that potentially identify one of the vendors
paulwerelds@233:       for (int i = 0; i < attributes.Length; i++) {
moel@231:         if (attributes[i].ID == SMART.SamsungAttributes.RemainingLife)
moel@231:           return SMART.SamsungAttributes.RemainingLife;
paulwerelds@233:         
paulwerelds@233:         if (attributes[i].ID == SMART.SandForceAttributes.ProgramFailCount)
moel@231:           return  SMART.SandForceAttributes.RemainingLife;
paulwerelds@233:         
paulwerelds@233:         if (attributes[i].ID == SMART.IndilinxAttributes.UnknownUnique)   
paulwerelds@233:           return SMART.IndilinxAttributes.RemainingLife;
paulwerelds@218:       }
paulwerelds@218: 
paulwerelds@218:       // TODO: Find out JMicron's Life attribute ID; their unique ID = 0xE4
paulwerelds@218: 
paulwerelds@218:       // For Intel, we make sure we have their 3 most important ID's
paulwerelds@218:       // We do a traditional loop again, because we all we need to know
paulwerelds@218:       // is whether we can find all 3; pointless to use Exists()
paulwerelds@218:       int intelRegisterCount = 0;
paulwerelds@218:       foreach (SMART.DriveAttribute attribute in attributes) {
paulwerelds@233:         if (attribute.ID == SMART.IntelAttributes.HostWrites ||
paulwerelds@233:           attribute.ID == SMART.IntelAttributes.RemainingLife ||
paulwerelds@233:           attribute.ID == SMART.IntelAttributes.MediaWearOutIndicator
paulwerelds@218:         )
paulwerelds@218:           intelRegisterCount++;
paulwerelds@218:       }
paulwerelds@218: 
paulwerelds@218:       return (intelRegisterCount == 3)
moel@231:         ? SMART.IntelAttributes.RemainingLife
moel@231:         : SMART.AttributeID.None;
paulwerelds@218:     }
paulwerelds@218: 
paulwerelds@218:     private SMART.AttributeID GetTemperatureIndex(
paulwerelds@233:       SMART.DriveAttribute[] attributes)
paulwerelds@218:     {
paulwerelds@218:       SMART.AttributeID[] validIds = new[] {
moel@231:         SMART.CommonAttributes.Temperature,
moel@231:         SMART.CommonAttributes.DriveTemperature,
moel@231:         SMART.CommonAttributes.AirflowTemperature
paulwerelds@218:       };
paulwerelds@218: 
paulwerelds@218:       foreach (SMART.AttributeID validId in validIds) {
paulwerelds@218:         SMART.AttributeID id = validId;
paulwerelds@233:         if (Array.Exists(attributes, attr => attr.ID == id))
paulwerelds@218:           return validId;
paulwerelds@218:       }
paulwerelds@218: 
moel@231:       return SMART.AttributeID.None;
paulwerelds@218:     }
paulwerelds@218: 
moel@1:     public IHardware[] Hardware {
moel@1:       get {
moel@1:         return hardware.ToArray();
moel@1:       }
moel@1:     }
moel@1: 
moel@1:     public string GetReport() {
paulwerelds@204:       int p = (int)Environment.OSVersion.Platform;
paulwerelds@204:       if (p == 4 || p == 128) return null;
paulwerelds@204: 
paulwerelds@204:       StringBuilder r = new StringBuilder();
paulwerelds@204: 
paulwerelds@204:       r.AppendLine("S.M.A.R.T Data");
paulwerelds@204:       r.AppendLine();
paulwerelds@204: 
paulwerelds@204:       for (int drive = 0; drive < MAX_DRIVES; drive++) {
paulwerelds@204:         IntPtr handle = SMART.OpenPhysicalDrive(drive);
paulwerelds@204: 
paulwerelds@204:         if (handle == SMART.INVALID_HANDLE_VALUE)
paulwerelds@204:           continue;
paulwerelds@204: 
paulwerelds@204:         if (!SMART.EnableSmart(handle, drive)) {
paulwerelds@204:           SMART.CloseHandle(handle);
paulwerelds@204:           continue;
paulwerelds@204:         }
paulwerelds@204: 
paulwerelds@204:         string name = SMART.ReadName(handle, drive);
paulwerelds@204:         if (name == null) {
paulwerelds@204:           SMART.CloseHandle(handle);
paulwerelds@204:           continue;
paulwerelds@204:         }
paulwerelds@204: 
paulwerelds@233:         SMART.DriveAttribute[] attributes = SMART.ReadSmart(handle, drive);
paulwerelds@204: 
paulwerelds@233:         if (attributes.Length > 0) {
paulwerelds@204:           r.AppendLine("Drive name: " + name);
paulwerelds@204:           r.AppendLine();
moel@250:           r.AppendFormat(CultureInfo.InvariantCulture, " {0}{1}{2}{3}{4}",
moel@205:             ("ID").PadRight(6),
moel@205:             ("RawValue").PadRight(20),
moel@205:             ("WorstValue").PadRight(12),
moel@205:             ("AttrValue").PadRight(12),
moel@205:             Environment.NewLine);
paulwerelds@204: 
moel@205:           foreach (SMART.DriveAttribute a in attributes) {
moel@231:             if (a.ID == SMART.AttributeID.None) continue;
moel@205:             string raw = BitConverter.ToString(a.RawValue);
moel@250:             r.AppendFormat(CultureInfo.InvariantCulture, " {0}{1}{2}{3}{4}",
moel@205:               a.ID.ToString("d").PadRight(6), 
moel@205:               raw.Replace("-", " ").PadRight(20),
moel@205:               a.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(12),
moel@205:               a.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(12),
moel@205:               Environment.NewLine);
paulwerelds@204:           }
paulwerelds@204:           r.AppendLine();
paulwerelds@204:         }
paulwerelds@204: 
paulwerelds@204:         SMART.CloseHandle(handle);
paulwerelds@204:       }
paulwerelds@204: 
paulwerelds@204:       return r.ToString();
moel@1:     }
moel@1: 
moel@1:     public void Close() {
moel@1:       foreach (HDD hdd in hardware) 
moel@1:         hdd.Close();
moel@1:     }
moel@1:   }
moel@1: }