--- embedaddon/smartmontools/atacmds.cpp 2012/10/09 09:36:45 1.1.1.2 +++ embedaddon/smartmontools/atacmds.cpp 2013/07/22 01:17:35 1.1.1.3 @@ -4,7 +4,7 @@ * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002-11 Bruce Allen - * Copyright (C) 2008-12 Christian Franke + * Copyright (C) 2008-13 Christian Franke * Copyright (C) 1999-2000 Michael Cornwell * Copyright (C) 2000 Andre Hedrick * @@ -14,8 +14,7 @@ * 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. + * (for example COPYING); If not, see . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -36,7 +35,7 @@ #include "utility.h" #include "dev_ata_cmd_set.h" // for parsed_ata_device -const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp,v 1.1.1.2 2012/10/09 09:36:45 misho Exp $" +const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp,v 1.1.1.3 2013/07/22 01:17:35 misho Exp $" ATACMDS_H_CVSID; // Print ATA debug messages? @@ -57,104 +56,7 @@ bool dont_print_serial_number = false; #define SRET_STATUS_HI_EXCEEDED 0x2C #define SRET_STATUS_MID_EXCEEDED 0xF4 -// 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/Documents/UploadedDocuments/project/d2008r7b-ATA-3.pdf -// to see this. -#define NOVAL_0 0x0000 -#define NOVAL_1 0xffff -/* word 81: minor version number */ -#define MINOR_MAX 0x22 -static const char * const 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 */ - "ATA/ATAPI-7 T13 1532D revision 1", /* 0x001a */ - "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ - "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ - "ATA/ATAPI-7 published, ANSI INCITS 397-2005",/* 0x001d */ - "ATA/ATAPI-7 T13 1532D revision 0", /* 0x001e */ - "reserved", /* 0x001f */ - "reserved", /* 0x0020 */ - "ATA/ATAPI-7 T13 1532D revision 4a", /* 0x0021 */ - "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022 */ -}; -// 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 -static 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: */ - 7, /* 0x001a WARNING: */ - 6, /* 0x001b WARNING: */ - 6, /* 0x001c WARNING: */ - 7, /* 0x001d WARNING: */ - 7, /* 0x001e WARNING: */ - 0, /* 0x001f WARNING: */ - 0, /* 0x0020 WARNING: */ - 7, /* 0x0021 WARNING: */ - 6 /* 0x0022 WARNING: */ -}; - -// Compile time check of above array sizes -typedef char assert_sizeof_minor_str [sizeof(minor_str) /sizeof(minor_str[0]) == MINOR_MAX+1 ? 1 : -1]; -typedef char assert_sizeof_actual_ver[sizeof(actual_ver)/sizeof(actual_ver[0]) == MINOR_MAX+1 ? 1 : -1]; - // Get ID and increase flag of current pending or offline // uncorrectable attribute. unsigned char get_unc_attr_id(bool offline, const ata_vendor_attr_defs & defs, @@ -246,7 +148,7 @@ const char * map_old_vendor_opts[][2] = { {"198,increasing" , "198,raw48+,Total_Offl_Uncorrectabl"}, // '+' sets flag {"200,writeerrorcount" , "200,raw48,Write_Error_Count"}, {"201,detectedtacount" , "201,raw48,Detected_TA_Count"}, - {"220,temp" , "220,raw48,Temperature_Celsius"}, + {"220,temp" , "220,tempminmax,Temperature_Celsius"}, }; const unsigned num_old_vendor_opts = sizeof(map_old_vendor_opts)/sizeof(map_old_vendor_opts[0]); @@ -327,7 +229,7 @@ bool parse_attribute_def(const char * opt, ata_vendor_ defs[i].priority = priority; defs[i].raw_format = format; defs[i].flags = flags; - strcpy(defs[i].byteorder, byteorder); + snprintf(defs[i].byteorder, sizeof(defs[i].byteorder), "%s", byteorder); } } else if (defs[id].priority <= priority) { @@ -337,7 +239,7 @@ bool parse_attribute_def(const char * opt, ata_vendor_ defs[id].raw_format = format; defs[id].priority = priority; defs[id].flags = flags; - strcpy(defs[id].byteorder, byteorder); + snprintf(defs[id].byteorder, sizeof(defs[id].byteorder), "%s", byteorder); } return true; @@ -359,6 +261,35 @@ std::string create_vendor_attribute_arg_list() return s; } + +// Parse firmwarebug def (-F option). +// Return false on error. +bool parse_firmwarebug_def(const char * opt, firmwarebug_defs & firmwarebugs) +{ + if (!strcmp(opt, "none")) + firmwarebugs.set(BUG_NONE); + else if (!strcmp(opt, "nologdir")) + firmwarebugs.set(BUG_NOLOGDIR); + else if (!strcmp(opt, "samsung")) + firmwarebugs.set(BUG_SAMSUNG); + else if (!strcmp(opt, "samsung2")) + firmwarebugs.set(BUG_SAMSUNG2); + else if (!strcmp(opt, "samsung3")) + firmwarebugs.set(BUG_SAMSUNG3); + else if (!strcmp(opt, "xerrorlba")) + firmwarebugs.set(BUG_XERRORLBA); + else + return false; + return true; +} + +// Return a string of valid argument words for parse_firmwarebug_def() +const char * get_valid_firmwarebug_args() +{ + return "none, nologdir, samsung, samsung2, samsung3, xerrorlba"; +} + + // swap two bytes. Point to low address void swap2(char *location){ char tmp=*location; @@ -433,17 +364,18 @@ static const char * const commandstrings[]={ }; -static const char * preg(const ata_register & r, char * buf) +static const char * preg(const ata_register & r, char (& buf)[8]) { if (!r.is_set()) //return "n/a "; return "...."; - sprintf(buf, "0x%02x", r.val()); return buf; + snprintf(buf, sizeof(buf), "0x%02x", r.val()); + return buf; } static void print_regs(const char * prefix, const ata_in_regs & r, const char * suffix = "\n") { - char bufs[7][4+1+13]; + char bufs[7][8]; pout("%s FR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, CMD=%s%s", prefix, preg(r.features, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), @@ -452,7 +384,7 @@ static void print_regs(const char * prefix, const ata_ static void print_regs(const char * prefix, const ata_out_regs & r, const char * suffix = "\n") { - char bufs[7][4+1+13]; + char bufs[7][8]; pout("%sERR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, STS=%s%s", prefix, preg(r.error, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]), preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]), @@ -672,7 +604,7 @@ int smartcommandhandler(ata_device * device, smart_com } else { // We haven't gotten output that makes sense; print out some debugging info - pout("Error SMART Status command failed\n"); + pout("SMART Status command failed\n"); pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE); pout("Register values returned from SMART Status command are:\n"); print_regs(" ", out.out_regs); @@ -883,7 +815,8 @@ bool ata_set_features(ata_device * device, unsigned ch // capable). The value of the integer helps identify the type of // Packet device, which is useful so that the user can connect the // formal device number with whatever object is inside their computer. -int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id) +int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_swapped_id, + unsigned char * raw_buf /* = 0 */) { unsigned short *rawshort=(unsigned short *)buf; unsigned char *rawbyte =(unsigned char *)buf; @@ -909,6 +842,10 @@ int ata_read_identity(ata_device * device, ata_identif swap2((char *)(buf->model+i)); } + // If requested, save raw data before endianness adjustments + if (raw_buf) + memcpy(raw_buf, buf, sizeof(*buf)); + #ifndef __NetBSD__ // if machine is big-endian, swap byte order as needed // NetBSD kernel delivers IDENTIFY data in host byte order @@ -963,68 +900,6 @@ int ata_read_identity(ata_device * device, ata_identif 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, const ata_identify_device * drive, unsigned short * minor) -{ - // get major and minor ATA revision numbers - unsigned short major = drive->major_rev_num; - *minor=drive->minor_rev_num; - - // First check if device has ANY ATA version information in it - if (major==NOVAL_0 || major==NOVAL_1) { - *description=NULL; - return 0; // No info found - } - - // 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; - } - } - - // Try new ATA-8 ACS minor revision numbers. - // Table 55 of T13/2015-D Revision 4a (ACS-2), December 9, 2010. - // (not in actual_ver/minor_str to avoid large sparse tables) - const char *desc; - switch (*minor) { - case 0x0027: desc = "ATA-8-ACS revision 3c"; break; - case 0x0028: desc = "ATA-8-ACS revision 6"; break; - case 0x0029: desc = "ATA-8-ACS revision 4"; break; - case 0x0031: desc = "ACS-2 revision 2"; break; - case 0x0033: desc = "ATA-8-ACS revision 3e"; break; - case 0x0039: desc = "ATA-8-ACS revision 4c"; break; - case 0x0042: desc = "ATA-8-ACS revision 3f"; break; - case 0x0052: desc = "ATA-8-ACS revision 3b"; break; - case 0x0107: desc = "ATA-8-ACS revision 2d"; break; - case 0x0110: desc = "ACS-2 revision 3"; break; - default: desc = 0; break; - } - if (desc) { - *description = desc; - return 8; - } - - // 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. - int i; - for (i=15; i>0; i--) - if (major & (0x1<> 12); } +// Get nominal media rotation rate. +// Returns: 0 = not reported, 1 = SSD, >1 = HDD rpm, < 0 = -(Unknown value) +int ata_get_rotation_rate(const ata_identify_device * id) +{ + // Table 37 of T13/1699-D (ATA8-ACS) Revision 6a, September 6, 2008 + // Table A.31 of T13/2161-D (ACS-3) Revision 3b, August 25, 2012 + unsigned short word217 = id->words088_255[217-88]; + if (word217 == 0x0000 || word217 == 0xffff) + return 0; + else if (word217 == 0x0001) + return 1; + else if (word217 > 0x0400) + return word217; + else + return -(int)word217; +} + // returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell int ataSmartSupport(const ata_identify_device * drive) { @@ -1082,7 +974,6 @@ int ataIsSmartEnabled(const ata_identify_device * driv int ataReadSmartValues(ata_device * device, struct ata_smart_values *data){ if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){ - pout("Error SMART Values Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1127,12 +1018,11 @@ static void fixsamsungselftestlog(ata_smart_selftestlo // Reads the Self Test Log (log #6) int ataReadSelfTestLog (ata_device * device, ata_smart_selftestlog * data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){ - pout("Error SMART Error Self-Test Log Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1141,7 +1031,7 @@ int ataReadSelfTestLog (ata_device * device, ata_smart checksumwarning("SMART Self-Test Log Structure"); // fix firmware bugs in self-test log - if (fix_firmwarebug == FIX_SAMSUNG) + if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungselftestlog(data); // swap endian order if needed @@ -1272,7 +1162,6 @@ int ataReadSelectiveSelfTestLog(ata_device * device, s // get data from device if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){ - pout("Error SMART Read Selective Self-Test Log failed: %s\n", device->get_errmsg()); return -1; } @@ -1294,9 +1183,6 @@ int ataReadSelectiveSelfTestLog(ata_device * device, s swap2((char *)&(data->pendingtime)); } - if (data->logversion != 1) - pout("Note: selective self-test log revision number (%d) not 1 implies that no selective self-test has ever been run\n", data->logversion); - return 0; } @@ -1315,6 +1201,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log sstlog, *data=&sstlog; unsigned char *ptr=(unsigned char *)data; if (ataReadSelectiveSelfTestLog(device, data)) { + pout("SMART Read Selective Self-test Log failed: %s\n", device->get_errmsg()); pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n"); return -1; } @@ -1325,7 +1212,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, // Host is NOT allowed to write selective self-test log if a selective // self-test is in progress. if (0currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) { - pout("Error SMART Selective or other Self-Test in progress.\n"); + pout("SMART Selective or other Self-test in progress\n"); return -4; } @@ -1470,7 +1357,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, // write new selective self-test log if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){ - pout("Error Write Selective Self-Test Log failed: %s\n", device->get_errmsg()); + pout("Write Selective Self-test Log failed: %s\n", device->get_errmsg()); return -3; } @@ -1512,12 +1399,11 @@ static void fixsamsungerrorlog2(ata_smart_errorlog * d // Error Log is #2, and the Extended Comprehensive SMART Error log is // #3 int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { // get data from device if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){ - pout("Error SMART Error Log Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1527,9 +1413,9 @@ int ataReadErrorLog (ata_device * device, ata_smart_er // Some disks have the byte order reversed in some SMART Summary // Error log entries - if (fix_firmwarebug == FIX_SAMSUNG) + if (firmwarebugs.is_set(BUG_SAMSUNG)) fixsamsungerrorlog(data); - else if (fix_firmwarebug == FIX_SAMSUNG2) + else if (firmwarebugs.is_set(BUG_SAMSUNG2)) fixsamsungerrorlog2(data); // swap endian order if needed @@ -1553,9 +1439,34 @@ int ataReadErrorLog (ata_device * device, ata_smart_er return 0; } + +// Fix LBA byte ordering of Extended Comprehensive Error Log +// if little endian instead of ATA register ordering is provided +template +static inline void fix_exterrlog_lba_cmd(T & cmd) +{ + T org = cmd; + cmd.lba_mid_register_hi = org.lba_high_register; + cmd.lba_low_register_hi = org.lba_mid_register_hi; + cmd.lba_high_register = org.lba_mid_register; + cmd.lba_mid_register = org.lba_low_register_hi; +} + +static void fix_exterrlog_lba(ata_smart_exterrlog * log, unsigned nsectors) +{ + for (unsigned i = 0; i < nsectors; i++) { + for (int ei = 0; ei < 4; ei++) { + ata_smart_exterrlog_error_log & entry = log[i].error_logs[ei]; + fix_exterrlog_lba_cmd(entry.error); + for (int ci = 0; ci < 5; ci++) + fix_exterrlog_lba_cmd(entry.commands[ci]); + } + } +} + // Read Extended Comprehensive Error Log bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log, - unsigned nsectors) + unsigned nsectors, firmwarebug_defs firmwarebugs) { if (!ataReadLogExt(device, 0x03, 0x00, 0, log, nsectors)) return false; @@ -1573,6 +1484,9 @@ bool ataReadExtErrorLog(ata_device * device, ata_smart } } + if (firmwarebugs.is_set(BUG_XERRORLBA)) + fix_exterrlog_lba(log, nsectors); + return true; } @@ -1581,7 +1495,6 @@ int ataReadSmartThresholds (ata_device * device, struc // get data from device if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){ - pout("Error SMART Thresholds Read failed: %s\n", device->get_errmsg()); return -1; } @@ -1598,7 +1511,6 @@ int ataReadSmartThresholds (ata_device * device, struc int ataEnableSmart (ata_device * device ){ if (smartcommandhandler(device, ENABLE, 0, NULL)){ - pout("Error SMART Enable failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1607,7 +1519,6 @@ int ataEnableSmart (ata_device * device ){ int ataDisableSmart (ata_device * device ){ if (smartcommandhandler(device, DISABLE, 0, NULL)){ - pout("Error SMART Disable failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1615,7 +1526,6 @@ int ataDisableSmart (ata_device * device ){ int ataEnableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){ - pout("Error SMART Enable Auto-save failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1624,7 +1534,6 @@ int ataEnableAutoSave(ata_device * device){ int ataDisableAutoSave(ata_device * device){ if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){ - pout("Error SMART Disable Auto-save failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1638,7 +1547,6 @@ int ataEnableAutoOffline (ata_device * device){ /* timer hard coded to 4 hours */ if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ - pout("Error SMART Enable Automatic Offline failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1649,7 +1557,6 @@ int ataEnableAutoOffline (ata_device * device){ int ataDisableAutoOffline (ata_device * device){ if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){ - pout("Error SMART Disable Automatic Offline failed: %s\n", device->get_errmsg()); return -1; } return 0; @@ -1730,11 +1637,11 @@ int ataSmartTest(ata_device * device, int testtype, bo // 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"); + snprintf(cmdmsg, sizeof(cmdmsg), "Abort SMART off-line mode self-test routine"); else if (!type) - sprintf(cmdmsg, "SMART EXECUTE OFF-LINE IMMEDIATE subcommand 0x%02x", testtype); + snprintf(cmdmsg, sizeof(cmdmsg), "SMART EXECUTE OFF-LINE IMMEDIATE subcommand 0x%02x", testtype); else - sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive); + snprintf(cmdmsg, sizeof(cmdmsg), "Execute SMART %s routine immediately in %s mode", type, captive); pout("Sending command: \"%s\".\n",cmdmsg); if (select) { @@ -2195,8 +2102,13 @@ std::string ata_format_attr_raw_value(const ata_smart_ // Attribute names shouldn't be longer than 23 chars, otherwise they break the // output of smartctl. -static const char * get_default_attr_name(unsigned char id) +static const char * get_default_attr_name(unsigned char id, int rpm) { + bool hdd = (rpm > 1), ssd = (rpm == 1); + + static const char Unknown_HDD_Attribute[] = "Unknown_HDD_Attribute"; + static const char Unknown_SSD_Attribute[] = "Unknown_SSD_Attribute"; + switch (id) { case 1: return "Raw_Read_Error_Rate"; @@ -2209,36 +2121,48 @@ static const char * get_default_attr_name(unsigned cha case 5: return "Reallocated_Sector_Ct"; case 6: + if (ssd) return Unknown_SSD_Attribute; return "Read_Channel_Margin"; case 7: + if (ssd) return Unknown_SSD_Attribute; return "Seek_Error_Rate"; case 8: + if (ssd) return Unknown_SSD_Attribute; return "Seek_Time_Performance"; case 9: return "Power_On_Hours"; case 10: + if (ssd) return Unknown_SSD_Attribute; return "Spin_Retry_Count"; case 11: + if (ssd) return Unknown_SSD_Attribute; return "Calibration_Retry_Count"; case 12: return "Power_Cycle_Count"; case 13: return "Read_Soft_Error_Rate"; case 175: + if (hdd) return Unknown_HDD_Attribute; return "Program_Fail_Count_Chip"; case 176: + if (hdd) return Unknown_HDD_Attribute; return "Erase_Fail_Count_Chip"; case 177: + if (hdd) return Unknown_HDD_Attribute; return "Wear_Leveling_Count"; case 178: + if (hdd) return Unknown_HDD_Attribute; return "Used_Rsvd_Blk_Cnt_Chip"; case 179: + if (hdd) return Unknown_HDD_Attribute; return "Used_Rsvd_Blk_Cnt_Tot"; case 180: + if (hdd) return Unknown_HDD_Attribute; return "Unused_Rsvd_Blk_Cnt_Tot"; case 181: return "Program_Fail_Cnt_Total"; case 182: + if (hdd) return Unknown_HDD_Attribute; return "Erase_Fail_Count_Total"; case 183: return "Runtime_Bad_Block"; @@ -2249,6 +2173,7 @@ static const char * get_default_attr_name(unsigned cha case 188: return "Command_Timeout"; case 189: + if (ssd) return Unknown_SSD_Attribute; return "High_Fly_Writes"; case 190: // Western Digital uses this for temperature. @@ -2260,10 +2185,12 @@ static const char * get_default_attr_name(unsigned cha // 55C sometime in the past. return "Airflow_Temperature_Cel"; case 191: + if (ssd) return Unknown_SSD_Attribute; return "G-Sense_Error_Rate"; case 192: return "Power-Off_Retract_Count"; case 193: + if (ssd) return Unknown_SSD_Attribute; return "Load_Cycle_Count"; case 194: return "Temperature_Celsius"; @@ -2279,11 +2206,14 @@ static const char * get_default_attr_name(unsigned cha case 199: return "UDMA_CRC_Error_Count"; case 200: + if (ssd) return Unknown_SSD_Attribute; // Western Digital return "Multi_Zone_Error_Rate"; case 201: + if (ssd) return Unknown_SSD_Attribute; return "Soft_Read_Error_Rate"; case 202: + if (ssd) return Unknown_SSD_Attribute; // Fujitsu: "TA_Increase_Count" return "Data_Address_Mark_Errs"; case 203: @@ -2298,36 +2228,49 @@ static const char * get_default_attr_name(unsigned cha return "Thermal_Asperity_Rate"; case 206: // Fujitsu + if (ssd) return Unknown_SSD_Attribute; return "Flying_Height"; case 207: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Spin_High_Current"; case 208: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Spin_Buzz"; case 209: // Maxtor + if (ssd) return Unknown_SSD_Attribute; return "Offline_Seek_Performnce"; case 220: + if (ssd) return Unknown_SSD_Attribute; return "Disk_Shift"; case 221: + if (ssd) return Unknown_SSD_Attribute; return "G-Sense_Error_Rate"; case 222: + if (ssd) return Unknown_SSD_Attribute; return "Loaded_Hours"; case 223: + if (ssd) return Unknown_SSD_Attribute; return "Load_Retry_Count"; case 224: + if (ssd) return Unknown_SSD_Attribute; return "Load_Friction"; case 225: + if (ssd) return Unknown_SSD_Attribute; return "Load_Cycle_Count"; case 226: + if (ssd) return Unknown_SSD_Attribute; return "Load-in_Time"; case 227: + if (ssd) return Unknown_SSD_Attribute; return "Torq-amp_Count"; case 228: return "Power-off_Retract_Count"; case 230: // seen in IBM DTPA-353750 + if (ssd) return Unknown_SSD_Attribute; return "Head_Amplitude"; case 231: return "Temperature_Celsius"; @@ -2336,8 +2279,10 @@ static const char * get_default_attr_name(unsigned cha return "Available_Reservd_Space"; case 233: // seen in Intel X25-E SSD + if (hdd) return Unknown_HDD_Attribute; return "Media_Wearout_Indicator"; case 240: + if (ssd) return Unknown_SSD_Attribute; return "Head_Flying_Hours"; case 241: return "Total_LBAs_Written"; @@ -2346,6 +2291,7 @@ static const char * get_default_attr_name(unsigned cha case 250: return "Read_Error_Retry_Rate"; case 254: + if (ssd) return Unknown_SSD_Attribute; return "Free_Fall_Sensor"; default: return "Unknown_Attribute"; @@ -2353,12 +2299,13 @@ static const char * get_default_attr_name(unsigned cha } // Get attribute name -std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs) +std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs, + int rpm /* = 0 */) { if (!defs[id].name.empty()) return defs[id].name; else - return get_default_attr_name(id); + return get_default_attr_name(id, rpm); } // Find attribute index for attribute id, -1 if not found. @@ -2377,11 +2324,11 @@ int ata_find_attr_index(unsigned char id, const ata_sm // non-default interpretations. If the Attribute does not exist, return 0 unsigned char ata_return_temperature_value(const ata_smart_values * data, const ata_vendor_attr_defs & defs) { - for (int i = 0; i < 3; i++) { - static const unsigned char ids[3] = {194, 9, 220}; + for (int i = 0; i < 4; i++) { + static const unsigned char ids[4] = {194, 190, 9, 220}; unsigned char id = ids[i]; const ata_attr_raw_format format = defs[id].raw_format; - if (!( (id == 194 && format == RAWFMT_DEFAULT) + if (!( ((id == 194 || id == 190) && format == RAWFMT_DEFAULT) || format == RAWFMT_TEMPMINMAX || format == RAWFMT_TEMP10X)) continue; int idx = ata_find_attr_index(id, *data); @@ -2409,7 +2356,7 @@ int ataReadSCTStatus(ata_device * device, ata_sct_stat // read SCT status via SMART log 0xe0 memset(sts, 0, sizeof(*sts)); if (smartcommandhandler(device, READ_LOG, 0xe0, (char *)sts)){ - pout("Error Read SCT Status failed: %s\n", device->get_errmsg()); + pout("Read SCT Status failed: %s\n", device->get_errmsg()); return -1; } @@ -2427,7 +2374,7 @@ int ataReadSCTStatus(ata_device * device, ata_sct_stat // Check format version if (!(sts->format_version == 2 || sts->format_version == 3)) { - pout("Error unknown SCT Status format version %u, should be 2 or 3.\n", sts->format_version); + pout("Unknown SCT Status format version %u, should be 2 or 3.\n", sts->format_version); return -1; } return 0; @@ -2464,14 +2411,14 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_te // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ - pout("Error Write SCT Data Table command failed: %s\n", device->get_errmsg()); + pout("Write SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } // read SCT data via SMART log page 0xe1 memset(tmh, 0, sizeof(*tmh)); if (smartcommandhandler(device, READ_LOG, 0xe1, (char *)tmh)){ - pout("Error Read SCT Data Table failed: %s\n", device->get_errmsg()); + pout("Read SCT Data Table failed: %s\n", device->get_errmsg()); return -1; } @@ -2480,7 +2427,7 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_te return -1; if (!(sts->ext_status_code == 0 && sts->action_code == 5 && sts->function_code == 1)) { - pout("Error unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts->ext_status_code, sts->action_code, sts->function_code); return -1; } @@ -2490,13 +2437,9 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_te swapx(&tmh->format_version); swapx(&tmh->sampling_period); swapx(&tmh->interval); + swapx(&tmh->cb_index); + swapx(&tmh->cb_size); } - - // Check format version - if (tmh->format_version != 2) { - pout("Error unknown SCT Temperature History Format Version (%u), should be 2.\n", tmh->format_version); - return -1; - } return 0; } @@ -2535,7 +2478,7 @@ int ataSetSCTTempInterval(ata_device * device, unsigne // write command via SMART log page 0xe0 if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ - pout("Error Write SCT Feature Control Command failed: %s\n", device->get_errmsg()); + pout("Write SCT Feature Control Command failed: %s\n", device->get_errmsg()); return -1; } @@ -2544,7 +2487,7 @@ int ataSetSCTTempInterval(ata_device * device, unsigne return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 4 && sts.function_code == 1)) { - pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } @@ -2599,7 +2542,7 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_de ata_cmd_out out; if (!device->ata_pass_through(in, out)) { - pout("Error Write SCT (%cet) Error Recovery Control Command failed: %s\n", + pout("Write SCT (%cet) Error Recovery Control Command failed: %s\n", (!set ? 'G' : 'S'), device->get_errmsg()); return -1; } @@ -2609,7 +2552,7 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_de return -1; if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == (set ? 1 : 2))) { - pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + pout("Unexpected SCT status 0x%04x (action_code=%u, function_code=%u)\n", sts.ext_status_code, sts.action_code, sts.function_code); return -1; } @@ -2619,9 +2562,16 @@ static int ataGetSetSCTErrorRecoveryControltime(ata_de if (!(out.out_regs.sector_count.is_set() && out.out_regs.lba_low.is_set())) { // TODO: Output register support should be checked within each ata_pass_through() // implementation before command is issued. - pout("Error SMART WRITE LOG does not return COUNT and LBA_LOW register\n"); + pout("SMART WRITE LOG does not return COUNT and LBA_LOW register\n"); return -1; } + if ( out.out_regs.sector_count == in.in_regs.sector_count + && out.out_regs.lba_low == in.in_regs.lba_low ) { + // 0xe001 (5734.5s) - this is most likely a broken ATA pass-through implementation + pout("SMART WRITE LOG returns COUNT and LBA_LOW register unchanged\n"); + return -1; + } + // Return value to caller time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8); } @@ -2713,8 +2663,9 @@ int ataPrintSmartSelfTestEntry(unsigned testnum, unsig char msglba[32]; if (retval < 0 && failing_lba < 0xffffffffffffULL) snprintf(msglba, sizeof(msglba), "%"PRIu64, failing_lba); - else - strcpy(msglba, "-"); + else { + msglba[0] = '-'; msglba[1] = 0; + } pout("#%2u %-19s %-29s %1d0%% %8u %s\n", testnum, msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba); @@ -2727,11 +2678,11 @@ int ataPrintSmartSelfTestEntry(unsigned testnum, unsig // bottom 8 bits: number of entries found where self-test showed an error // remaining bits: if nonzero, power on hours of last self-test where error was found int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * data, bool allentries, - unsigned char fix_firmwarebug) + firmwarebug_defs firmwarebugs) { if (allentries) pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber); - if ((data->revnumber!=0x0001) && allentries && fix_firmwarebug != FIX_SAMSUNG) + if (data->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_SAMSUNG)) pout("Warning: ATA Specification requires self-test log structure revision number = 1\n"); if (data->mostrecenttest==0){ if (allentries)