Skip to content
Snippets Groups Projects
Select Git revision
  • dd9056243cab893518820102ea204409111f3635
  • trunk
  • RELEASE_6_5_DRIVEDB
  • RELEASE_6_6_DRIVEDB
  • RELEASE_7_0_DRIVEDB
  • RELEASE_7_2_DRIVEDB
  • RELEASE_7_3_DRIVEDB
  • RELEASE_6_0_DRIVEDB
  • RELEASE_6_1_DRIVEDB
  • RELEASE_6_2_DRIVEDB
  • RELEASE_6_3_DRIVEDB
  • RELEASE_6_4_DRIVEDB
  • tags/RELEASE_7_4
  • tags/RELEASE_7_3
  • RELEASE_5_41_DRIVEDB
  • RELEASE_5_42_DRIVEDB
  • RELEASE_5_43_DRIVEDB
  • tags/RELEASE_7_2
  • tags/RELEASE_7_1
  • tags/RELEASE_7_0
  • RELEASE_5_40_DRIVEDB
21 results

ataprint.c

Blame
  • ataprint.c 29.90 KiB
    /*
     * ataprint.c
     *
     * Home page of code is: http://smartmontools.sourceforge.net
     *
     * Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
     * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2, or (at your option)
     * any later version.
     *
     * You should have received a copy of the GNU General Public License
     * (for example COPYING); if not, write to the Free
     * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     *
     * This code was originally developed as a Senior Thesis by Michael Cornwell
     * at the Concurrent Systems Laboratory (now part of the Storage Systems
     * Research Center), Jack Baskin School of Engineering, University of
     * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
     *
     */
    
    #include <ctype.h>
    #include <stdio.h>
    #include "ataprint.h"
    #include "smartctl.h"
    #include "extern.h"
    
    const char *CVSid4="$Id: ataprint.c,v 1.33 2002/10/24 09:54:02 ballen4705 Exp $"
    CVSID2 CVSID3 CVSID6;
    
    // Function for printing ASCII byte-swapped strings, skipping white
    // space. This is needed on little-endian architectures, eg Intel,
    // Alpha. If someone wants to run this on SPARC they'll need to test
    // for the Endian-ness and skip the byte swapping if it's big-endian.
    void printswap(char *in, unsigned int n){
      unsigned int i;
      char out[64];
    
      // swap bytes
      for (i=0;i<n;i+=2){
        unsigned int j=i+1;
        out[i]=in[j];
        out[j]=in[i];
      }
    
      // find the end of the white space
      for (i=0;i<n && isspace(out[i]);i++);
    
      // and do the printing starting from first non-white space
      if (n-i)
        pout("%.*s\n",(int)(n-i),out+i);
      else
        pout("[No Information Found]\n");
    
      return;
    }
    
    
    void ataPrintDriveInfo (struct hd_driveid drive){
      int version;
      const char *description;
      char unknown[64];
    
      // print out model, serial # and firmware versions  (byte-swap ASCI strings)
      pout("Device Model:     ");
      printswap(drive.model,40);
    
      pout("Serial Number:    ");
      printswap(drive.serial_no,20);
    
      pout("Firmware Version: ");
      printswap(drive.fw_rev,8);
    
      // now get ATA version info
      version=ataVersionInfo(&description,drive);
    
      // unrecognized minor revision code
      if (!description){
        sprintf(unknown,"Unrecognized. Minor revision code: 0x%02x",drive.minor_rev_num);
        description=unknown;
      }
      
      
      // SMART Support was first added into the ATA/ATAPI-3 Standard with
      // Revision 3 of the document, July 25, 1995.  Look at the "Document
      // Status" revision commands at the beginning of
      // http://www.t13.org/project/d2008r6.pdf to see this.  So it's not
      // enough to check if we are ATA-3.  Version=-3 indicates ATA-3
      // BEFORE Revision 3.
      pout("ATA Version is:   %i\n",version>0?version:-1*version);
      pout("ATA Standard is:  %s\n",description);
      
      if (version>=3)
        return;
      
      pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n");
      pout("We will try to proceed in spite of this.\n");
      return;
    }
    
    
    /* void PrintSmartOfflineStatus ( struct ata_smart_values data) 
       prints verbose value Off-line data collection status byte */
    
    void PrintSmartOfflineStatus ( struct ata_smart_values data)
    {
       pout ("Off-line data collection status: ");	
       
       switch (data.offline_data_collection_status){
       case 0x00:
       case 0x80:
         pout ("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t",
    	     data.offline_data_collection_status);
         pout("never started.\n");
         break;
       case 0x01:
       case 0x81:
         pout ("(0x%02x)\tReserved.\n",
    	     data.offline_data_collection_status);
         break;
       case 0x02:
       case 0x82:
         pout ("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t",
    	     data.offline_data_collection_status);
         pout ("completed without error.\n");
         break;
       case 0x03:
       case 0x83:
         pout ("(0x%02x)\tReserved.\n",
    	     data.offline_data_collection_status);
         break;
       case 0x04:
       case 0x84:
         pout ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
    	     data.offline_data_collection_status);
         pout ("suspended by an interrupting command from host.\n");
         break;
       case 0x05:
       case 0x85:
         pout ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
    	     data.offline_data_collection_status);
         pout ("aborted by an interrupting command from host.\n");
         break;
       case 0x06:
       case 0x86:
         pout ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
    	     data.offline_data_collection_status);
         pout ("aborted by the device with a fatal error.\n");
         break;
       default:
         if ( ((data.offline_data_collection_status >= 0x07) &&
    	   (data.offline_data_collection_status <= 0x3f)) ||
    	  ((data.offline_data_collection_status >= 0xc0) &&
    	   (data.offline_data_collection_status <= 0xff)) )
           {
    	 pout ("(0x%02x)\tVendor Specific.\n",
    		 data.offline_data_collection_status);
           } 
         else 
           {
    	 pout ("(0x%02x)\tReserved.\n",
    		 data.offline_data_collection_status);
           }
       }
    }
    
    
    
    void PrintSmartSelfExecStatus ( struct ata_smart_values data)
    {
       pout ("Self-test execution status:      ");
       
       switch (data.self_test_exec_status >> 4)
       {
          case 0:
            pout ("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
                    data.self_test_exec_status);
            pout ("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
            break;
           case 1:
             pout ("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
                     data.self_test_exec_status);
             pout ("the host.\n");
             break;
           case 2:
             pout ("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
                     data.self_test_exec_status);
             pout ("by the host with a hard or soft reset.\n");
             break;
           case 3:
              pout ("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("occurred while the device was executing\n\t\t\t\t\t");
              pout ("its self-test routine and the device \n\t\t\t\t\t");
              pout ("was unable to complete the self-test \n\t\t\t\t\t");
              pout ("routine.\n");
              break;
           case 4:
              pout ("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("a test element that failed and the test\n\t\t\t\t\t");
              pout ("element that failed is not known.\n");
              break;
           case 5:
              pout ("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("the electrical element of the test\n\t\t\t\t\t");
              pout ("failed.\n");
              break;
           case 6:
              pout ("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("the servo (and/or seek) element of the \n\t\t\t\t\t");
              pout ("test failed.\n");
              break;
           case 7:
              pout ("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("the read element of the test failed.\n");
              break;
           case 15:
              pout ("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
                      data.self_test_exec_status);
              pout ("%1d0%% of test remaining.\n", 
                      data.self_test_exec_status & 0x0f);
              break;
           default:
              pout ("(%4d)\tReserved.\n",
                      data.self_test_exec_status);
              break;
       }
    	
    }
    
    
    
    void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values data)
    {
       pout ("Total time to complete off-line \n");
       pout ("data collection: \t\t (%4d) seconds.\n", 
               data.total_time_to_complete_off_line);
    }
    
    
    
    void PrintSmartOfflineCollectCap ( struct ata_smart_values data)
    {
       pout ("Offline data collection\n");
       pout ("capabilities: \t\t\t (0x%02x) ",
                data.offline_data_collection_capability);
    
       if (data.offline_data_collection_capability == 0x00)
       {
          pout ("\tOff-line data collection not supported.\n");
       } 
       else 
       {
          pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
                  "SMART execute Offline immediate." :
                  "No SMART execute Offline immediate.");
    
          pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
                  "Automatic timer ON/OFF support.":
                  "No Automatic timer ON/OFF support.");
    		
          pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
                  "Abort Offline collection upon new\n\t\t\t\t\tcommand.":
                  "Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
    
          pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
                  "Offline surface scan supported.":
                  "No Offline surface scan supported.");
    
          pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
                  "Self-test supported.":
                  "No Self-test supported.");
        }
    }
    
    
    
    void PrintSmartCapability ( struct ata_smart_values data)
    {
       pout ("SMART capabilities:            ");
       pout ("(0x%04x)\t", data.smart_capability);
       
       if (data.smart_capability == 0x00)
       {
           pout ("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
       } 
       else 
       {
    	
          pout( "%s\n", (data.smart_capability & 0x01)? 
                  "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
                  "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
    		
          if ( data.smart_capability & 0x02 )
          {
              pout ("\t\t\t\t\tSupports SMART auto save timer.\n");
          }
       }
    }
    
    
    
    void PrintSmartErrorLogCapability ( struct ata_smart_values data)
    {
    
       pout ("Error logging capability:       ");
        
       if ( isSmartErrorLogCapable(data) )
       {
          pout (" (0x%02x)\tError logging supported.\n",
                   data.errorlog_capability);
       }
       else {
           pout (" (0x%02x)\tError logging NOT supported.\n",
                    data.errorlog_capability);
       }
    }
    
    
    
    void PrintSmartShortSelfTestPollingTime ( struct ata_smart_values data)
    {
       if ( isSupportSelfTest(data) )
       {
          pout ("Short self-test routine \n");
          pout ("recommended polling time: \t (%4d) minutes.\n", 
                   data.short_test_completion_time);
    
       }
       else
       {
          pout ("Short self-test routine \n");
          pout ("recommended polling time: \t        Not Supported.\n");
       }
    }
    
    
    void PrintSmartExtendedSelfTestPollingTime ( struct ata_smart_values data)
    {
       if ( isSupportSelfTest(data) )
       {
          pout ("Extended self-test routine \n");
          pout ("recommended polling time: \t (%4d) minutes.\n", 
                   data.extend_test_completion_time);
       }
       else
       {
          pout ("Extended self-test routine \n");
          pout ("recommended polling time: \t        Not Supported.\n");
       }
    }
    
    
    // onlyfailed=0 : print all attribute values
    // onlyfailed=1:  just ones that are currently failed and have prefailure bit set
    // onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
    void PrintSmartAttribWithThres (struct ata_smart_values data, 
    				struct ata_smart_thresholds thresholds,
    				int onlyfailed){
      int i,j;
      long long rawvalue;
      int needheader=1;
        
      // step through all vendor attributes
      for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
        char *status;
        struct ata_smart_attribute *disk=data.vendor_attributes+i;
        struct ata_smart_threshold_entry *thre=thresholds.thres_entries+i;
        
        // consider only valid attributes
        if (disk->id && thre->id){
          char *type;
          int failednow,failedever;
          char attributename[64];
    
          failednow =disk->current <= thre->threshold;
          failedever=disk->worst   <= thre->threshold;
          
          // These break out of the loop if we are only printing certain entries...
          if (onlyfailed==1 && (!disk->status.flag.prefailure || !failednow))
    	continue;
          
          if (onlyfailed==2 && !failedever)
    	continue;
          
          // print header only if needed
          if (needheader){
    	if (!onlyfailed){
    	  pout ("SMART Attributes Data Structure revision number: %i\n",data.revnumber);
    	  pout ("Vendor Specific SMART Attributes with Thresholds:\n");
    	}
    	pout("ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE     WHEN_FAILED RAW_VALUE\n");
    	needheader=0;
          }
          
          // is this currently failed, or has it ever failed?
          if (failednow)
    	status="FAILING_NOW";
          else if (failedever)
    	status="In_the_past";
          else
    	status="    -";
          
          // Print name of attribute
          ataPrintSmartAttribName(attributename,disk->id);
          pout("%-28s",attributename);
    
          // printing line for each valid attribute
          type=disk->status.flag.prefailure?"Pre-fail":"Old_age";
          pout("0x%04x   %.3i   %.3i   %.3i    %-9s%-12s", 
    	     disk->status.all, disk->current, disk->worst,
    	     thre->threshold, type, status);
          
          // convert the six individual bytes to a long long (8 byte) integer
          rawvalue = 0;
          for (j = 0 ; j < 6 ; j++)
    	rawvalue |= disk->raw[j] << (8*j) ;
          
          // This switch statement is where we handle Raw attributes
          // that are stored in an unusual vendor-specific format,
          switch (disk->id){
    	// Power on time
          case 9:
    	if (smart009minutes)
    	  // minutes
    	  pout ("%llu h + %2llu m\n",rawvalue/60,rawvalue%60);
    	else
    	   // hours
    	  pout ("%llu\n", rawvalue);  //stored in hours
    	break;
    	
    	// Temperature
          case 194:
    	pout ("%u", disk->raw[0]);
    	if (rawvalue==disk->raw[0])
    	  pout("\n");
    	else
    	  // The other bytes are in use. Try IBM's model
    	  pout(" (Lifetime Min/Max %u/%u)\n",disk->raw[2],
    		 disk->raw[4]);
    	break;
          default:
    	pout("%llu\n", rawvalue);
          }	    
        }
      }
      if (!needheader) pout("\n");
    }
    
    
    void ataPrintGeneralSmartValues(struct ata_smart_values data){
      pout ("General SMART Values:\n");
      
      PrintSmartOfflineStatus(data); 
      
      if (isSupportSelfTest(data)){
        PrintSmartSelfExecStatus (data);
      }
      
      PrintSmartTotalTimeCompleteOffline(data);
      PrintSmartOfflineCollectCap(data);
      PrintSmartCapability(data);
      
      PrintSmartErrorLogCapability(data);
      if (isSupportSelfTest(data)){
        PrintSmartShortSelfTestPollingTime (data);
        PrintSmartExtendedSelfTestPollingTime (data);
      }
      pout("\n");
    }
    
    // Is not (currently) used in ANY code
    void ataPrintSmartThresholds (struct ata_smart_thresholds data)
    {
       int i;
    
       pout ("SMART Thresholds\n");
       pout ("SMART Threshold Revision Number: %i\n", data.revnumber);
    	
       for ( i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
          if (data.thres_entries[i].id)	
              pout ("Attribute %3i threshold: %02x (%2i)\n", 
                       data.thres_entries[i].id, 
                       data.thres_entries[i].threshold, 
                       data.thres_entries[i].threshold);
       }
    }
    
    
    // Returns nonzero if region of memory contains non-zero entries
    int nonempty(unsigned char *testarea,int n){
      int i;
      for (i=0;i<n;i++)
        if (testarea[i])
          return 1;
      return 0;
    }
      
    void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
      int i,j,k;
      
      pout ("SMART Error Log Version: %i\n", data.revnumber);
      
      // if no errors logged, return
      if (!data.error_log_pointer){
        pout ("No Errors Logged\n\n");
        return;
      }
      QUIETON;
      // if log pointer out of range, return
      if ( data.error_log_pointer>5 ){
        pout("Invalid Error Log index = %02x (T13/1321D rev 1c"
    	 "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n",
    	 data.error_log_pointer);
        return;
      }
      
      // starting printing error log info
      if (data.ata_error_count<=5)
        pout ( "ATA Error Count: %u\n", data.ata_error_count);
      else
        pout ( "ATA Error Count: %u (device log contains only the most recent five errors)\n",
    	   data.ata_error_count);
      QUIETOFF;
      pout("\tDCR = Device Control Register\n");
      pout("\tFR  = Features Register\n");
      pout("\tSC  = Sector Count Register\n");
      pout("\tSN  = Sector Number Register\n");
      pout("\tCL  = Cylinder Low Register\n");
      pout("\tCH  = Cylinder High Register\n");
      pout("\tD/H = Device/Head Register\n");
      pout("\tCR  = Content written to Command Register\n");
      pout("\tER  = Error register\n");
      pout("\tSTA = Status register\n");
      pout("Timestamp is seconds since the previous disk power-on.\n");
      pout("Note: timestamp \"wraps\" after 2^32 msec = 49.710 days.\n\n");
      
      // now step through the five error log data structures (table 39 of spec)
      for (k = 4; k >= 0; k-- ) {
        
        // The error log data structure entries are a circular buffer
        i=(data.error_log_pointer+k)%5;
        
        // Spec says: unused error log structures shall be zero filled
        if (nonempty((unsigned char*)&(data.errorlog_struct[i]),sizeof(data.errorlog_struct[i]))){
          char *msgstate;
          switch (data.errorlog_struct[i].error_struct.state){
          case 0x00: msgstate="in an unknown state";break;
          case 0x01: msgstate="sleeping"; break;
          case 0x02: msgstate="in standby mode"; break;
          case 0x03: msgstate="active or idle"; break;
          case 0x04: msgstate="doing SMART off-line or self test"; break;
          default:   msgstate="in a vendor specific or reserved state";
          }
          // See table 42 of ATA5 spec
          QUIETON;
          pout("Error %i occurred at disk power-on lifetime: %u hours\n",
    	     5-k,data.errorlog_struct[i].error_struct.timestamp);
          QUIETOFF;
          pout("When the command that caused the error occurred, the device was %s.\n",msgstate);
          pout("After command completion occurred, registers were:\n");
          pout("ER:%02x SC:%02x SN:%02x CL:%02x CH:%02x D/H:%02x ST:%02x\n",
    	     data.errorlog_struct[i].error_struct.error_register,
    	     data.errorlog_struct[i].error_struct.sector_count,
    	     data.errorlog_struct[i].error_struct.sector_number,
    	     data.errorlog_struct[i].error_struct.cylinder_low,
    	     data.errorlog_struct[i].error_struct.cylinder_high,
    	     data.errorlog_struct[i].error_struct.drive_head,
    	     data.errorlog_struct[i].error_struct.status);
          pout("Sequence of commands leading to the command that caused the error were:\n");
          pout("DCR   FR   SC   SN   CL   CH   D/H   CR   Timestamp\n");
          for ( j = 4; j >= 0; j--){
    	struct ata_smart_errorlog_command_struct *thiscommand=&(data.errorlog_struct[i].commands[j]);
    	
    	// Spec says: unused data command structures shall be zero filled
    	if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand)))
    	  pout ( " %02x   %02x   %02x   %02x   %02x   %02x    %02x   %02x     %u.%03u\n", 
    		   thiscommand->devicecontrolreg,
    		   thiscommand->featuresreg,
    		   thiscommand->sector_count,
    		   thiscommand->sector_number,
    		   thiscommand->cylinder_low,
    		   thiscommand->cylinder_high,
    		   thiscommand->drive_head,
    		   thiscommand->commandreg,
    		   (unsigned int)(thiscommand->timestamp / 1000),
    		   (unsigned int)(thiscommand->timestamp % 1000)); 
          }
          pout("\n");
        }
      }
      QUIETON;
      if (quietmode)
        pout("\n");
      QUIETOFF;
      return;  
    }
    
    // return value is number of entries found where the self-test showed an error
    int ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data,int allentries){
      int i,j,noheaderprinted=1;
      int retval=0;
    
      if (allentries)
        pout("SMART Self-test log, version number %u\n",data.revnumber);
      if (data.revnumber!=0x01 && allentries)
        pout("Warning - structure revision number does not match spec!\n");
      if (data.mostrecenttest==0){
        if (allentries)
          pout("No self-tests have been logged\n\n");
        return 0;
      }
    
      // print log      
      for (i=20;i>=0;i--){    
        struct ata_smart_selftestlog_struct *log;
        // log is a circular buffer
        j=(i+data.mostrecenttest)%21;
        log=data.selftest_struct+j;
    
        if (nonempty((unsigned char*)log,sizeof(*log))){
          char *msgtest,*msgstat,percent[64],firstlba[64];
          int errorfound=0;
    
          // test name
          switch(log->selftestnumber){
          case   0: msgtest="Off-line           "; break;
          case   1: msgtest="Short off-line     "; break;
          case   2: msgtest="Extended off-line  "; break;
          case 127: msgtest="Abort off-line test"; break;
          case 129: msgtest="Short captive      "; break;
          case 130: msgtest="Extended captive   "; break;
          default:  msgtest="Unknown test       ";
          }
          
          // test status
          switch((log->selfteststatus)>>4){
          case  0:msgstat="Completed                    "; break;
          case  1:msgstat="Aborted by host              "; break;
          case  2:msgstat="Interrupted (host reset)     "; break;
          case  3:msgstat="Fatal or unknown error       "; errorfound=1; break;
          case  4:msgstat="Completed: unknown failure   "; errorfound=1; break;
          case  5:msgstat="Completed: electrical failure"; errorfound=1; break;
          case  6:msgstat="Completed: servo/seek failure"; errorfound=1; break;
          case  7:msgstat="Completed: read failure      "; errorfound=1; break;
          case 15:msgstat="Test in progress             "; break;
          default:msgstat="Unknown test status          ";
          }
          
          retval+=errorfound;
    
          sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
          if (log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000)
    	sprintf(firstlba,"%s","");
          else	
    	sprintf(firstlba,"0x%08x",log->lbafirstfailure);
    
          if (noheaderprinted && (allentries || errorfound)){
    	pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
    	noheaderprinted=0;
          }
          
          if (allentries || errorfound)
    	pout("#%2d  %s %s %s  %8u         %s\n",21-i,msgtest,msgstat,
    	     percent,log->timestamp,firstlba);
        }
      }
      if (!allentries && retval)
        pout("\n");
      return retval;
    }
    
    void ataPseudoCheckSmart ( struct ata_smart_values data, 
                               struct ata_smart_thresholds thresholds) {
      int i;
      int failed = 0;
      for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
        if (data.vendor_attributes[i].id &&   
    	thresholds.thres_entries[i].id &&
    	data.vendor_attributes[i].status.flag.prefailure &&
    	(data.vendor_attributes[i].current <= thresholds.thres_entries[i].threshold) &&
    	(thresholds.thres_entries[i].threshold != 0xFE)){
          pout("Attribute ID %i Failed\n",data.vendor_attributes[i].id);
          failed = 1;
        } 
      }   
      pout("%s\n", ( failed )?
    	 "SMART overall-health self-assessment test result: FAILED!\n"
    	 "Drive failure expected in less than 24 hours. SAVE ALL DATA":
    	 "SMART overall-health self-assessment test result: PASSED");
    }
    
    
    
    // Initialize to zero just in case some SMART routines don't work
    struct hd_driveid drive;
    struct ata_smart_values smartval;
    struct ata_smart_thresholds smartthres;
    struct ata_smart_errorlog smarterror;
    struct ata_smart_selftestlog smartselftest;
    
    int ataPrintMain (int fd){
      int timewait,code;
      int returnval=0;
      
      // Start by getting Drive ID information.  We need this, to know if SMART is supported.
      if (ataReadHDIdentity(fd,&drive)){
        pout("Smartctl: Hard Drive Read Identity Failed\n\n");
        returnval|=FAILID;
      }
      
      // Print most drive identity information if requested
      if (driveinfo){
        pout("=== START OF INFORMATION SECTION ===\n");
        ataPrintDriveInfo(drive);
      }
      
      // now check if drive supports SMART; otherwise time to exit
      if (!ataSmartSupport(drive)){
        pout("SMART support is: Unavailable - device lacks SMART capability.\n");
        pout("                  Checking to be sure by trying SMART ENABLE command.\n");
        if (ataEnableSmart(fd)){
          pout("                  No SMART functionality found. Sorry.\n");
          return returnval|FAILSMART;
        }
        else
          pout("                  SMART appears to work.  Continuing.\n"); 
        if (!driveinfo) pout("\n");
      }
      
      // Now print remaining drive info: is SMART enabled?    
      if (driveinfo){
        pout("SMART support is: Available - device has SMART capability.\n");
        if (ataDoesSmartWork(fd))
          pout("SMART support is: Enabled\n");
        else
          pout("SMART support is: Disabled\n");
        pout("\n");
      }
    
      
      // START OF THE ENABLE/DISABLE SECTION OF THE CODE
      if (smartenable || smartdisable || 
          smartautosaveenable || smartautosavedisable || 
          smartautoofflineenable || smartautoofflinedisable)
        pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
      
      // Enable/Disable SMART commands
      if (smartenable){
        if (ataEnableSmart(fd)) {
          pout("Smartctl: SMART Enable Failed.\n\n");
          returnval|=FAILSMART;
        }
        else
          pout("SMART Enabled.\n");
      }
      
      // From here on, every command requires that SMART be enabled...
      if (!ataDoesSmartWork(fd)) {
        pout("SMART Disabled. Use option -%c to enable it.\n", SMARTENABLE );
        return returnval;
      }
      
      // Turn off SMART on device
      if (smartdisable){    
        if (ataDisableSmart(fd)) {
          pout( "Smartctl: SMART Disable Failed.\n\n");
          returnval|=FAILSMART;
        }
        pout("SMART Disabled. Use option -%c to enable it.\n",SMARTENABLE);
        return returnval;		
      }
      
      // Let's ALWAYS issue this command to get the SMART status
      code=ataSmartStatus2(fd);
      if (code==-1)
        returnval|=FAILSMART;
      
      // Enable/Disable Auto-save attributes
      if (smartautosaveenable){
        if (ataEnableAutoSave(fd)){
          pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
          returnval|=FAILSMART;
        }
        else
          pout("SMART Attribute Autosave Enabled.\n");
      }
      if (smartautosavedisable){
        if (ataDisableAutoSave(fd)){
          pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
          returnval|=FAILSMART;
        }
        else
          pout("SMART Attribute Autosave Disabled.\n");
      }
      
      // for everything else read values and thresholds are needed
      if (ataReadSmartValues(fd, &smartval)){
        pout("Smartctl: SMART Read Values failed.\n\n");
        returnval|=FAILSMART;
      }
      if (ataReadSmartThresholds(fd, &smartthres)){
        pout("Smartctl: SMART Read Thresholds failed.\n\n");
        returnval|=FAILSMART;
      }
    
      // Enable/Disable Off-line testing
      if (smartautoofflineenable){
        if (!isSupportAutomaticTimer(smartval)){
          pout("Warning: device does not support SMART Automatic Timers.\n\n");
        }
        if (ataEnableAutoOffline(fd)){
          pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
          returnval|=FAILSMART;
        }
        else
          pout ("SMART Automatic Offline Testing Enabled every four hours.\n");
      }
      if (smartautoofflinedisable){
        if (!isSupportAutomaticTimer(smartval)){
          pout("Warning: device does not support SMART Automatic Timers.\n\n");
        }
        if (ataDisableAutoOffline(fd)){
          pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
          returnval|=FAILSMART;
        }
        else
          pout("SMART Automatic Offline Testing Disabled.\n");
      }
    
      // all this for a newline!
      if (smartenable || smartdisable || 
          smartautosaveenable || smartautosavedisable || 
          smartautoofflineenable || smartautoofflinedisable)
        pout("\n");
    
      // START OF READ-ONLY OPTIONS APART FROM -V and -i
      if (checksmart || generalsmartvalues || smartvendorattrib || smarterrorlog || smartselftestlog)
        pout("=== START OF READ SMART DATA SECTION ===\n");
      
      // Check SMART status (use previously returned value)
      if (checksmart){
        if (code) {
          QUIETON;
          pout("SMART overall-health self-assessment test result: FAILED!\n"
    	     "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
          QUIETOFF;
          if (ataCheckSmart(smartval, smartthres,1)){
    	returnval|=FAILATTR;
    	if (smartvendorattrib)
    	  pout("See vendor-specific Attribute list for failed Attributes.\n\n");
    	else {
    	  QUIETON;
    	  pout("Failed Attributes:\n");
    	  PrintSmartAttribWithThres(smartval, smartthres,1);
    	}
          }
          else
    	pout("No failed Attributes found.\n\n");   
          returnval|=FAILSTATUS;
          QUIETOFF;
        }
        else {
          pout("SMART overall-health self-assessment test result: PASSED\n");
          if (ataCheckSmart(smartval, smartthres,0)){
    	if (smartvendorattrib)
    	  pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
    	else {
    	  QUIETON;
    	  pout("Please note the following marginal attributes:\n");
    	  PrintSmartAttribWithThres(smartval, smartthres,2);
    	} 
    	returnval|=FAILAGE;
          }
          else
    	pout("\n");
        }
        QUIETOFF;
      }
      
      // Print general SMART values
      if (generalsmartvalues)
        ataPrintGeneralSmartValues(smartval); 
      
      // Print vendor-specific attributes
      if (smartvendorattrib){
        QUIETON;
        PrintSmartAttribWithThres(smartval, smartthres,quietmode?2:0);
        QUIETOFF;
      }
      
      // Print SMART error log
      if (smarterrorlog){
        if (!isSmartErrorLogCapable(smartval))
          pout("Warning: device does not support Error Logging\n");
        if (ataReadErrorLog(fd, &smarterror)){
          pout("Smartctl: SMART Errorlog Read Failed\n");
          returnval|=FAILSMART;
        }
        else {
          // turn on quiet mode inside this
          ataPrintSmartErrorlog(smarterror);
          QUIETOFF;
        }
      }
      
      // Print SMART self-test log
      if (smartselftestlog){
        if (!isSmartErrorLogCapable(smartval))
          pout("Warning: device does not support Self Test Logging\n");
        else {
          if(ataReadSelfTestLog(fd, &smartselftest)){
    	pout("Smartctl: SMART Self Test Log Read Failed\n");
    	returnval|=FAILSMART;
          }
          else {
    	QUIETON;
    	if (ataPrintSmartSelfTestlog(smartselftest,!quietmode))
    	  returnval|=FAILLOG;
    	QUIETOFF;
    	pout("\n");
          }
        } 
      }
      
      // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
      if (testcase==-1)
        return returnval;
      
      pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
      // if doing a self-test, be sure it's supported by the hardware
      if (testcase==OFFLINE_FULL_SCAN &&  !isSupportExecuteOfflineImmediate(smartval))
        pout("Warning: device does not support Execute Off-Line Immediate function.\n\n");
      else if (!isSupportSelfTest(smartval))
        pout ("Warning: device does not support Self-Test functions.\n\n");
      
      // Now do the test
      if (ataSmartTest(fd, testcase))
        return returnval|=FAILSMART;
      
      // Tell user how long test will take to complete  
      if ((timewait=TestTime(smartval,testcase))){ 
        pout ("Please wait %d %s for test to complete.\n",
    	    timewait, testcase==OFFLINE_FULL_SCAN?"seconds":"minutes");
        
        if (testcase!=SHORT_CAPTIVE_SELF_TEST && testcase!=EXTEND_CAPTIVE_SELF_TEST)
          pout ("Use smartctl -%c to abort test.\n", SMARTSELFTESTABORT);	
      }    
      return returnval;
    }