Search code examples
delphiata

Where can I find the SMART Threshold values of a HDD?


I am writing a SMART monitor tool and I managed to get SMART attributes [Current, Worst, Raw Data] with SMART_REA_DATA command, but I need the thresholds too, and I searched the ATA documentation but I have not found anything about how to read the thresholds. Does anyone know from where can I get those values ?


Solution

  • I found the answer :) It was simple. To get the threshold values you must use 0xD1 in Feature register and configure the rest as for 0xD0 (SMART_READ_DATA) command. I was not finding it because 0xD1 it's an obsolete feature... but it seems it's still used even in new hard drives.

    The structure for results can be the like this:

      SMART_ATTRIBUTE = packed record
       ID: Byte;
       case byte of
        0: (Flags: Word;
            CurrValue: Byte;
            WorstValue: Byte;
            RawData: array [0..5] of Byte;
            Reserved0: Byte;);
        1: (Threshold: Byte;
            Reserved1: array [0..9] of Byte;);
      end;
    
      SMART_DATA = packed record
       Version: Word;
       Attribute: array [0..29] of SMART_ATTRIBUTE;
      end;
    

    And the function for retrieving attributes an thresholds:

    function GetSMARTData(Drive:Byte; Feature:Byte; var SMARTData:SMART_DATA):Boolean;
    var hDrive: THandle;
        atpexb: ATA_PASS_THROUGH_EX_WITH_BUFFERS;
        bytesRet: DWord;
    begin
     //Result:=false;
     hDrive:=CreateFile(PWideChar('\\.\PhysicalDrive'+IntToStr(Drive)), GENERIC_READ or GENERIC_WRITE,
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
     if hDrive = INVALID_HANDLE_VALUE then RaiseLastOSError;
     try
      bytesRet:=0;
      FillChar(atpexb,SizeOf(atpexb),0);
      atpexb.Length:=SizeOf(ATA_PASS_THROUGH_EX);
      atpexb.AtaFlags:=ATA_FLAGS_DATA_IN;
      atpexb.DataTransferLength:=512;
      atpexb.TimeOutValue:=1;
      atpexb.DataBufferOffset:=SizeOf(ATA_PASS_THROUGH_EX);
      atpexb.CurrentTaskFile.Command:=WIN_SMART;
      atpexb.CurrentTaskFile.Features:=Feature;
      atpexb.CurrentTaskFile.LBA_Mid:=$4F;
      atpexb.CurrentTaskFile.LBA_High:=$C2;
    
      if not DeviceIoControl(hDrive, IOCTL_ATA_PASS_THROUGH, @atpexb, SizeOf(ATA_PASS_THROUGH_EX),
       @atpexb, SizeOf(atpexb), bytesRet, nil) then RaiseLastOSError;
    
      Result:=(atpexb.CurrentTaskFile.Command and 1)=0;
      if Result then Move(atpexb.DataBuff, SMARTData, SizeOf(SMART_DATA));
    
     finally
      CloseHandle(hDrive);
     end;
    end;