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