diff --git a/sm5/atacmds.c b/sm5/atacmds.c index 3e5693be88f5e9f9011c30df40947851e4406708..7189e8ecd8c5e59d2ca8e8b2071cd78bb0c9372f 100644 --- a/sm5/atacmds.c +++ b/sm5/atacmds.c @@ -1,4 +1,4 @@ -// $Id: atacmds.c,v 1.8 2002/10/15 14:24:26 ballen4705 Exp $ +// $Id: atacmds.c,v 1.9 2002/10/20 19:22:02 ballen4705 Exp $ /* * atacmds.c * @@ -27,11 +27,15 @@ #include <stdio.h> #include <string.h> +#include <syslog.h> #include "atacmds.h" - -// These Drive Identity tables are taken from hdparm 5.2. That's the -// "Gold Standard" +// 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 */ @@ -45,10 +49,10 @@ const char *minor_str[] = { /* word 81 value: */ "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ - "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ + "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ /* SMART NOT INCLUDED */ "ATA-2 X3T10 948D revision 3", /* 0x0009 */ "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ - "ATA-3 X3T10 2008D revision 6", /* 0x000b */ + "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 */ @@ -71,7 +75,7 @@ const char *minor_str[] = { /* word 81 value: */ "reserved" /* 0x001f-0xfffe*/ }; -const char actual_ver[] = { +const int actual_ver[] = { /* word 81 value: */ 0, /* 0x0000 WARNING: */ 1, /* 0x0001 WARNING: */ @@ -81,7 +85,7 @@ const char actual_ver[] = { 2, /* 0x0005 WARNING: corresponds */ 3, /* 0x0006 WARNING: *exactly* */ 2, /* 0x0007 WARNING: to the ATA/ */ - 3, /* 0x0008 WARNING: ATAPI version */ + -3, /*<== */ /* 0x0008 WARNING: ATAPI version */ 2, /* 0x0009 WARNING: listed in */ 3, /* 0x000a WARNING: the */ 3, /* 0x000b WARNING: minor_str */ @@ -106,19 +110,72 @@ const char actual_ver[] = { }; +// Used to warn users about invalid checksums. However we will not +// abort on invalid checksums. +void checksumwarning(const char *string){ + printf("Warning! %s error: invalid checksum.\n",string); + fprintf(stderr,"Warning! %s error: invalid checksum.\n",string); + syslog(LOG_INFO,"Warning! %s error: invalid checksum.\n",string); + return; +} -int ataReadHDIdentity ( int device, struct hd_driveid *buf) -{ - if (ioctl ( device , HDIO_GET_IDENTITY, buf ) != 0) - { - perror ("ATA GET HD Failed"); - return -1; - } - - return 0; +// 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 ("ATA GET HD Failed"); + return -1; + } + return 0; } +#endif + +// Reads current Device Identity info (512 bytes) into buf +int ataReadHDIdentity (int device, struct hd_driveid *buf){ + unsigned char parms[HDIO_DRIVE_CMD_HDR_SIZE+sizeof(*buf)]= + {WIN_IDENTIFY, 0, 0, 1,}; + + if (ioctl(device ,HDIO_DRIVE_CMD,parms)){ + perror ("ATA GET HD Identity Failed"); + return -1; + } + + // copy data into driveid structure + memcpy(buf,parms+HDIO_DRIVE_CMD_HDR_SIZE,sizeof(*buf)); + + // If driveid structure contains a checksum, then compute it, and + // issue a warning message if something is wrong. I prefer this + // rather than simply exiting at this point, though that is another + // option. + + // Note -- the declaration that appears in + // /usr/include/linux/hdregs.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. + if ((buf->words160_255[95] & 0x00ff) == 0x00a5){ + 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 major,minor; int i,atavalue=0; @@ -157,25 +214,55 @@ int ataVersionInfo (const char** description, struct hd_driveid drive){ return atavalue; } +// returns 1 if SMART supported, 0 if not supported or can't tell +int ataSmartSupport(struct hd_driveid drive){ + unsigned short word82,word83; -int ataSmartSupport ( struct hd_driveid drive){ + // get correct bits of IDENTIFY DEVICE structure #ifdef __NEW_HD_DRIVE_ID - if (drive.command_set_1 & 0x0001) + word82=drive.command_set_1; + word83=drive.command_set_2; #else - if (drive.command_sets & 0x0001) + word82=drive.command_sets; + word83=drive.word83; #endif - return 1; /* drive supports S.M.A.R.T. and is disabled */ - return 0; + + // 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); } -int ataReadSmartValues (int device, struct ata_smart_values *data){ +// 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)){ - perror ("Smart Values Read failed"); + perror ("SMART Values Read failed"); return -1; } @@ -184,10 +271,8 @@ int ataReadSmartValues (int device, struct ata_smart_values *data){ chksum+=buf[i+HDIO_DRIVE_CMD_HDR_SIZE]; // verify that checksum vanishes - if (chksum){ - perror ("Smart Read Failed, Checksum error!"); - return -1; - } + if (chksum) + checksumwarning("SMART Data Structure"); // copy data and return memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE,ATA_SMART_SEC_SIZE); @@ -195,293 +280,321 @@ int ataReadSmartValues (int device, struct ata_smart_values *data){ } -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}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Error Log Read failed"); - return -1; - } - - // compute checksum - for (i=0;i<ATA_SMART_SEC_SIZE;i++) - chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; - - if (chksum){ - fprintf(stderr,"Smart Self Test Log Checksum Incorrect!\n"); - return -1; - } - - 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)){ + perror ("SMART Error 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"); + + // copy data back to the user and return + memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); + return 0; } - -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}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Error Log Read failed"); - return -1; - } - - // compute checksum - for (i=0;i<ATA_SMART_SEC_SIZE;i++) - chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; - - if (chksum){ - fprintf(stderr,"Smart Error Log Checksum Incorrect!\n"); - return -1; - } - 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)) { + perror ("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 Error Log"); + + //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) -{ - unsigned char buf[ HDIO_DRIVE_CMD_HDR_SIZE + - ATA_SMART_SEC_SIZE] = - { WIN_SMART, 1, SMART_READ_THRESHOLDS, 1}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Thresholds Read failed"); - return -1; - } - - memcpy( data, &buf[HDIO_DRIVE_CMD_HDR_SIZE] , ATA_SMART_SEC_SIZE); - - return 0; +// This routine is marked as "Obsolete" in the ATA-5 spec, but it's +// very important for us. Together with the SMART READ DATA command +// above, it's the only way for us to find out if the SMART status is +// good or not. Hopefully this will get fixed -- I will find a way to +// get SMART Status directly. +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)){ + perror ("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"); + + // copy data back to user and return + memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); + return 0; } -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, (unsigned char *) &buf ) != 0) - { - perror ("Smart Thresholds Read failed"); - return -1; - } - 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. +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)){ + perror ("SMART Thresholds Read failed"); + return -1; + } + + return 0; } -int ataEnableSmart (int device ) -{ +int ataEnableSmart (int device ){ + unsigned char parms[4] = {WIN_SMART, 1, SMART_ENABLE, 0}; + + if (ioctl ( device , HDIO_DRIVE_CMD, &parms )){ + perror ("SMART Enable failed"); + return -1; + } + + return 0; +} - unsigned char parms[4] = { WIN_SMART, 1, SMART_ENABLE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms ) != 0) - { - perror ("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 ) != 0) - { - perror ("Smart Disable 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 )){ + perror ("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 ) != 0) - { - perror ("Smart Enable Auto-save failed"); - return -1; - } - - return 0; -}; + unsigned char parms[4] = {WIN_SMART, 241, SMART_AUTOSAVE, 0}; + + if (ioctl(device , HDIO_DRIVE_CMD, parms )){ + perror ("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 ) != 0) - { - perror ("Smart Disable Auto-save failed"); - return -1; - } - - return 0; -}; - -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 ) != 0) - { - perror ("Smart Enable Automatic Offline failed"); - return -1; - } - - return 0; + unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTOSAVE, 0}; + + if (ioctl(device, HDIO_DRIVE_CMD, parms)){ + perror ("SMART Disable Auto-save failed"); + return -1; + } + + return 0; } +// Note that in the ATA-5 standard this command is marked "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)){ + perror ("SMART Enable Automatic Offline failed"); + return -1; + } + + return 0; +} -int ataDisableAutoOffline (int device ) -{ - unsigned char parms[4] = { WIN_SMART, 0, SMART_AUTO_OFFLINE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms ) != 0) - { - perror ("Smart Disable Automatic Offline failed"); - return -1; - } - - return 0; +// Another Obsolete Command! +int ataDisableAutoOffline (int device ){ + unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTO_OFFLINE, 0}; + + if (ioctl(device , HDIO_DRIVE_CMD, parms)){ + perror ("SMART Disable Automatic Offline failed"); + return -1; + } + + return 0; } // Not being used correctly. Must examine the CL and CH registers to -// see what the smart status was. How to fix this? I don't know... +// see what the smart status was. Look at ataSmartStatus2() int ataSmartStatus (int device ){ - unsigned char parms[4] = { WIN_SMART, 0, SMART_STATUS, 0}; + unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0}; - if (ioctl ( device , HDIO_DRIVE_CMD, &parms)) - return -1; - return 0; -} - - - -int ataSmartTest (int device, int testtype) -{ - unsigned char parms[4] = { WIN_SMART, testtype, - SMART_IMMEDIATE_OFFLINE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms) != 0) - { - perror ("Smart Offline failed"); - return -1; + if (ioctl(device, HDIO_DRIVE_CMD, parms)){ + perror("Return SMART Status failed"); + return -1; } - - printf("Completed Off-line command\n"); - return 0; } - -int ataSmartOfflineTest (int device) -{ - return ataSmartTest( device, OFFLINE_FULL_SCAN ); +// 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); } -int ataSmartShortSelfTest (int device) -{ - return ataSmartTest( device, SHORT_SELF_TEST ); -} -int ataSmartExtendSelfTest (int device) -{ - return ataSmartTest( device, EXTEND_SELF_TEST ); -} - -int ataSmartShortCapSelfTest (int device) -{ - return ataSmartTest( device, SHORT_CAPTIVE_SELF_TEST ); +// This function needs to be properly tested and debugged. I am not +// yet sure if this is right; have asked Andre for help. May need to +// use IDE_DRIVE_TASK. Does CONFIG_IDE_TASKFILE_IO need to be +// configured into the kernel? +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, // CMD + SMART_STATUS, // FR + 0, // NS + 0, // SC + 0, // CL + 0, // CH + 0 // SEL -- Andre, is this right?? Or should it be 1? + }; + + // load CL and CH values + parms[4]=normal_cyl_lo; + parms[5]=normal_cyl_hi; + + if (ioctl(device,HDIO_DRIVE_TASK,&parms)){ + perror ("SMART Status command 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 + perror("SMART Status returned register values that don't make sense:\n"); + printf("CMD=0x%02x\n",parms[0]); + printf("FR =0x%02x\n",parms[1]); + printf("NS =0x%02x\n",parms[2]); + printf("SC =0x%02x\n",parms[3]); + printf("CL =0x%02x\n",parms[4]); + printf("CH =0x%02x\n",parms[5]); + printf("SEL=0x%02x\n",parms[6]); + + return -1; } -int ataSmartExtendCapSelfTest (int device) -{ - return ataSmartTest( device, EXTEND_CAPTIVE_SELF_TEST ); +// 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; + + 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); + printf("Sending command: \"%s\".\n",cmdmsg); + + // Now send the command to test + if (ioctl(device , HDIO_DRIVE_CMD, parms)){ + char errormsg[128]; + sprintf(errormsg,"Command \"%s\" failed.\n\n",cmdmsg); + perror (errormsg); + return -1; + } + + // Since the command succeeded, tell user + if (testtype==ABORT_SELF_TEST) + printf("Self-testing aborted!\n"); + else + printf("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg); + return 0; } -int ataSmartSelfTestAbort (int device) -{ - return ataSmartTest( device, 127 ); -} - /* Test Time Functions */ - -int isOfflineTestTime ( struct ata_smart_values data) -{ - return (int) data.total_time_to_complete_off_line; -} - - -int isShortSelfTestTime ( struct ata_smart_values data) -{ - return (int) data.short_test_completion_time; -} - - -int isExtendedSelfTestTime ( struct ata_smart_values data) -{ - return (int) data.extend_test_completion_time; +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; + } } -int isSmartErrorLogCapable ( struct ata_smart_values data) -{ +int isSmartErrorLogCapable ( struct ata_smart_values data){ return data.errorlog_capability & 0x01; } - - -int isSupportExecuteOfflineImmediate ( struct ata_smart_values data) -{ +int isSupportExecuteOfflineImmediate ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x01; } - - -int isSupportAutomaticTimer ( struct ata_smart_values data) -{ +int isSupportAutomaticTimer ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x02; } - - -int isSupportOfflineAbort ( struct ata_smart_values data) -{ +int isSupportOfflineAbort ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x04; } - - -int isSupportOfflineSurfaceScan ( struct ata_smart_values data) -{ +int isSupportOfflineSurfaceScan ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x08; } - - -int isSupportSelfTest (struct ata_smart_values data) -{ +int isSupportSelfTest (struct ata_smart_values data){ return data.offline_data_collection_capability & 0x10; } diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp index 16f5cf9bfb70736cbf7e49a31da1da1dc4180ea5..45e9a820dda258e1df780553c72f07c77c34724b 100644 --- a/sm5/atacmds.cpp +++ b/sm5/atacmds.cpp @@ -1,4 +1,4 @@ -// $Id: atacmds.cpp,v 1.8 2002/10/15 14:24:26 ballen4705 Exp $ +// $Id: atacmds.cpp,v 1.9 2002/10/20 19:22:02 ballen4705 Exp $ /* * atacmds.c * @@ -27,11 +27,15 @@ #include <stdio.h> #include <string.h> +#include <syslog.h> #include "atacmds.h" - -// These Drive Identity tables are taken from hdparm 5.2. That's the -// "Gold Standard" +// 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 */ @@ -45,10 +49,10 @@ const char *minor_str[] = { /* word 81 value: */ "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ - "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ + "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ /* SMART NOT INCLUDED */ "ATA-2 X3T10 948D revision 3", /* 0x0009 */ "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ - "ATA-3 X3T10 2008D revision 6", /* 0x000b */ + "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 */ @@ -71,7 +75,7 @@ const char *minor_str[] = { /* word 81 value: */ "reserved" /* 0x001f-0xfffe*/ }; -const char actual_ver[] = { +const int actual_ver[] = { /* word 81 value: */ 0, /* 0x0000 WARNING: */ 1, /* 0x0001 WARNING: */ @@ -81,7 +85,7 @@ const char actual_ver[] = { 2, /* 0x0005 WARNING: corresponds */ 3, /* 0x0006 WARNING: *exactly* */ 2, /* 0x0007 WARNING: to the ATA/ */ - 3, /* 0x0008 WARNING: ATAPI version */ + -3, /*<== */ /* 0x0008 WARNING: ATAPI version */ 2, /* 0x0009 WARNING: listed in */ 3, /* 0x000a WARNING: the */ 3, /* 0x000b WARNING: minor_str */ @@ -106,19 +110,72 @@ const char actual_ver[] = { }; +// Used to warn users about invalid checksums. However we will not +// abort on invalid checksums. +void checksumwarning(const char *string){ + printf("Warning! %s error: invalid checksum.\n",string); + fprintf(stderr,"Warning! %s error: invalid checksum.\n",string); + syslog(LOG_INFO,"Warning! %s error: invalid checksum.\n",string); + return; +} -int ataReadHDIdentity ( int device, struct hd_driveid *buf) -{ - if (ioctl ( device , HDIO_GET_IDENTITY, buf ) != 0) - { - perror ("ATA GET HD Failed"); - return -1; - } - - return 0; +// 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 ("ATA GET HD Failed"); + return -1; + } + return 0; } +#endif + +// Reads current Device Identity info (512 bytes) into buf +int ataReadHDIdentity (int device, struct hd_driveid *buf){ + unsigned char parms[HDIO_DRIVE_CMD_HDR_SIZE+sizeof(*buf)]= + {WIN_IDENTIFY, 0, 0, 1,}; + + if (ioctl(device ,HDIO_DRIVE_CMD,parms)){ + perror ("ATA GET HD Identity Failed"); + return -1; + } + + // copy data into driveid structure + memcpy(buf,parms+HDIO_DRIVE_CMD_HDR_SIZE,sizeof(*buf)); + + // If driveid structure contains a checksum, then compute it, and + // issue a warning message if something is wrong. I prefer this + // rather than simply exiting at this point, though that is another + // option. + + // Note -- the declaration that appears in + // /usr/include/linux/hdregs.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. + if ((buf->words160_255[95] & 0x00ff) == 0x00a5){ + 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 major,minor; int i,atavalue=0; @@ -157,25 +214,55 @@ int ataVersionInfo (const char** description, struct hd_driveid drive){ return atavalue; } +// returns 1 if SMART supported, 0 if not supported or can't tell +int ataSmartSupport(struct hd_driveid drive){ + unsigned short word82,word83; -int ataSmartSupport ( struct hd_driveid drive){ + // get correct bits of IDENTIFY DEVICE structure #ifdef __NEW_HD_DRIVE_ID - if (drive.command_set_1 & 0x0001) + word82=drive.command_set_1; + word83=drive.command_set_2; #else - if (drive.command_sets & 0x0001) + word82=drive.command_sets; + word83=drive.word83; #endif - return 1; /* drive supports S.M.A.R.T. and is disabled */ - return 0; + + // 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); } -int ataReadSmartValues (int device, struct ata_smart_values *data){ +// 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)){ - perror ("Smart Values Read failed"); + perror ("SMART Values Read failed"); return -1; } @@ -184,10 +271,8 @@ int ataReadSmartValues (int device, struct ata_smart_values *data){ chksum+=buf[i+HDIO_DRIVE_CMD_HDR_SIZE]; // verify that checksum vanishes - if (chksum){ - perror ("Smart Read Failed, Checksum error!"); - return -1; - } + if (chksum) + checksumwarning("SMART Data Structure"); // copy data and return memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE,ATA_SMART_SEC_SIZE); @@ -195,293 +280,321 @@ int ataReadSmartValues (int device, struct ata_smart_values *data){ } -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}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Error Log Read failed"); - return -1; - } - - // compute checksum - for (i=0;i<ATA_SMART_SEC_SIZE;i++) - chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; - - if (chksum){ - fprintf(stderr,"Smart Self Test Log Checksum Incorrect!\n"); - return -1; - } - - 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)){ + perror ("SMART Error 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"); + + // copy data back to the user and return + memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); + return 0; } - -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}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Error Log Read failed"); - return -1; - } - - // compute checksum - for (i=0;i<ATA_SMART_SEC_SIZE;i++) - chksum+=buf[HDIO_DRIVE_CMD_HDR_SIZE+i]; - - if (chksum){ - fprintf(stderr,"Smart Error Log Checksum Incorrect!\n"); - return -1; - } - 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)) { + perror ("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 Error Log"); + + //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) -{ - unsigned char buf[ HDIO_DRIVE_CMD_HDR_SIZE + - ATA_SMART_SEC_SIZE] = - { WIN_SMART, 1, SMART_READ_THRESHOLDS, 1}; - - if (ioctl ( device , HDIO_DRIVE_CMD, (unsigned char *) &buf ) != 0) - { - perror ("Smart Thresholds Read failed"); - return -1; - } - - memcpy( data, &buf[HDIO_DRIVE_CMD_HDR_SIZE] , ATA_SMART_SEC_SIZE); - - return 0; +// This routine is marked as "Obsolete" in the ATA-5 spec, but it's +// very important for us. Together with the SMART READ DATA command +// above, it's the only way for us to find out if the SMART status is +// good or not. Hopefully this will get fixed -- I will find a way to +// get SMART Status directly. +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)){ + perror ("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"); + + // copy data back to user and return + memcpy(data,buf+HDIO_DRIVE_CMD_HDR_SIZE, ATA_SMART_SEC_SIZE); + return 0; } -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, (unsigned char *) &buf ) != 0) - { - perror ("Smart Thresholds Read failed"); - return -1; - } - 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. +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)){ + perror ("SMART Thresholds Read failed"); + return -1; + } + + return 0; } -int ataEnableSmart (int device ) -{ +int ataEnableSmart (int device ){ + unsigned char parms[4] = {WIN_SMART, 1, SMART_ENABLE, 0}; + + if (ioctl ( device , HDIO_DRIVE_CMD, &parms )){ + perror ("SMART Enable failed"); + return -1; + } + + return 0; +} - unsigned char parms[4] = { WIN_SMART, 1, SMART_ENABLE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms ) != 0) - { - perror ("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 ) != 0) - { - perror ("Smart Disable 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 )){ + perror ("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 ) != 0) - { - perror ("Smart Enable Auto-save failed"); - return -1; - } - - return 0; -}; + unsigned char parms[4] = {WIN_SMART, 241, SMART_AUTOSAVE, 0}; + + if (ioctl(device , HDIO_DRIVE_CMD, parms )){ + perror ("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 ) != 0) - { - perror ("Smart Disable Auto-save failed"); - return -1; - } - - return 0; -}; - -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 ) != 0) - { - perror ("Smart Enable Automatic Offline failed"); - return -1; - } - - return 0; + unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTOSAVE, 0}; + + if (ioctl(device, HDIO_DRIVE_CMD, parms)){ + perror ("SMART Disable Auto-save failed"); + return -1; + } + + return 0; } +// Note that in the ATA-5 standard this command is marked "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)){ + perror ("SMART Enable Automatic Offline failed"); + return -1; + } + + return 0; +} -int ataDisableAutoOffline (int device ) -{ - unsigned char parms[4] = { WIN_SMART, 0, SMART_AUTO_OFFLINE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms ) != 0) - { - perror ("Smart Disable Automatic Offline failed"); - return -1; - } - - return 0; +// Another Obsolete Command! +int ataDisableAutoOffline (int device ){ + unsigned char parms[4] = {WIN_SMART, 0, SMART_AUTO_OFFLINE, 0}; + + if (ioctl(device , HDIO_DRIVE_CMD, parms)){ + perror ("SMART Disable Automatic Offline failed"); + return -1; + } + + return 0; } // Not being used correctly. Must examine the CL and CH registers to -// see what the smart status was. How to fix this? I don't know... +// see what the smart status was. Look at ataSmartStatus2() int ataSmartStatus (int device ){ - unsigned char parms[4] = { WIN_SMART, 0, SMART_STATUS, 0}; + unsigned char parms[4] = {WIN_SMART, 0, SMART_STATUS, 0}; - if (ioctl ( device , HDIO_DRIVE_CMD, &parms)) - return -1; - return 0; -} - - - -int ataSmartTest (int device, int testtype) -{ - unsigned char parms[4] = { WIN_SMART, testtype, - SMART_IMMEDIATE_OFFLINE, 0}; - - if (ioctl ( device , HDIO_DRIVE_CMD, &parms) != 0) - { - perror ("Smart Offline failed"); - return -1; + if (ioctl(device, HDIO_DRIVE_CMD, parms)){ + perror("Return SMART Status failed"); + return -1; } - - printf("Completed Off-line command\n"); - return 0; } - -int ataSmartOfflineTest (int device) -{ - return ataSmartTest( device, OFFLINE_FULL_SCAN ); +// 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); } -int ataSmartShortSelfTest (int device) -{ - return ataSmartTest( device, SHORT_SELF_TEST ); -} -int ataSmartExtendSelfTest (int device) -{ - return ataSmartTest( device, EXTEND_SELF_TEST ); -} - -int ataSmartShortCapSelfTest (int device) -{ - return ataSmartTest( device, SHORT_CAPTIVE_SELF_TEST ); +// This function needs to be properly tested and debugged. I am not +// yet sure if this is right; have asked Andre for help. May need to +// use IDE_DRIVE_TASK. Does CONFIG_IDE_TASKFILE_IO need to be +// configured into the kernel? +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, // CMD + SMART_STATUS, // FR + 0, // NS + 0, // SC + 0, // CL + 0, // CH + 0 // SEL -- Andre, is this right?? Or should it be 1? + }; + + // load CL and CH values + parms[4]=normal_cyl_lo; + parms[5]=normal_cyl_hi; + + if (ioctl(device,HDIO_DRIVE_TASK,&parms)){ + perror ("SMART Status command 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 + perror("SMART Status returned register values that don't make sense:\n"); + printf("CMD=0x%02x\n",parms[0]); + printf("FR =0x%02x\n",parms[1]); + printf("NS =0x%02x\n",parms[2]); + printf("SC =0x%02x\n",parms[3]); + printf("CL =0x%02x\n",parms[4]); + printf("CH =0x%02x\n",parms[5]); + printf("SEL=0x%02x\n",parms[6]); + + return -1; } -int ataSmartExtendCapSelfTest (int device) -{ - return ataSmartTest( device, EXTEND_CAPTIVE_SELF_TEST ); +// 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; + + 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); + printf("Sending command: \"%s\".\n",cmdmsg); + + // Now send the command to test + if (ioctl(device , HDIO_DRIVE_CMD, parms)){ + char errormsg[128]; + sprintf(errormsg,"Command \"%s\" failed.\n\n",cmdmsg); + perror (errormsg); + return -1; + } + + // Since the command succeeded, tell user + if (testtype==ABORT_SELF_TEST) + printf("Self-testing aborted!\n"); + else + printf("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg); + return 0; } -int ataSmartSelfTestAbort (int device) -{ - return ataSmartTest( device, 127 ); -} - /* Test Time Functions */ - -int isOfflineTestTime ( struct ata_smart_values data) -{ - return (int) data.total_time_to_complete_off_line; -} - - -int isShortSelfTestTime ( struct ata_smart_values data) -{ - return (int) data.short_test_completion_time; -} - - -int isExtendedSelfTestTime ( struct ata_smart_values data) -{ - return (int) data.extend_test_completion_time; +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; + } } -int isSmartErrorLogCapable ( struct ata_smart_values data) -{ +int isSmartErrorLogCapable ( struct ata_smart_values data){ return data.errorlog_capability & 0x01; } - - -int isSupportExecuteOfflineImmediate ( struct ata_smart_values data) -{ +int isSupportExecuteOfflineImmediate ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x01; } - - -int isSupportAutomaticTimer ( struct ata_smart_values data) -{ +int isSupportAutomaticTimer ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x02; } - - -int isSupportOfflineAbort ( struct ata_smart_values data) -{ +int isSupportOfflineAbort ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x04; } - - -int isSupportOfflineSurfaceScan ( struct ata_smart_values data) -{ +int isSupportOfflineSurfaceScan ( struct ata_smart_values data){ return data.offline_data_collection_capability & 0x08; } - - -int isSupportSelfTest (struct ata_smart_values data) -{ +int isSupportSelfTest (struct ata_smart_values data){ return data.offline_data_collection_capability & 0x10; } diff --git a/sm5/atacmds.h b/sm5/atacmds.h index 40acfcea415961bed5979482b86df67b8fdc1f9a..c5b2012a71222b26b8e47693f0c4ecc179afde31 100644 --- a/sm5/atacmds.h +++ b/sm5/atacmds.h @@ -1,4 +1,4 @@ -// $Id: atacmds.h,v 1.12 2002/10/15 14:24:26 ballen4705 Exp $ +// $Id: atacmds.h,v 1.13 2002/10/20 19:22:02 ballen4705 Exp $ /* * atacmds.h * @@ -72,6 +72,12 @@ #define SMART_WRITE_LOG_SECTOR 0xd6 #endif +// The following is obsolete -- don't use it! +#ifndef SMART_WRITE_THRESHOLDS +#define SMART_WRITE_THRESHOLDS 0xd7 +#endif + + #ifndef SMART_ENABLE #define SMART_ENABLE 0xd8 #endif @@ -84,6 +90,7 @@ #define SMART_STATUS 0xda #endif +// The following is also marked obsolete in ATA-5 #ifndef SMART_AUTO_OFFLINE #define SMART_AUTO_OFFLINE 0xdb #endif @@ -91,6 +98,7 @@ #define OFFLINE_FULL_SCAN 0 #define SHORT_SELF_TEST 1 #define EXTEND_SELF_TEST 2 +#define ABORT_SELF_TEST 127 #define SHORT_CAPTIVE_SELF_TEST 129 #define EXTEND_CAPTIVE_SELF_TEST 130 @@ -306,22 +314,19 @@ int ataSmartSelfTestAbort (int device); int ataVersionInfo (const char **description, struct hd_driveid drive); -/* int ataSmartSupport ( int device, struct hd_driveid drive) -* Check if S.M.A.R.T. is supported and enabled in drive -* returns -1:if S.M.A.R.T. capability can not be checked -* returns 0: if drive does not support S.M.A.R.T. -* 1: if drive supports S.M.A.R.T. but not enabled -* 2: if drive supports S.M.A.R.T. and enabled -* 255: if drive supports S.M.A.R.T. but does not -* support ATA-4. -* ATA 3 and lower do not support S.M.A.R.T. enabled bit -* Attempt a Read S.M.A.R.T. attributes to check if enabled -*/ +// If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0. +int ataDoesSmartWork(int device); +// returns 1 if SMART supported, 0 if not supported or can't tell int ataSmartSupport ( struct hd_driveid drive); -/* Check SMART for Threshold failure */ +// Return values: +// 1: SMART enabled +// 0: SMART disabled +// -1: can't tell if SMART is enabled -- try issuing ataDoesSmartWork command to see +int ataIsSmartEnabled(struct hd_driveid drive); +/* Check SMART for Threshold failure */ int ataCheckSmart ( struct ata_smart_values data, struct ata_smart_thresholds thresholds); /* int isOfflineTestTime ( struct ata_smart_values data) @@ -355,6 +360,8 @@ int isSupportOfflineSurfaceScan ( struct ata_smart_values data); int isSupportSelfTest (struct ata_smart_values data); +int ataSmartTest(int device, int testtype); +int TestTime(struct ata_smart_values data,int testtype); #endif /* _ATACMDS_H_ */ diff --git a/sm5/ataprint.c b/sm5/ataprint.c index 98e65df217d709c1993120332ac10066ba9e844a..c20078a61c3baf48679af296f74a01dff542c238 100644 --- a/sm5/ataprint.c +++ b/sm5/ataprint.c @@ -1,4 +1,4 @@ -// $Id: ataprint.c,v 1.12 2002/10/17 04:03:33 ballen4705 Exp $ +// $Id: ataprint.c,v 1.13 2002/10/20 19:22:02 ballen4705 Exp $ /* * ataprint.c * @@ -23,15 +23,56 @@ * */ +#include <ctype.h> +#include <stdio.h> #include "ataprint.h" #include "smartctl.h" #include "extern.h" +// 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) + printf("%.*s\n",n-i,out+i); + else + printf("[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) + printf("Device Model: "); + printswap(drive.model,40); + + printf("Serial Number: "); + printswap(drive.serial_no,20); + + printf("Firmware Version: "); + printswap(drive.fw_rev,8); + + // now get ATA version info version=ataVersionInfo(&description,drive); // unrecognized minor revision code @@ -40,17 +81,20 @@ void ataPrintDriveInfo (struct hd_driveid drive){ description=unknown; } - // print out information for user - printf("Device Model: %.40s\n",drive.model); - printf("Serial Number: %.20s\n",drive.serial_no); - printf("Firmware Version: %.8s\n",drive.fw_rev); - printf("ATA Version is: %i\n",version); + + // 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. + printf("ATA Version is: %i\n",version>0?version:-1*version); printf("ATA Standard is: %s\n",description); - + if (version>=3) return; - - printf("SMART is only supported in ATA version 3 or greater\n"); + + printf("SMART is only available in ATA Version 3 Revision 3 or greater.\n\n"); exit (0); } @@ -62,55 +106,61 @@ void PrintSmartOfflineStatus ( struct ata_smart_values data) { printf ("Off-line data collection status: "); - switch (data.offline_data_collection_status) - { - case 0x0: case 0x80: - printf ("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t", - data.offline_data_collection_status); - printf("never started.\n"); - break; - case 0x01: case 0x81: - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - break; - case 0x02: case 0x82: - printf ("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("completed without error.\n"); - break; - case 0x03: case 0x83: - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - break; - case 0x04: case 0x84: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("suspended by an interrupting command.\n"); - break; - case 0x05: case 0x85: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("aborted by an interrupting command.\n"); - break; - case 0x06: case 0x86: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("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)) ) - { - printf ("(0x%02x)\tVendor Specific.\n", - data.offline_data_collection_status); - } - else - { - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - } + switch (data.offline_data_collection_status){ + case 0x00: + case 0x80: + printf ("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t", + data.offline_data_collection_status); + printf("never started.\n"); + break; + case 0x01: + case 0x81: + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + break; + case 0x02: + case 0x82: + printf ("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("completed without error.\n"); + break; + case 0x03: + case 0x83: + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + break; + case 0x04: + case 0x84: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("suspended by an interrupting command from host.\n"); + break; + case 0x05: + case 0x85: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("aborted by an interrupting command from host.\n"); + break; + case 0x06: + case 0x86: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("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)) ) + { + printf ("(0x%02x)\tVendor Specific.\n", + data.offline_data_collection_status); + } + else + { + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + } } } @@ -236,8 +286,7 @@ void PrintSmartCapability ( struct ata_smart_values data) if (data.smart_capability == 0x00) { - printf ("automatic saving of SMART data"); - printf ("\t\t\t\t\tis not implemented.\n"); + printf ("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n"); } else { @@ -451,9 +500,9 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data) // starting printing error log info if (data.ata_error_count<=5) - printf ( "ATA Error Count: %u\n\n", data.ata_error_count); + printf ( "ATA Error Count: %u\n", data.ata_error_count); else - printf ( "ATA Error Count: %u (only the most recent five errors are shown below)\n\n", + printf ( "ATA Error Count: %u (only the most recent five errors are shown below)\n", data.ata_error_count); printf( "Acronyms used below:\n"); @@ -530,7 +579,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data) void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ int i,j; - printf("\nSMART Self-test log, version number %u\n",data.revnumber); + printf("SMART Self-test log, version number %u\n",data.revnumber); if (data.revnumber!=0x01) printf("Warning - structure revision number does not match spec!\n"); @@ -540,7 +589,7 @@ void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ } // print log - printf("\nNum Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); + printf("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); for (i=20;i>=0;i--){ struct ata_smart_selftestlog_struct *log; @@ -595,25 +644,24 @@ void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ return; } -void ataPsuedoCheckSmart ( struct ata_smart_values data, +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)){ - printf("Attribute ID %i Failed\n", - data.vendor_attributes[i].id); - failed = 1; - } - } - printf("%s\n", ( failed )? - "SMART overall-health self-assessment test result: FAILED!\n" - "Drive failure expected in less than 24 hours. SAVE ALL DATA\n": - "SMART overall-health self-assessment test result: PASSED\n"); + 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)){ + printf("Attribute ID %i Failed\n",data.vendor_attributes[i].id); + failed = 1; + } + } + printf("%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"); } void ataPrintSmartAttribName ( unsigned char id ){ @@ -722,322 +770,195 @@ void ataPrintSmartAttribName ( unsigned char id ){ Called by smartctl to access ataprint **/ -void ataPrintMain ( int fd ) -{ - struct hd_driveid drive; - struct ata_smart_values smartval; - struct ata_smart_thresholds smartthres; - struct ata_smart_errorlog smarterror; - struct ata_smart_selftestlog smartselftest; - - if ( driveinfo ) - { - if ( ataReadHDIdentity ( fd, &drive) != 0 ) - { - printf("Smartctl: Hard Drive Identity Failed\n"); - exit(0); - } - - ataPrintDriveInfo(drive); - - - if (ataSmartSupport(drive)) - { - printf ("SMART support is: "); - - if ( ataSmartStatus(fd) != 0) - { - printf( "Disabled\n"); - printf( "Use option -%c to enable\n\n", SMARTENABLE ); - exit(0); - } - else { - printf( "Enabled\n\n"); - } - } - else { - printf("SMART support is: Unavailable. Device lacks SMART capability.\n\n"); - exit (0); - } +void ataPrintMain (int fd){ + 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 timewait; + + // Start by getting Drive ID information. We need this, to know if SMART is supported. + if (ataReadHDIdentity(fd,&drive)){ + printf("Smartctl: Hard Drive Read Identity Failed\n\n"); + exit(-1); } - - if ( smartdisable ) - { - - if ( ataDisableSmart(fd) != 0) - { - printf( "Smartctl: SMART Enable Failed\n"); - exit(-1); - } - - printf("SMART Disabled\n"); - exit (0); - - } - - if ( smartenable ) - { - if ( ataEnableSmart(fd) != 0) - { - printf( "Smartctl: SMART Enable Failed\n"); - exit(-1); - } - - if (ataSmartStatus(fd)==0) - printf("SMART Enabled\n"); - else - printf( "Smartctl: SMART Enable Failed for unknown reasons\n"); - } - - - if ( smartautosavedisable ){ - if (ataDisableAutoSave(fd) != 0) - { - printf( "Smartctl: SMART Disable Attribute Autosave Failed\n"); - exit(-1); - } - printf("SMART Attribute Autosave Disabled\n"); - } - - if ( smartautosaveenable ){ - if (ataEnableAutoSave(fd) != 0) - { - printf( "Smartctl: SMART Enable Attribute Autosave Failed\n"); - exit(-1); - } - printf("SMART Attribute Autosave Enabled\n"); - } - - /* for everything else read values and thresholds - are needed */ - - if ( ataReadSmartValues ( fd, &smartval) != 0 ) - { - printf("Smartctl: SMART Values Read Failed\n"); - exit (-1); - } - - if ( ataReadSmartThresholds ( fd, &smartthres) != 0 ) - { - printf("Smartctl: SMART Thresholds Read Failed\n"); - exit (-1); - } - - if ( checksmart ) - { - /* pseudo is used because linux does not support access to - Task File registers */ - // I am very confused by this comment. Does anyone get it? Bruce - ataPsuedoCheckSmart ( smartval , smartthres); - - } - - if ( generalsmartvalues ) - { - ataPrintGeneralSmartValues( smartval ); - } - - if ( smartvendorattrib ) - { - PrintSmartAttribWithThres( smartval, smartthres); - } - - if ( smarterrorlog ) - { - if ( isSmartErrorLogCapable(smartval) == 0) - { - printf("Device does not support Error Logging\n"); - } - else - { - if ( ataReadErrorLog ( fd, &smarterror) != 0 ) - { - printf("Smartctl: SMART Errorlog Read Failed\n"); - } - else - { - ataPrintSmartErrorlog ( smarterror); - } - } + + // Print most drive identity information if requested + if (driveinfo){ + printf("\n=== START OF INFORMATION SECTION ===\n"); + ataPrintDriveInfo(drive); + } + + // now check if drive supports SMART; otherwise time to exit + if (!ataSmartSupport(drive)){ + printf("SMART support is: Unavailable - device lacks SMART capability.\n"); + exit (0); + } + + // Now print remaining drive info: is SMART enabled? + if (driveinfo){ + printf("SMART support is: Available - device has SMART capability.\n"); + if (ataDoesSmartWork(fd)) + printf("SMART support is: Enabled\n"); + else + printf("SMART support is: Disabled\n"); + } + + // START OF THE ENABLE/DISABLE SECTION OF THE CODE + if (smartenable || smartdisable || + smartautosaveenable || smartautosavedisable || + smartautoofflineenable || smartautoofflinedisable) + printf("\n=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); + + // Enable/Disable SMART commands + if (smartenable){ + if (ataEnableSmart(fd)) { + printf("Smartctl: SMART Enable Failed.\n\n"); + exit(-1); } - - - if ( smartselftestlog ) - { - if ( isSmartErrorLogCapable(smartval) == 0) - { - printf("Device does not support Self Test Logging\n"); - } - else - { - if ( ataReadSelfTestLog( fd, &smartselftest) != 0 ) - { - printf("Smartctl: SMART Self Test Log Read Failed\n"); - } - else - { - ataPrintSmartSelfTestlog (smartselftest); - } - } + else + printf("SMART Enabled.\n"); + } + + // From here on, every command requires that SMART be enabled... + if (!ataDoesSmartWork(fd)) { + printf("SMART Disabled. Use option -%c to enable it.\n", SMARTENABLE ); + exit(0); + } + + // Turn off SMART on device + if (smartdisable){ + if (ataDisableSmart(fd)) { + printf( "Smartctl: SMART Disable Failed.\n\n"); + exit(-1); } + printf("SMART Disabled. Use option -%c to enable it.\n",SMARTENABLE); + exit (0); + } + + // Enable/Disable Auto-save attributes + if (smartautosaveenable){ + if (ataEnableAutoSave(fd)){ + printf( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n"); + exit(-1); + } + printf("SMART Attribute Autosave Enabled\n"); + } + if (smartautosavedisable){ + if (ataDisableAutoSave(fd)){ + printf( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n"); + exit(-1); + } + printf("SMART Attribute Autosave Disabled\n"); + } + + // for everything else read values and thresholds are needed + if (ataReadSmartValues(fd, &smartval)){ + printf("Smartctl: SMART Values Read Failed.\n\n"); + exit (-1); + } + if (ataReadSmartThresholds(fd, &smartthres)){ + printf("Smartctl: SMART Thresholds Read Failed.\n\n"); + exit (-1); + } - if ( smartautoofflineenable ) - { - if ( !isSupportAutomaticTimer (smartval)) - { - printf("Device does not support SMART Automatic Timers\n"); - exit(-1); - } - - if ( ataEnableAutoOffline (fd) != 0) - { - printf( "Smartctl: SMART Enable Automatic Offline Failed\n"); - exit(-1); - } - - printf ("SMART Automatic Offline Testing Enabled every four hours\n"); + // Enable/Disable Off-line testing + if (smartautoofflineenable){ + if (!isSupportAutomaticTimer (smartval)){ + printf("Device does not support SMART Automatic Timers.\n\n"); + exit(-1); } + if (ataEnableAutoOffline (fd)){ + printf( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); + exit(-1); + } + printf ("SMART Automatic Offline Testing Enabled every four hours.\n"); + } + if (smartautoofflinedisable){ + if (!isSupportAutomaticTimer (smartval)){ + printf("Device does not support SMART Automatic Timers.\n\n"); + exit(-1); + } + if ( ataDisableAutoOffline (fd)){ + printf( "Smartctl: SMART Disable Automatic Offline Failed.\n\n"); + exit(-1); + } + printf ("SMART Automatic Offline Testing Disabled.\n"); + } - if ( smartautoofflinedisable ) - { - if ( !isSupportAutomaticTimer (smartval)) - { - printf("Device does not support SMART Automatic Timers\n"); - exit(-1); - } - - if ( ataDisableAutoOffline (fd) != 0) - { - printf( "Smartctl: SMART Disable Automatic Offline Failed\n"); - exit(-1); - } - - printf ("SMART Automatic Offline Testing Disabled\n"); - - } - - - if ( smartexeoffimmediate ) - { - if ( ataSmartOfflineTest (fd) != 0) - { - printf( "Smartctl: SMART Offline Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d seconds for test to complete\n", - isOfflineTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - exit (0); - } - - if ( smartshortcapselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartShortCapSelfTest (fd) != 0) - { - printf( "Smartctl: SMART Short Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isShortSelfTestTime (smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); + // START OF READ-ONLY OPTIONS APART FROM -p and -i + if (checksmart || generalsmartvalues || smartvendorattrib || smarterrorlog || smartselftestlog) + printf("\n=== START OF READ SMART DATA SECTION ===\n"); + + // Check SMART status + if (checksmart) + // Eventually when working use the ataSmartStatus2 function here. + // This will then not require read values & thresholds above, and + // shouldl be moved in the code to just before the read values & + // thresholds statements. + ataPseudoCheckSmart(smartval, smartthres); + + // Print general SMART values + if (generalsmartvalues) + ataPrintGeneralSmartValues(smartval); + + // Print vendor-specific attributes + if (smartvendorattrib) + PrintSmartAttribWithThres(smartval, smartthres); - /* Make sure Offline testing is last thing done */ - exit (0); - } + // Print SMART error log + if (smarterrorlog){ + if (!isSmartErrorLogCapable(smartval)) + printf("Device does not support Error Logging\n"); + else { + if (ataReadErrorLog(fd, &smarterror)) + printf("Smartctl: SMART Errorlog Read Failed\n"); + else + ataPrintSmartErrorlog(smarterror); + } + } - if ( smartshortselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartShortSelfTest (fd) != 0) - { - printf( "Smartctl: SMART Short Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isShortSelfTestTime (smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - - /* Make sure Offline testing is last thing done */ - exit (0); - } - - - if ( smartextendselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartExtendSelfTest (fd) != 0) - { - printf( "SMART Extended Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful self test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isExtendedSelfTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - - exit (0); - } + // Print SMART self-test log + if (smartselftestlog){ + if (!isSmartErrorLogCapable(smartval)) + printf("Device does not support Self Test Logging\n"); + else { + if(ataReadSelfTestLog(fd, &smartselftest)) + printf("Smartctl: SMART Self Test Log Read Failed\n"); + else + ataPrintSmartSelfTestlog(smartselftest); + } + } + + // START OF THE TESTING SECTION OF THE CODE. IF NO TESTING, RETURN + if (testcase==-1) + return; - - if ( smartextendcapselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support self test function\n"); - exit(-1); - } - - if ( ataSmartExtendCapSelfTest (fd) != 0) - { - printf( "SMART Extended Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful captive extended self test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isExtendedSelfTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - exit (0); - } + printf("\n=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n"); - if ( smartselftestabort ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartSelfTestAbort (fd) != 0) - { - printf( "SMART Self Test Abort Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful self test aborted\n"); - } - + + // if doing a self-test, be sure it's supported by the hardware + if (testcase==OFFLINE_FULL_SCAN && !isSupportExecuteOfflineImmediate(smartval)){ + printf("ERROR: device does not support Execute Off-Line Immediate function.\n\n"); + exit(-1); + } + else if (!isSupportSelfTest(smartval)){ + printf ("ERROR: device does not support Self-Test functions.\n\n"); + exit(-1); + } + + // Now do the test + if (ataSmartTest(fd, testcase)) + exit(-1); + + // Tell user how long test will take to complete + if ((timewait=TestTime(smartval,testcase))){ + printf ("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) + printf ("Use smartctl -%c to abort test.\n", SMARTSELFTESTABORT); + } + return; } diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp index 544a339e8005cdb87218353f688d3f4140c7005b..4834cb2f3c649c84a3fca0d0c1586fe9237380bf 100644 --- a/sm5/ataprint.cpp +++ b/sm5/ataprint.cpp @@ -1,4 +1,4 @@ -// $Id: ataprint.cpp,v 1.12 2002/10/17 04:03:33 ballen4705 Exp $ +// $Id: ataprint.cpp,v 1.13 2002/10/20 19:22:02 ballen4705 Exp $ /* * ataprint.c * @@ -23,15 +23,56 @@ * */ +#include <ctype.h> +#include <stdio.h> #include "ataprint.h" #include "smartctl.h" #include "extern.h" +// 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) + printf("%.*s\n",n-i,out+i); + else + printf("[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) + printf("Device Model: "); + printswap(drive.model,40); + + printf("Serial Number: "); + printswap(drive.serial_no,20); + + printf("Firmware Version: "); + printswap(drive.fw_rev,8); + + // now get ATA version info version=ataVersionInfo(&description,drive); // unrecognized minor revision code @@ -40,17 +81,20 @@ void ataPrintDriveInfo (struct hd_driveid drive){ description=unknown; } - // print out information for user - printf("Device Model: %.40s\n",drive.model); - printf("Serial Number: %.20s\n",drive.serial_no); - printf("Firmware Version: %.8s\n",drive.fw_rev); - printf("ATA Version is: %i\n",version); + + // 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. + printf("ATA Version is: %i\n",version>0?version:-1*version); printf("ATA Standard is: %s\n",description); - + if (version>=3) return; - - printf("SMART is only supported in ATA version 3 or greater\n"); + + printf("SMART is only available in ATA Version 3 Revision 3 or greater.\n\n"); exit (0); } @@ -62,55 +106,61 @@ void PrintSmartOfflineStatus ( struct ata_smart_values data) { printf ("Off-line data collection status: "); - switch (data.offline_data_collection_status) - { - case 0x0: case 0x80: - printf ("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t", - data.offline_data_collection_status); - printf("never started.\n"); - break; - case 0x01: case 0x81: - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - break; - case 0x02: case 0x82: - printf ("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("completed without error.\n"); - break; - case 0x03: case 0x83: - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - break; - case 0x04: case 0x84: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("suspended by an interrupting command.\n"); - break; - case 0x05: case 0x85: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("aborted by an interrupting command.\n"); - break; - case 0x06: case 0x86: - printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", - data.offline_data_collection_status); - printf ("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)) ) - { - printf ("(0x%02x)\tVendor Specific.\n", - data.offline_data_collection_status); - } - else - { - printf ("(0x%02x)\tReserved.\n", - data.offline_data_collection_status); - } + switch (data.offline_data_collection_status){ + case 0x00: + case 0x80: + printf ("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t", + data.offline_data_collection_status); + printf("never started.\n"); + break; + case 0x01: + case 0x81: + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + break; + case 0x02: + case 0x82: + printf ("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("completed without error.\n"); + break; + case 0x03: + case 0x83: + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + break; + case 0x04: + case 0x84: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("suspended by an interrupting command from host.\n"); + break; + case 0x05: + case 0x85: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("aborted by an interrupting command from host.\n"); + break; + case 0x06: + case 0x86: + printf ("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t", + data.offline_data_collection_status); + printf ("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)) ) + { + printf ("(0x%02x)\tVendor Specific.\n", + data.offline_data_collection_status); + } + else + { + printf ("(0x%02x)\tReserved.\n", + data.offline_data_collection_status); + } } } @@ -236,8 +286,7 @@ void PrintSmartCapability ( struct ata_smart_values data) if (data.smart_capability == 0x00) { - printf ("automatic saving of SMART data"); - printf ("\t\t\t\t\tis not implemented.\n"); + printf ("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n"); } else { @@ -451,9 +500,9 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data) // starting printing error log info if (data.ata_error_count<=5) - printf ( "ATA Error Count: %u\n\n", data.ata_error_count); + printf ( "ATA Error Count: %u\n", data.ata_error_count); else - printf ( "ATA Error Count: %u (only the most recent five errors are shown below)\n\n", + printf ( "ATA Error Count: %u (only the most recent five errors are shown below)\n", data.ata_error_count); printf( "Acronyms used below:\n"); @@ -530,7 +579,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data) void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ int i,j; - printf("\nSMART Self-test log, version number %u\n",data.revnumber); + printf("SMART Self-test log, version number %u\n",data.revnumber); if (data.revnumber!=0x01) printf("Warning - structure revision number does not match spec!\n"); @@ -540,7 +589,7 @@ void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ } // print log - printf("\nNum Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); + printf("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n"); for (i=20;i>=0;i--){ struct ata_smart_selftestlog_struct *log; @@ -595,25 +644,24 @@ void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data){ return; } -void ataPsuedoCheckSmart ( struct ata_smart_values data, +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)){ - printf("Attribute ID %i Failed\n", - data.vendor_attributes[i].id); - failed = 1; - } - } - printf("%s\n", ( failed )? - "SMART overall-health self-assessment test result: FAILED!\n" - "Drive failure expected in less than 24 hours. SAVE ALL DATA\n": - "SMART overall-health self-assessment test result: PASSED\n"); + 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)){ + printf("Attribute ID %i Failed\n",data.vendor_attributes[i].id); + failed = 1; + } + } + printf("%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"); } void ataPrintSmartAttribName ( unsigned char id ){ @@ -722,322 +770,195 @@ void ataPrintSmartAttribName ( unsigned char id ){ Called by smartctl to access ataprint **/ -void ataPrintMain ( int fd ) -{ - struct hd_driveid drive; - struct ata_smart_values smartval; - struct ata_smart_thresholds smartthres; - struct ata_smart_errorlog smarterror; - struct ata_smart_selftestlog smartselftest; - - if ( driveinfo ) - { - if ( ataReadHDIdentity ( fd, &drive) != 0 ) - { - printf("Smartctl: Hard Drive Identity Failed\n"); - exit(0); - } - - ataPrintDriveInfo(drive); - - - if (ataSmartSupport(drive)) - { - printf ("SMART support is: "); - - if ( ataSmartStatus(fd) != 0) - { - printf( "Disabled\n"); - printf( "Use option -%c to enable\n\n", SMARTENABLE ); - exit(0); - } - else { - printf( "Enabled\n\n"); - } - } - else { - printf("SMART support is: Unavailable. Device lacks SMART capability.\n\n"); - exit (0); - } +void ataPrintMain (int fd){ + 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 timewait; + + // Start by getting Drive ID information. We need this, to know if SMART is supported. + if (ataReadHDIdentity(fd,&drive)){ + printf("Smartctl: Hard Drive Read Identity Failed\n\n"); + exit(-1); } - - if ( smartdisable ) - { - - if ( ataDisableSmart(fd) != 0) - { - printf( "Smartctl: SMART Enable Failed\n"); - exit(-1); - } - - printf("SMART Disabled\n"); - exit (0); - - } - - if ( smartenable ) - { - if ( ataEnableSmart(fd) != 0) - { - printf( "Smartctl: SMART Enable Failed\n"); - exit(-1); - } - - if (ataSmartStatus(fd)==0) - printf("SMART Enabled\n"); - else - printf( "Smartctl: SMART Enable Failed for unknown reasons\n"); - } - - - if ( smartautosavedisable ){ - if (ataDisableAutoSave(fd) != 0) - { - printf( "Smartctl: SMART Disable Attribute Autosave Failed\n"); - exit(-1); - } - printf("SMART Attribute Autosave Disabled\n"); - } - - if ( smartautosaveenable ){ - if (ataEnableAutoSave(fd) != 0) - { - printf( "Smartctl: SMART Enable Attribute Autosave Failed\n"); - exit(-1); - } - printf("SMART Attribute Autosave Enabled\n"); - } - - /* for everything else read values and thresholds - are needed */ - - if ( ataReadSmartValues ( fd, &smartval) != 0 ) - { - printf("Smartctl: SMART Values Read Failed\n"); - exit (-1); - } - - if ( ataReadSmartThresholds ( fd, &smartthres) != 0 ) - { - printf("Smartctl: SMART Thresholds Read Failed\n"); - exit (-1); - } - - if ( checksmart ) - { - /* pseudo is used because linux does not support access to - Task File registers */ - // I am very confused by this comment. Does anyone get it? Bruce - ataPsuedoCheckSmart ( smartval , smartthres); - - } - - if ( generalsmartvalues ) - { - ataPrintGeneralSmartValues( smartval ); - } - - if ( smartvendorattrib ) - { - PrintSmartAttribWithThres( smartval, smartthres); - } - - if ( smarterrorlog ) - { - if ( isSmartErrorLogCapable(smartval) == 0) - { - printf("Device does not support Error Logging\n"); - } - else - { - if ( ataReadErrorLog ( fd, &smarterror) != 0 ) - { - printf("Smartctl: SMART Errorlog Read Failed\n"); - } - else - { - ataPrintSmartErrorlog ( smarterror); - } - } + + // Print most drive identity information if requested + if (driveinfo){ + printf("\n=== START OF INFORMATION SECTION ===\n"); + ataPrintDriveInfo(drive); + } + + // now check if drive supports SMART; otherwise time to exit + if (!ataSmartSupport(drive)){ + printf("SMART support is: Unavailable - device lacks SMART capability.\n"); + exit (0); + } + + // Now print remaining drive info: is SMART enabled? + if (driveinfo){ + printf("SMART support is: Available - device has SMART capability.\n"); + if (ataDoesSmartWork(fd)) + printf("SMART support is: Enabled\n"); + else + printf("SMART support is: Disabled\n"); + } + + // START OF THE ENABLE/DISABLE SECTION OF THE CODE + if (smartenable || smartdisable || + smartautosaveenable || smartautosavedisable || + smartautoofflineenable || smartautoofflinedisable) + printf("\n=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); + + // Enable/Disable SMART commands + if (smartenable){ + if (ataEnableSmart(fd)) { + printf("Smartctl: SMART Enable Failed.\n\n"); + exit(-1); } - - - if ( smartselftestlog ) - { - if ( isSmartErrorLogCapable(smartval) == 0) - { - printf("Device does not support Self Test Logging\n"); - } - else - { - if ( ataReadSelfTestLog( fd, &smartselftest) != 0 ) - { - printf("Smartctl: SMART Self Test Log Read Failed\n"); - } - else - { - ataPrintSmartSelfTestlog (smartselftest); - } - } + else + printf("SMART Enabled.\n"); + } + + // From here on, every command requires that SMART be enabled... + if (!ataDoesSmartWork(fd)) { + printf("SMART Disabled. Use option -%c to enable it.\n", SMARTENABLE ); + exit(0); + } + + // Turn off SMART on device + if (smartdisable){ + if (ataDisableSmart(fd)) { + printf( "Smartctl: SMART Disable Failed.\n\n"); + exit(-1); } + printf("SMART Disabled. Use option -%c to enable it.\n",SMARTENABLE); + exit (0); + } + + // Enable/Disable Auto-save attributes + if (smartautosaveenable){ + if (ataEnableAutoSave(fd)){ + printf( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n"); + exit(-1); + } + printf("SMART Attribute Autosave Enabled\n"); + } + if (smartautosavedisable){ + if (ataDisableAutoSave(fd)){ + printf( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n"); + exit(-1); + } + printf("SMART Attribute Autosave Disabled\n"); + } + + // for everything else read values and thresholds are needed + if (ataReadSmartValues(fd, &smartval)){ + printf("Smartctl: SMART Values Read Failed.\n\n"); + exit (-1); + } + if (ataReadSmartThresholds(fd, &smartthres)){ + printf("Smartctl: SMART Thresholds Read Failed.\n\n"); + exit (-1); + } - if ( smartautoofflineenable ) - { - if ( !isSupportAutomaticTimer (smartval)) - { - printf("Device does not support SMART Automatic Timers\n"); - exit(-1); - } - - if ( ataEnableAutoOffline (fd) != 0) - { - printf( "Smartctl: SMART Enable Automatic Offline Failed\n"); - exit(-1); - } - - printf ("SMART Automatic Offline Testing Enabled every four hours\n"); + // Enable/Disable Off-line testing + if (smartautoofflineenable){ + if (!isSupportAutomaticTimer (smartval)){ + printf("Device does not support SMART Automatic Timers.\n\n"); + exit(-1); } + if (ataEnableAutoOffline (fd)){ + printf( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); + exit(-1); + } + printf ("SMART Automatic Offline Testing Enabled every four hours.\n"); + } + if (smartautoofflinedisable){ + if (!isSupportAutomaticTimer (smartval)){ + printf("Device does not support SMART Automatic Timers.\n\n"); + exit(-1); + } + if ( ataDisableAutoOffline (fd)){ + printf( "Smartctl: SMART Disable Automatic Offline Failed.\n\n"); + exit(-1); + } + printf ("SMART Automatic Offline Testing Disabled.\n"); + } - if ( smartautoofflinedisable ) - { - if ( !isSupportAutomaticTimer (smartval)) - { - printf("Device does not support SMART Automatic Timers\n"); - exit(-1); - } - - if ( ataDisableAutoOffline (fd) != 0) - { - printf( "Smartctl: SMART Disable Automatic Offline Failed\n"); - exit(-1); - } - - printf ("SMART Automatic Offline Testing Disabled\n"); - - } - - - if ( smartexeoffimmediate ) - { - if ( ataSmartOfflineTest (fd) != 0) - { - printf( "Smartctl: SMART Offline Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d seconds for test to complete\n", - isOfflineTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - exit (0); - } - - if ( smartshortcapselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartShortCapSelfTest (fd) != 0) - { - printf( "Smartctl: SMART Short Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isShortSelfTestTime (smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); + // START OF READ-ONLY OPTIONS APART FROM -p and -i + if (checksmart || generalsmartvalues || smartvendorattrib || smarterrorlog || smartselftestlog) + printf("\n=== START OF READ SMART DATA SECTION ===\n"); + + // Check SMART status + if (checksmart) + // Eventually when working use the ataSmartStatus2 function here. + // This will then not require read values & thresholds above, and + // shouldl be moved in the code to just before the read values & + // thresholds statements. + ataPseudoCheckSmart(smartval, smartthres); + + // Print general SMART values + if (generalsmartvalues) + ataPrintGeneralSmartValues(smartval); + + // Print vendor-specific attributes + if (smartvendorattrib) + PrintSmartAttribWithThres(smartval, smartthres); - /* Make sure Offline testing is last thing done */ - exit (0); - } + // Print SMART error log + if (smarterrorlog){ + if (!isSmartErrorLogCapable(smartval)) + printf("Device does not support Error Logging\n"); + else { + if (ataReadErrorLog(fd, &smarterror)) + printf("Smartctl: SMART Errorlog Read Failed\n"); + else + ataPrintSmartErrorlog(smarterror); + } + } - if ( smartshortselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartShortSelfTest (fd) != 0) - { - printf( "Smartctl: SMART Short Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful offline test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isShortSelfTestTime (smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - - /* Make sure Offline testing is last thing done */ - exit (0); - } - - - if ( smartextendselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartExtendSelfTest (fd) != 0) - { - printf( "SMART Extended Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful self test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isExtendedSelfTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - - exit (0); - } + // Print SMART self-test log + if (smartselftestlog){ + if (!isSmartErrorLogCapable(smartval)) + printf("Device does not support Self Test Logging\n"); + else { + if(ataReadSelfTestLog(fd, &smartselftest)) + printf("Smartctl: SMART Self Test Log Read Failed\n"); + else + ataPrintSmartSelfTestlog(smartselftest); + } + } + + // START OF THE TESTING SECTION OF THE CODE. IF NO TESTING, RETURN + if (testcase==-1) + return; - - if ( smartextendcapselftest ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support self test function\n"); - exit(-1); - } - - if ( ataSmartExtendCapSelfTest (fd) != 0) - { - printf( "SMART Extended Self Test Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful captive extended self test has begun\n"); - printf ("Please wait %d minutes for test to complete\n", - isExtendedSelfTestTime(smartval) ); - printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT); - exit (0); - } + printf("\n=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n"); - if ( smartselftestabort ) - { - if ( ! isSupportSelfTest(smartval) ) - { - printf ("ERROR: device does not support Self-Test function\n"); - exit(-1); - } - - if ( ataSmartSelfTestAbort (fd) != 0) - { - printf( "SMART Self Test Abort Failed\n"); - exit(-1); - } - - printf ("Drive Command Successful self test aborted\n"); - } - + + // if doing a self-test, be sure it's supported by the hardware + if (testcase==OFFLINE_FULL_SCAN && !isSupportExecuteOfflineImmediate(smartval)){ + printf("ERROR: device does not support Execute Off-Line Immediate function.\n\n"); + exit(-1); + } + else if (!isSupportSelfTest(smartval)){ + printf ("ERROR: device does not support Self-Test functions.\n\n"); + exit(-1); + } + + // Now do the test + if (ataSmartTest(fd, testcase)) + exit(-1); + + // Tell user how long test will take to complete + if ((timewait=TestTime(smartval,testcase))){ + printf ("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) + printf ("Use smartctl -%c to abort test.\n", SMARTSELFTESTABORT); + } + return; } diff --git a/sm5/ataprint.h b/sm5/ataprint.h index bc3f0ebb0661e4b730f367de4abaf5b858a71750..2184b51913c7feb52312e524fa4e092abc6e8cb9 100644 --- a/sm5/ataprint.h +++ b/sm5/ataprint.h @@ -1,4 +1,4 @@ -// $Id: ataprint.h,v 1.5 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: ataprint.h,v 1.6 2002/10/20 19:22:02 ballen4705 Exp $ /* * ataprint.c @@ -32,11 +32,7 @@ #include "atacmds.h" -/* Print Format of Structures for SMART information */ - - /* Prints ATA Drive Information and S.M.A.R.T. Capability */ - void ataPrintDriveInfo (struct hd_driveid); void ataPrintGeneralSmartValues (struct ata_smart_values); @@ -52,12 +48,11 @@ void PrintSmartAttribWithThres (struct ata_smart_values data, void ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data); -void ataPsuedoCheckSmart (struct ata_smart_values , +void ataPseudoCheckSmart (struct ata_smart_values , struct ata_smart_thresholds ); /* Prints Attribute Name for standard SMART attributes */ /* prints 20 character string */ - void ataPrintSmartAttribName (unsigned char id); void ataPrintMain ( int fd ); diff --git a/sm5/extern.h b/sm5/extern.h index ce81cd2bacf3453ecdf38bae1040945bed8f8c06..a49096f4ff22608bbd70d18299ba0228f3e9ab9a 100644 --- a/sm5/extern.h +++ b/sm5/extern.h @@ -1,4 +1,4 @@ -// $Id: extern.h,v 1.5 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: extern.h,v 1.6 2002/10/20 19:22:02 ballen4705 Exp $ /* * extern.h * @@ -46,5 +46,5 @@ extern unsigned char smartautoofflinedisable; extern unsigned char smartautosaveenable; extern unsigned char smartautosavedisable; extern unsigned char smart009minutes; - +extern int testcase; #endif diff --git a/sm5/smartctl.c b/sm5/smartctl.c index f90a0429158fa89a8da07123bb15d1a47b943235..c4995adc39cf061ab315cf167947f95a74ca5f10 100644 --- a/sm5/smartctl.c +++ b/sm5/smartctl.c @@ -1,4 +1,4 @@ -// $Id: smartctl.c,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: smartctl.c,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $ /* * smartctl.c * @@ -58,6 +58,7 @@ unsigned char smartautosaveenable = FALSE; unsigned char smartautosavedisable = FALSE; unsigned char printcopyleft = FALSE; unsigned char smart009minutes = FALSE; +int testcase = -1; /* void Usage (void) @@ -66,7 +67,7 @@ unsigned char smart009minutes = FALSE; void Usage ( void){ - printf( "usage: smartctl -[options] [device]\n"); + printf( "Usage: smartctl -[options] [device]\n\n"); printf( "Read Only Options:\n"); printf( "\t\t%c\t\tShow version, copyright and license information\n", PRINTCOPYLEFT); printf( "\t\t%c\t\tShow all S.M.A.R.T. Information (ATA and SCSI)\n", SMARTVERBOSEALL); @@ -94,12 +95,12 @@ void Usage ( void){ printf( "\t\t%c\t\tExecute Short Self Test (Captive Mode) (ATA Only)\n", SMARTSHORTCAPSELFTEST ); printf( "\t\t%c\t\tExecute Extended Self Test (ATA Only)\n", SMARTEXTENDSELFTEST ); printf( "\t\t%c\t\tExecute Extended Self Test (Captive Mode) (ATA Only)\n", SMARTEXTENDCAPSELFTEST ); - printf( "\t\t%c\t\tExecute Self Test Abort (ATA Only)\n\n", SMARTSELFTESTABORT ); + printf( "\t\t%c\t\tExecute Self Test Abort (ATA Only)\n", SMARTSELFTESTABORT ); printf( "Examples:\n"); printf("\tsmartctl -etf /dev/hda (Enables S.M.A.R.T. on first disk)\n"); printf("\tsmartctl -a /dev/hda (Prints all S.M.A.R.T. information)\n"); - printf("\tsmartctl -X /dev/hda (Executes extended disk self-test)\n"); - printf("Please see the man pages or the web site for further information.\n"); + printf("\tsmartctl -X /dev/hda (Executes extended disk self-test)\n\n"); + printf("Please see the man pages or %s for further information.\n",PROJECTHOME); } @@ -131,7 +132,6 @@ void ParseOpts (int argc, char** argv){ driveinfo = TRUE; break; case CHECKSMART : - driveinfo = TRUE; checksmart = TRUE; break; case SMARTVERBOSEALL : @@ -174,21 +174,27 @@ void ParseOpts (int argc, char** argv){ break; case SMARTEXEOFFIMMEDIATE: smartexeoffimmediate = TRUE; + testcase=OFFLINE_FULL_SCAN; break; case SMARTSHORTSELFTEST : smartshortselftest = TRUE; + testcase=SHORT_SELF_TEST; break; case SMARTEXTENDSELFTEST : smartextendselftest = TRUE; + testcase=EXTEND_SELF_TEST; break; case SMARTSHORTCAPSELFTEST: smartshortcapselftest = TRUE; + testcase=SHORT_CAPTIVE_SELF_TEST; break; case SMARTEXTENDCAPSELFTEST: smartextendcapselftest = TRUE; + testcase=EXTEND_CAPTIVE_SELF_TEST; break; case SMARTSELFTESTABORT: smartselftestabort = TRUE; + testcase=ABORT_SELF_TEST; break; default: Usage(); @@ -197,9 +203,9 @@ void ParseOpts (int argc, char** argv){ if ( (smartexeoffimmediate + smartshortselftest + smartextendselftest + smartshortcapselftest + - smartextendcapselftest ) > 1){ + smartextendcapselftest +smartselftestabort ) > 1){ Usage(); - printf ("\n ERROR: smartctl can only run a single test at a time \n"); + printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n"); exit(-1); } } @@ -212,19 +218,20 @@ int main (int argc, char **argv){ char *device; printf("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n",RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION); - printf("Home page of smartctl is %s\n\n",PROJECTHOME); + printf("Home page of smartctl is %s\n",PROJECTHOME); // Part input arguments ParseOpts (argc,argv); // Print Copyright/License info if needed if (printcopyleft){ - printf("smartctl comes with ABSOLUTELY NO WARRANTY. This\n"); + printf("\nsmartctl comes with ABSOLUTELY NO WARRANTY. This\n"); printf("is free software, and you are welcome to redistribute it\n"); printf("under the terms of the GNU General Public License Version 2.\n"); printf("See http://www.gnu.org for further details.\n\n"); - printf("CVS version ID %s\n","$Id: smartctl.c,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $"); - exit(0); + printf("CVS version ID %s\n","$Id: smartctl.c,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $"); + if (argc==2) + exit(0); } // Further argument checking diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp index aed6cba2b36724df946186f62e9e21a6fe5d269f..1a5bf755d71194154e3a944bcc9d4e5f6f087f58 100644 --- a/sm5/smartctl.cpp +++ b/sm5/smartctl.cpp @@ -1,4 +1,4 @@ -// $Id: smartctl.cpp,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: smartctl.cpp,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $ /* * smartctl.c * @@ -58,6 +58,7 @@ unsigned char smartautosaveenable = FALSE; unsigned char smartautosavedisable = FALSE; unsigned char printcopyleft = FALSE; unsigned char smart009minutes = FALSE; +int testcase = -1; /* void Usage (void) @@ -66,7 +67,7 @@ unsigned char smart009minutes = FALSE; void Usage ( void){ - printf( "usage: smartctl -[options] [device]\n"); + printf( "Usage: smartctl -[options] [device]\n\n"); printf( "Read Only Options:\n"); printf( "\t\t%c\t\tShow version, copyright and license information\n", PRINTCOPYLEFT); printf( "\t\t%c\t\tShow all S.M.A.R.T. Information (ATA and SCSI)\n", SMARTVERBOSEALL); @@ -94,12 +95,12 @@ void Usage ( void){ printf( "\t\t%c\t\tExecute Short Self Test (Captive Mode) (ATA Only)\n", SMARTSHORTCAPSELFTEST ); printf( "\t\t%c\t\tExecute Extended Self Test (ATA Only)\n", SMARTEXTENDSELFTEST ); printf( "\t\t%c\t\tExecute Extended Self Test (Captive Mode) (ATA Only)\n", SMARTEXTENDCAPSELFTEST ); - printf( "\t\t%c\t\tExecute Self Test Abort (ATA Only)\n\n", SMARTSELFTESTABORT ); + printf( "\t\t%c\t\tExecute Self Test Abort (ATA Only)\n", SMARTSELFTESTABORT ); printf( "Examples:\n"); printf("\tsmartctl -etf /dev/hda (Enables S.M.A.R.T. on first disk)\n"); printf("\tsmartctl -a /dev/hda (Prints all S.M.A.R.T. information)\n"); - printf("\tsmartctl -X /dev/hda (Executes extended disk self-test)\n"); - printf("Please see the man pages or the web site for further information.\n"); + printf("\tsmartctl -X /dev/hda (Executes extended disk self-test)\n\n"); + printf("Please see the man pages or %s for further information.\n",PROJECTHOME); } @@ -131,7 +132,6 @@ void ParseOpts (int argc, char** argv){ driveinfo = TRUE; break; case CHECKSMART : - driveinfo = TRUE; checksmart = TRUE; break; case SMARTVERBOSEALL : @@ -174,21 +174,27 @@ void ParseOpts (int argc, char** argv){ break; case SMARTEXEOFFIMMEDIATE: smartexeoffimmediate = TRUE; + testcase=OFFLINE_FULL_SCAN; break; case SMARTSHORTSELFTEST : smartshortselftest = TRUE; + testcase=SHORT_SELF_TEST; break; case SMARTEXTENDSELFTEST : smartextendselftest = TRUE; + testcase=EXTEND_SELF_TEST; break; case SMARTSHORTCAPSELFTEST: smartshortcapselftest = TRUE; + testcase=SHORT_CAPTIVE_SELF_TEST; break; case SMARTEXTENDCAPSELFTEST: smartextendcapselftest = TRUE; + testcase=EXTEND_CAPTIVE_SELF_TEST; break; case SMARTSELFTESTABORT: smartselftestabort = TRUE; + testcase=ABORT_SELF_TEST; break; default: Usage(); @@ -197,9 +203,9 @@ void ParseOpts (int argc, char** argv){ if ( (smartexeoffimmediate + smartshortselftest + smartextendselftest + smartshortcapselftest + - smartextendcapselftest ) > 1){ + smartextendcapselftest +smartselftestabort ) > 1){ Usage(); - printf ("\n ERROR: smartctl can only run a single test at a time \n"); + printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n"); exit(-1); } } @@ -212,19 +218,20 @@ int main (int argc, char **argv){ char *device; printf("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n",RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION); - printf("Home page of smartctl is %s\n\n",PROJECTHOME); + printf("Home page of smartctl is %s\n",PROJECTHOME); // Part input arguments ParseOpts (argc,argv); // Print Copyright/License info if needed if (printcopyleft){ - printf("smartctl comes with ABSOLUTELY NO WARRANTY. This\n"); + printf("\nsmartctl comes with ABSOLUTELY NO WARRANTY. This\n"); printf("is free software, and you are welcome to redistribute it\n"); printf("under the terms of the GNU General Public License Version 2.\n"); printf("See http://www.gnu.org for further details.\n\n"); - printf("CVS version ID %s\n","$Id: smartctl.cpp,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $"); - exit(0); + printf("CVS version ID %s\n","$Id: smartctl.cpp,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $"); + if (argc==2) + exit(0); } // Further argument checking diff --git a/sm5/smartd.c b/sm5/smartd.c index 900cfe55da1c49a968e67b617901c4e6a0418d13..b593fbcb9180f638ae54e4958a7f8640d05e38ed 100644 --- a/sm5/smartd.c +++ b/sm5/smartd.c @@ -1,4 +1,4 @@ -// $Id: smartd.c,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: smartd.c,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $ /* * smartd.c * @@ -117,6 +117,10 @@ void atadevicescan ( atadevices_t *devices){ printout(LOG_INFO,"%s Found and is SMART capable\n",device); + // This makes NO sense. We may want to know if the drive supports + // Offline Surface Scan, for example. But checking if it supports + // self-tests seems useless. In any case, smartd NEVER uses this + // field anywhere... devices[numatadevices].selftest = isSupportSelfTest(devices[numatadevices].smartval); @@ -213,6 +217,8 @@ int ataCheckDevice( atadevices_t *drive){ if ((failed=ataCheckSmart(tempsmartval,tempsmartthres))) printout(LOG_CRIT,"Device: %s, Failed attribute: %i\n",drive->devicename,failed); + // WHEN IT WORKS, we should here add a call to ataSmartStatus2() + // see if any values have changed. Second argument is new values ataCompareSmartValues (drive , tempsmartval); @@ -272,7 +278,7 @@ char copyleftstring[]= "is free software, and you are welcome to redistribute it\n" "under the terms of the GNU General Public License Version 2.\n" "See http://www.gnu.org for further details.\n\n" -"CVS Version ID $Id: smartd.c,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $\n"; +"CVS Version ID $Id: smartd.c,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $\n"; const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'\0' }; diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp index d070985caca394a87426fca58c598cd3418cae6d..ce1aa9ff2fca8952d7aa55d37d206e0e15c2ce80 100644 --- a/sm5/smartd.cpp +++ b/sm5/smartd.cpp @@ -1,4 +1,4 @@ -// $Id: smartd.cpp,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $ +// $Id: smartd.cpp,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $ /* * smartd.c * @@ -117,6 +117,10 @@ void atadevicescan ( atadevices_t *devices){ printout(LOG_INFO,"%s Found and is SMART capable\n",device); + // This makes NO sense. We may want to know if the drive supports + // Offline Surface Scan, for example. But checking if it supports + // self-tests seems useless. In any case, smartd NEVER uses this + // field anywhere... devices[numatadevices].selftest = isSupportSelfTest(devices[numatadevices].smartval); @@ -213,6 +217,8 @@ int ataCheckDevice( atadevices_t *drive){ if ((failed=ataCheckSmart(tempsmartval,tempsmartthres))) printout(LOG_CRIT,"Device: %s, Failed attribute: %i\n",drive->devicename,failed); + // WHEN IT WORKS, we should here add a call to ataSmartStatus2() + // see if any values have changed. Second argument is new values ataCompareSmartValues (drive , tempsmartval); @@ -272,7 +278,7 @@ char copyleftstring[]= "is free software, and you are welcome to redistribute it\n" "under the terms of the GNU General Public License Version 2.\n" "See http://www.gnu.org for further details.\n\n" -"CVS Version ID $Id: smartd.cpp,v 1.9 2002/10/15 14:24:27 ballen4705 Exp $\n"; +"CVS Version ID $Id: smartd.cpp,v 1.10 2002/10/20 19:22:02 ballen4705 Exp $\n"; const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'\0' };