Skip to content
Snippets Groups Projects
Select Git revision
  • 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
20 results

atacmds.c

Blame
  • atacmds.c 27.32 KiB
    /*
     * atacmds.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>
     * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.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 <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include "atacmds.h"
    
    const char *CVSid1="$Id: atacmds.c,v 1.43 2002/11/26 12:37:12 ballen4705 Exp $" CVSID1;
    
    // These Drive Identity tables are taken from hdparm 5.2, and are also
    // given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
    // that SMART 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.
    #define NOVAL_0			0x0000
    #define NOVAL_1			0xffff
    /* word 81: minor version number */
    #define MINOR_MAX 0x1C
    const char *minor_str[] = {			/* word 81 value: */
      "Device does not report version",		/* 0x0000	*/
      "ATA-1 X3T9.2 781D prior to revision 4",	/* 0x0001	*/
      "ATA-1 published, ANSI X3.221-1994",		/* 0x0002	*/
      "ATA-1 X3T9.2 781D revision 4",		/* 0x0003	*/
      "ATA-2 published, ANSI X3.279-1996",		/* 0x0004	*/
      "ATA-2 X3T10 948D prior to revision 2k",	/* 0x0005	*/
      "ATA-3 X3T10 2008D revision 1",		/* 0x0006	*/ /* SMART NOT INCLUDED */
      "ATA-2 X3T10 948D revision 2k",		/* 0x0007	*/
      "ATA-3 X3T10 2008D revision 0",		/* 0x0008	*/ 
      "ATA-2 X3T10 948D revision 3",		/* 0x0009	*/
      "ATA-3 published, ANSI X3.298-199x",		/* 0x000a	*/
      "ATA-3 X3T10 2008D revision 6",		/* 0x000b	*/ /* 1st VERSION WITH SMART */
      "ATA-3 X3T13 2008D revision 7 and 7a",	/* 0x000c	*/
      "ATA/ATAPI-4 X3T13 1153D revision 6",		/* 0x000d	*/
      "ATA/ATAPI-4 T13 1153D revision 13",		/* 0x000e	*/
      "ATA/ATAPI-4 X3T13 1153D revision 7",		/* 0x000f	*/
      "ATA/ATAPI-4 T13 1153D revision 18",		/* 0x0010	*/
      "ATA/ATAPI-4 T13 1153D revision 15",		/* 0x0011	*/
      "ATA/ATAPI-4 published, ANSI NCITS 317-1998",	/* 0x0012	*/
      "ATA/ATAPI-5 T13 1321D revision 3",	        /* 0x0013	*/
      "ATA/ATAPI-4 T13 1153D revision 14",		/* 0x0014	*/
      "ATA/ATAPI-5 T13 1321D revision 1",		/* 0x0015	*/
      "ATA/ATAPI-5 published, ANSI NCITS 340-2000",	/* 0x0016	*/
      "ATA/ATAPI-4 T13 1153D revision 17",		/* 0x0017	*/
      "ATA/ATAPI-6 T13 1410D revision 0",		/* 0x0018	*/
      "ATA/ATAPI-6 T13 1410D revision 3a",		/* 0x0019	*/
      "Reserved",					/* 0x001a	*/
      "ATA/ATAPI-6 T13 1410D revision 2",		/* 0x001b	*/
      "ATA/ATAPI-6 T13 1410D revision 1",		/* 0x001c	*/
      "reserved"					/* 0x001d	*/
      "reserved"					/* 0x001e	*/
      "reserved"					/* 0x001f-0xfffe*/
    };
    
    // NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device
    // attribute structures were NOT completely vendor specific.  So any
    // disk that is ATA/ATAPI-4 or above can not be trusted to show the
    // vendor values in sensible format.
    
    // Negative values below are because it doesn't support SMART
    const int actual_ver[] = { 
      /* word 81 value: */
      0,		/* 0x0000	WARNING: 	*/
      1,		/* 0x0001	WARNING: 	*/
      1,		/* 0x0002	WARNING: 	*/
      1,		/* 0x0003	WARNING: 	*/
      2,		/* 0x0004	WARNING:   This array 		*/
      2,		/* 0x0005	WARNING:   corresponds 		*/
      -3, /*<== */	/* 0x0006	WARNING:   *exactly*		*/
      2,		/* 0x0007	WARNING:   to the ATA/		*/
      -3, /*<== */	/* 0x0008	WARNING:   ATAPI version	*/
      2,		/* 0x0009	WARNING:   listed in	 	*/
      3,		/* 0x000a	WARNING:   the 		 	*/
      3,		/* 0x000b	WARNING:   minor_str 		*/
      3,		/* 0x000c	WARNING:   array		*/
      4,		/* 0x000d	WARNING:   above.		*/
      4,		/* 0x000e	WARNING:  			*/
      4,		/* 0x000f	WARNING:   If you change 	*/
      4,		/* 0x0010	WARNING:   that one,      	*/
      4,		/* 0x0011	WARNING:   change this one	*/
      4,		/* 0x0012	WARNING:   too!!!        	*/
      5,		/* 0x0013	WARNING:	*/
      4,		/* 0x0014	WARNING:	*/
      5,		/* 0x0015	WARNING:	*/
      5,		/* 0x0016	WARNING:	*/
      4,		/* 0x0017	WARNING:	*/
      6,		/* 0x0018	WARNING:	*/
      6,		/* 0x0019	WARNING:	*/
      0,		/* 0x001a	WARNING:	*/
      6,		/* 0x001b	WARNING:	*/
      6,		/* 0x001c	WARNING:	*/
      0		/* 0x001d-0xfffe    		*/
    };
    
    // A replacement for perror() that sends output to our choice of
    // printing.
    void syserror(const char *message){
      const char *errormessage;
    
      // Get the correct system error message:
      if (errno<sys_nerr)
        errormessage=sys_errlist[errno];
      else
        errormessage="unrecognized system error";
    
      // Check that caller has handed a sensible string, and provide
      // appropriate output. See perrror(3) man page to understand better.
        if (message && *message)
          pout("%s: %s\n",message, errormessage);
        else
          pout("%s\n",errormessage);
    	
        return;
    }
    
    // We no longer use this function, because the IOCTL appears to return
    // only the drive identity at the time that the system was booted
    // (perhaps from the BIOS.  It doesn't correctly reflect the current
    // state information, and for example the checksum is usually
    // wrong. The replacement function follows afterwards
    #if (0)
    int ataReadHDIdentity (int device, struct hd_driveid *buf){
      if (ioctl(device, HDIO_GET_IDENTITY, buf)){ 
        perror ("Error ATA GET HD Identity Failed");
        return -1;
      }
      return 0;
    }
    #endif
    
    // Reads current Device Identity info (512 bytes) into buf
    int ataReadHDIdentity (int device, struct hd_driveid *buf){
      unsigned short driveidchecksum;
      unsigned char parms[HDIO_DRIVE_CMD_HDR_SIZE+sizeof(*buf)]=
      {WIN_IDENTIFY, 0, 0, 1,};
      
      if (ioctl(device ,HDIO_DRIVE_CMD,parms)){
        // See if device responds to packet command...
        parms[0]=WIN_PIDENTIFY;
        if (ioctl(device ,HDIO_DRIVE_CMD,parms)){
          syserror("Error ATA GET HD Identity Failed");
          return -1; 
        }
      }
      // copy data into driveid structure
      memcpy(buf,parms+HDIO_DRIVE_CMD_HDR_SIZE,sizeof(*buf));
      
    #if 0
      // The following ifdef is a HACK to distinguish different versions
      // of the header file defining hd_driveid
    #ifdef CFA_REQ_EXT_ERROR_CODE
      driveidchecksum=buf->integrity_word;
    #else
      // Note -- the declaration that appears in
      // /usr/include/linux/hdreg.h: short words160_255[95], is WRONG.
      // It should say: short words160_255[96]. I have written to Andre
      // Hedrick about this on Oct 17 2002.  Please remove this comment
      // once the fix has made it into the stock kernel tree.
      driveidchecksum=buf->words160_255[95];
    #endif
    #else
      // This way is ugly and you may feel ill -- but it always works...
      {
        unsigned short *rawstructure=
          (unsigned short *)buf;
        driveidchecksum=rawstructure[255];
      }
    #endif
      
      if ((driveidchecksum & 0x00ff) == 0x00a5){
        // Device identity structure contains a checksum
        unsigned char cksum=0;
        int i;
        
        for (i=0;i<sizeof(*buf);i++)
          cksum+=parms[i+HDIO_DRIVE_CMD_HDR_SIZE];
        
        if (cksum)
          checksumwarning("Drive Identity Structure");
      }
     
     return 0;
    }
    
    // Returns ATA version as an integer, and a pointer to a string
    // describing which revision.  Note that Revision 0 of ATA-3 does NOT
    // support SMART.  For this one case we return -3 rather than +3 as
    // the version number.  See notes above.
    int ataVersionInfo (const char** description, struct hd_driveid *drive, unsigned short *minor){
      unsigned short major;
      int i;
      
      // get major and minor ATA revision numbers
    #ifdef __NEW_HD_DRIVE_ID
      major=drive->major_rev_num;
      *minor=drive->minor_rev_num;
    #else
      major=drive->word80;
      *minor=drive->word81;
    #endif
      
      // First check if device has ANY ATA version information in it
      if (major==NOVAL_0 || major==NOVAL_1) {
        *description=NULL;
        return -1;
      }
      
      // The minor revision number has more information - try there first
      if (*minor && (*minor<=MINOR_MAX)){
        int std = actual_ver[*minor];
        if (std) {
          *description=minor_str[*minor];
          return std;
        }
      }
      
      // HDPARM has a very complicated algorithm from here on. Since SMART only
      // exists on ATA-3 and later standards, let's punt on this.  If you don't
      // like it, please fix it.  The code's in CVS.
      for (i=15; i>0; i--)
        if (major & (0x1<<i))
          break;
      
      *description=NULL; 
      if (i==0)
        return 1;
      else
        return i;;
    }
    
    // returns 1 if SMART supported, 0 if not supported or can't tell
    int ataSmartSupport(struct hd_driveid *drive){
      unsigned short word82,word83;
    
      // get correct bits of IDENTIFY DEVICE structure
    #ifdef __NEW_HD_DRIVE_ID
      word82=drive->command_set_1;
      word83=drive->command_set_2;
    #else
      word82=drive->command_sets;
      word83=drive->word83;
    #endif
    
      // Note this does not work for ATA3 < Revision 6, when word82 and word83 were added
      // we should check for ATA3 Rev 0 in minor identity code...  
      return (word83 & 0x0001<<14) && !(word83 & 0x0001<<15) && (word82 & 0x0001);
    }
    
    // returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
    int ataIsSmartEnabled(struct hd_driveid *drive){
        unsigned short word85,word87;
    
      // Get correct bits of IDENTIFY DRIVE structure
    #ifdef __NEW_HD_DRIVE_ID
      word85=drive->cfs_enable_1;
      word87=drive->csf_default;
    #else
      word85=drive->word85;
      word87=drive->word87;
    #endif
      
      if ((word87 & 0x0001<<14) && !(word87 & 0x0001<<15))
        // word85 contains valid information, so
        return word85 & 0x0001;
      
      // Since we can't rely word85, we don't know if SMART is enabled.
      return -1;
    }
    
    
    // Reads SMART attributes into *data
    int ataReadSmartValues(int device, struct ata_smart_values *data){	
      int i;
      unsigned char chksum=0;
      unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE]= 
        {WIN_SMART, 0, SMART_READ_VALUES, 1, };
      
      if (ioctl(device,HDIO_DRIVE_CMD,buf)){
        syserror("Error SMART Values Read failed");
        return -1;
      }
      
      // compute checksum
      for (i=0;i<ATA_SMART_SEC_SIZE;i++)
        chksum+=buf[i+HDIO_DRIVE_CMD_HDR_SIZE];
      
      // verify that checksum vanishes
      if (chksum)
        checksumwarning("SMART Attribute Data Structure");
      
      // copy data and return
      memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE,ATA_SMART_SEC_SIZE);
      return 0;
    }
    
    
    // Reads the Self Test Log (log #6)
    int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){	
      int i;
      unsigned char chksum=0;	
      unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
        {WIN_SMART, 0x06, SMART_READ_LOG_SECTOR, 1,};
      
      // get data from device
      if (ioctl(device, HDIO_DRIVE_CMD, buf)){
        syserror("Error SMART Error Self-Test Log Read failed");
        return -1;
      }
      
      // compute its checksum, and issue a warning if needed
      for (i=0;i<ATA_SMART_SEC_SIZE;i++)
        chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i];
      if (chksum)
        checksumwarning("SMART Self-Test Log Structure");
      
      // copy data back to the user and return
      memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); 
      return 0;
    }
    
    // Reads the Error Log (log #1)
    int ataReadErrorLog (int device, struct ata_smart_errorlog *data){	
      int i;
      unsigned char chksum=0;	
      unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
        {WIN_SMART, 0x01, SMART_READ_LOG_SECTOR, 1,};
      
      // get data from device
      if (ioctl(device,HDIO_DRIVE_CMD,buf)) {
        syserror("Error SMART Error Log Read failed");
        return -1;
      }
      
      // compute checksum and issue warning if needed
      for (i=0;i<ATA_SMART_SEC_SIZE;i++)
        chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i];
      if (chksum)
        checksumwarning("SMART ATA Error Log Structure");
      
      //copy data back to user and return
      memcpy(data, buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE);
      return 0;
    }
    
    
    int ataReadSmartThresholds (int device, struct ata_smart_thresholds *data){
      int i;
      unsigned char chksum=0;	
      unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
        {WIN_SMART, 1, SMART_READ_THRESHOLDS, 1,};
      
      // get data from device
      if (ioctl(device ,HDIO_DRIVE_CMD, buf)){
        syserror("Error SMART Thresholds Read failed");
        return -1;
      }
      
      // compute checksum and issue warning if needed
      for (i=0;i<ATA_SMART_SEC_SIZE;i++)
        chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i];
      if (chksum)
        checksumwarning("SMART Attribute Thresholds Structure");
      
      // copy data back to user and return
      memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE);
      return 0;
    }
    
    
    // This routine is not currently in use, and it's been marked as
    // "Obsolete" in the ANSI ATA-5 spec.  So it should probably be left
    // alone and unused.  If you do modify the thresholds, be sure to set
    // the checksum correctly before putting the structure back!
    int ataSetSmartThresholds ( int device, struct ata_smart_thresholds *data){	
      unsigned char buf[HDIO_DRIVE_CMD_HDR_SIZE+ATA_SMART_SEC_SIZE] = 
        {WIN_SMART, 1, 0xD7, 1,};
      
      memcpy(buf+HDIO_DRIVE_CMD_HDR_SIZE, data, ATA_SMART_SEC_SIZE);
      
      if (ioctl(device, HDIO_DRIVE_CMD, buf)){
        syserror("Error SMART Thresholds Write failed");
        return -1;
      }
      return 0;
    }
    
    int ataEnableSmart (int device ){	
      unsigned char parms[4] = {WIN_SMART, 1, SMART_ENABLE, 0};
      
      if (ioctl (device, HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Enable failed");
        return -1;
      }
      return 0;
    }
    
    int ataDisableSmart (int device ){	
      unsigned char parms[4] = {WIN_SMART, 1, SMART_DISABLE, 0};
      
      if (ioctl(device, HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Disable failed");
        return -1;
      }  
      return 0;
    }
    
    int ataEnableAutoSave(int device){
      unsigned char parms[4] = {WIN_SMART, 241, SMART_AUTOSAVE, 0};
      
      if (ioctl(device, HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Enable Auto-save failed");
        return -1;
      }
      return 0;
    }
    
    int ataDisableAutoSave(int device){
      unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTOSAVE, 0};
      
      if (ioctl(device, HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Disable Auto-save failed");
        return -1;
      }
      return 0;
    }
    
    // Note that in the ATA-5 standard the Enable/Disable AutoOffline
    // command is marked "OBSOLETE".  Curiously, I could not find it
    // documented in ANY of the ATA specifications.  In other words, it's
    // been obsolete forever. However some vendors (eg, IBM) seem to be
    // using this command anyway.  For example see the IBM Travelstar
    // 40GNX hard disk drive specifications page 164 Revision 1.1 22 Apr
    // 2002.  This gives a detailed description of the command, although
    // the drive claims to comply with the ATA/ATAPI-5 Revision 3
    // standard!  The latter document makes no mention of this command at
    // all, other than to say that it is "obsolete".
    int ataEnableAutoOffline (int device ){	
      
      /* timer hard coded to 4 hours */
      unsigned char parms[4] = {WIN_SMART, 248, SMART_AUTO_OFFLINE, 0};
      
      if (ioctl(device , HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Enable Automatic Offline failed");
        return -1;
      }
      return 0;
    }
    
    // Another Obsolete Command.  See comments directly above, associated
    // with the corresponding Enable command.
    int ataDisableAutoOffline (int device ){	
      unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTO_OFFLINE, 0};
      
      if (ioctl(device , HDIO_DRIVE_CMD, parms)){
        syserror("Error SMART Disable Automatic Offline failed");
        return -1;
      }
      return 0;
    }
    
    
    // This function does NOTHING except tell us if SMART is working &
    // enabled on the device.  See ataSmartStatus2() for one that actually
    // returns SMART status.
    int ataSmartStatus (int device ){	
       unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0};
    
       if (ioctl(device, HDIO_DRIVE_CMD, parms)){
         syserror("Error Return SMART Status via HDIO_DRIVE_CMD failed");
         return -1;
       }
       return 0;
    }
    
    // If SMART is enabled, supported, and working, then this call is
    // guaranteed to return 1, else zero.  Silent inverse of
    // ataSmartStatus()
    int ataDoesSmartWork(int device){	
       unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0};
       return !ioctl(device, HDIO_DRIVE_CMD, parms);
    }
    
    
    #ifdef HDIO_DRIVE_TASK
    // This function uses a different interface (DRIVE_TASK) than the
    // other commands in this file.
    int ataSmartStatus2(int device){
      unsigned char normal_cyl_lo=0x4f, normal_cyl_hi=0xc2;
      unsigned char failed_cyl_lo=0xf4, failed_cyl_hi=0x2c;
      
      unsigned char parms[HDIO_DRIVE_TASK_HDR_SIZE]=
        {WIN_SMART, SMART_STATUS, 0, 0, 0, 0, 0};
      
      // load CL and CH values
      parms[4]=normal_cyl_lo;
      parms[5]=normal_cyl_hi;
    
      if (ioctl(device,HDIO_DRIVE_TASK,parms)){
        syserror("Error SMART Status command via HDIO_DRIVE_TASK failed");
        return -1;
      }
      
      // Cyl low and Cyl high unchanged means "Good SMART status"
      if (parms[4]==normal_cyl_lo && parms[5]==normal_cyl_hi)
        return 0;
      
      // These values mean "Bad SMART status"
      if (parms[4]==failed_cyl_lo && parms[5]==failed_cyl_hi)
        return 1;
    
      // We haven't gotten output that makes sense; print out some debugging info
      syserror("Error SMART Status command failed");
      pout("Please get assistance from %s\n",PROJECTHOME);
      pout("Register values returned from SMART Status command are:\n");
      pout("CMD=0x%02x\n",(int)parms[0]);
      pout("FR =0x%02x\n",(int)parms[1]);
      pout("NS =0x%02x\n",(int)parms[2]);
      pout("SC =0x%02x\n",(int)parms[3]);
      pout("CL =0x%02x\n",(int)parms[4]);
      pout("CH =0x%02x\n",(int)parms[5]);
      pout("SEL=0x%02x\n",(int)parms[6]);
      return -1;
    }
    #else
    // Just a hack so that the code compiles on 
    // 2.2 kernels without HDIO_DRIVE TASK support.  
    // Should be fixed by putting in a call to code 
    // that compares smart data to thresholds.
    int ataSmartStatus2(int device){
      return ataSmartStatus(device);
    }
    #endif
    
    
    // This is the way to execute ALL tests: offline, short self-test,
    // extended self test, with and without captive mode, etc.
    int ataSmartTest(int device, int testtype){	
      unsigned char parms[4] = {WIN_SMART, 0, SMART_IMMEDIATE_OFFLINE};
      char cmdmsg[128],*type,*captive;
      int errornum;
    
      parms[1]=testtype;
    
      // Set up strings that describe the type of test
      if (testtype==SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST)
        captive="captive";
      else
        captive="off-line";
    
      if (testtype==OFFLINE_FULL_SCAN)
        type="off-line";
      else  if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST)
        type="Short self-test";
      else 
        type="Extended self-test";
    
      //  Print ouf message that we are sending the command to test
      if (testtype==ABORT_SELF_TEST)
        sprintf(cmdmsg,"Abort SMART off-line mode self-test routine");
      else
        sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive);
      pout("Sending command: \"%s\".\n",cmdmsg);
    
      // Now send the command to test
      errornum=ioctl(device, HDIO_DRIVE_CMD, parms);
      if (errornum && !((testtype=SHORT_CAPTIVE_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST) && errno==EIO)){
        char errormsg[128];
        sprintf(errormsg,"Command \"%s\" failed",cmdmsg); 
        syserror(errormsg);
        fprintf(stderr,"\n");
        return -1;
      }
      
      // Since the command succeeded, tell user
      if (testtype==ABORT_SELF_TEST)
        pout("Self-testing aborted!\n");
      else
        pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg);
      return 0;
    }
    
    /* Test Time Functions */
    int TestTime(struct ata_smart_values *data,int testtype){
      switch (testtype){
      case OFFLINE_FULL_SCAN:
        return (int) data->total_time_to_complete_off_line;
      case SHORT_SELF_TEST:
      case SHORT_CAPTIVE_SELF_TEST:
        return (int) data->short_test_completion_time;
      case EXTEND_SELF_TEST:
      case EXTEND_CAPTIVE_SELF_TEST:
        return (int) data->extend_test_completion_time;
      default:
        return 0;
      }
    }
    
    // This function tells you both about the ATA error log and the
    // self-test error log capability.  The bit is poorly documented in
    // the ATA/ATAPI standard.
    int isSmartErrorLogCapable ( struct ata_smart_values *data){
       return data->errorlog_capability & 0x01;
    }
    int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data){
       return data->offline_data_collection_capability & 0x01;
    }
    
    // Note in the ATA-5 standard, the following bit is listed as "Vendor
    // Specific".  So it may not be reliable. The only use of this that I
    // have found is in IBM drives, where it is well-documented.  See for
    // example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
    // hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
    int isSupportAutomaticTimer ( struct ata_smart_values *data){
       return data->offline_data_collection_capability & 0x02;
    }
    int isSupportOfflineAbort ( struct ata_smart_values *data){
       return data->offline_data_collection_capability & 0x04;
    }
    int isSupportOfflineSurfaceScan ( struct ata_smart_values *data){
       return data->offline_data_collection_capability & 0x08;
    }
    int isSupportSelfTest (struct ata_smart_values *data){
       return data->offline_data_collection_capability & 0x10;
    }
    
    
    // Loop over all valid attributes.  If they are prefailure attributes
    // and are at or below the threshold value, then return the ID of the
    // first failing attribute found.  Return 0 if all prefailure
    // attributes are in bounds.  The spec says "Bit 0
    // -Pre-failure/advisory - If the value of this bit equals zero, an
    // attribute value less than or equal to its corresponding attribute
    // threshold indicates an advisory condition where the usage or age of
    // the device has exceeded its intended design life period. If the
    // value of this bit equals one, an atribute value less than or equal
    // to its corresponding attribute threshold indicates a pre-failure
    // condition where imminent loss of data is being predicted."
    
    
    // onlyfailed=0 : are or were any age or prefailure attributes <= threshold
    // onlyfailed=1:  are any prefailure attributes <= threshold now
    int ataCheckSmart(struct ata_smart_values *data,
    		  struct ata_smart_thresholds *thresholds,
    		  int onlyfailed){
      int i;
      
      // loop over all attributes
      for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
    
        // pointers to disk's values and vendor's thresholds
        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){
          int failednow,failedever;
          
          failednow =disk->current <= thre->threshold;
          failedever=disk->worst   <= thre->threshold;
          
          if (!onlyfailed && failedever)
    	return disk->id;
          
          if (onlyfailed && failednow && disk->status.flag.prefailure)
    	return disk->id;      
        }
      }
      return 0;
    }
    
    
    
    // This checks the n'th attribute in the attribute list, NOT the
    // attribute with id==n.  If the attribute does not exist, or the
    // attribute is > threshold, then returns zero.  If the attribute is
    // <= threshold (failing) then we the attribute number if it is a
    // prefail attribute.  Else we return minus the attribute number if it
    // is a usage attribute.
    int ataCheckAttribute(struct ata_smart_values *data,
    		      struct ata_smart_thresholds *thresholds,
    		      int n){
      struct ata_smart_attribute *disk;
      struct ata_smart_threshold_entry *thre;
      
      if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
        return 0;
      
      // pointers to disk's values and vendor's thresholds
      disk=data->vendor_attributes+n;
      thre=thresholds->thres_entries+n;
    
      if (!disk || !thre)
        return 0;
      
      // consider only valid attributes, check for failure
      if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
        return 0;
      
      // We have found a failed attribute.  Return positive or negative? 
      if (disk->status.flag.prefailure)
        return disk->id;
      else
        return -1*(disk->id);
    }
    
    // Note some attribute names appear redundant because different
    // manufacturers use different attribute IDs for an attribute with the
    // same name.
    void ataPrintSmartAttribName(char *out, unsigned char id){
      char *name;
      switch (id){
        
      case 1:
        name="Raw_Read_Error_Rate";
        break;
      case 2:
        name="Throughput_Performance";
        break;
      case 3:
        name="Spin_Up_Time";
        break;
      case 4:
        name="Start_Stop_Count";
        break;
      case 5:
        name="Reallocated_Sector_Ct";
        break;
      case 6:
        name="Read_Channel_Margin";
        break;
      case 7:
        name="Seek_Error_Rate";
        break;
      case 8:
        name="Seek_Time_Performance";
        break;
      case 9:
        name="Power_On_Hours";
        break;
      case 10:
        name="Spin_Retry_Count";
        break;
      case 11:
        name="Calibration_Retry_Count";
        break;
      case 12:
        name="Power_Cycle_Count";
        break;
      case 13:
        name="Read_Soft_Error_Rate";
        break;
      case 191:
        name="G-Sense_Error_Rate";
        break;
      case 192:
        name="Power-Off_Retract_Count";
        break;
      case 193:
        name="Load_Cycle_Count";
        break;
      case 194:
        name="Temperature_Celsius";
        break;
      case 195:
        name="Hardware_ECC_Recovered";
        break;
      case 196:
        name="Reallocated_Event_Count";
        break;
      case 197:
        name="Current_Pending_Sector";
        break;
      case 198:
        name="Offline_Uncorrectable";
        break;
      case 199:
        name="UDMA_CRC_Error_Count";
        break;
      case 200:
        // Western Digital
        name="Multi_Zone_Error_Rate";
        break;
      case 220:
        // Note -- this is also apparently used for temperature.
        name="Disk_Shift";
        break;
      case 221:
        name="G-Sense_Error_Rate";
        break;
      case 222:
        name="Loaded_Hours";
        break;
      case 223:
        name="Load_Retry_Count";
        break;
      case 224:
        name="Load_Friction";
        break;
      case 225:
        name="Load_Cycle_Count";
        break;
      case 226:
        name="Load-in_Time";
        break;
      case 227:
        name="Torq-amp_Count";
        break;
      case 228:
        name="Power-off_Retract_Count";
        break;
      case 231:
        name="Temperature_Celsius";
        break;
      default:
        name="Unknown_Attribute";
        break;
      }
      sprintf(out,"%3hhu %s",id,name);
      return;
    }
    
    
    // These are two utility functions for printing CVS IDs. They don't
    // really belong here.  But it's the only common source file included
    // in both smartd and smartctl.  returns distance that it has moved
    // ahead in the input string
    int massagecvs(char *out, const char *cvsid){
      char *copy,*filename,*date,*version;
      const char delimiters[] = " ,$";
    
      // make a copy on stack, go to first token,
      if (!(copy=strdup(cvsid)) || !(filename=strtok(copy, delimiters))) 
        return 0;
    
      // move to first instance of "Id:"
      while (strcmp(filename,"Id:"))
        if (!(filename=strtok(NULL, delimiters)))
          return 0;
    
      // get filename, skip "v", get version and date
      if (!(  filename=strtok(NULL, delimiters)  ) ||
          !(           strtok(NULL, delimiters)  ) ||
          !(   version=strtok(NULL, delimiters)  ) ||
          !(      date=strtok(NULL, delimiters)  ) )
        return 0;
    
       sprintf(out,"%-13s revision: %-6s date: %-15s", filename, version, date);
       free(copy);
       return  (date-copy)+strlen(date);
    }
    
    // prints a single set of CVS ids
    void printone(char *block, const char *cvsid){
      char strings[CVSMAXLEN];
      const char *here=cvsid;
      int line=1,len=strlen(cvsid)+1;
    
      // check that the size of the output block is sufficient
      if (len>=CVSMAXLEN) {
        fprintf(stderr,"CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1);
        exit(1);
      }
    
      // loop through the different strings
      while ((len=massagecvs(strings,here))){
        switch (line++){
        case 1:
          block+=snprintf(block,CVSMAXLEN,"Module:");
          break;
        default:
          block+=snprintf(block,CVSMAXLEN,"  uses:");
        } 
        block+=snprintf(block,CVSMAXLEN," %s\n",strings);
        here+=len;
      }
      return;
    }