diff -bw -N -r -c smartmontools-5.39/atacmds.cpp smartmontools-5.39/atacmds.cpp *** smartmontools-5.39/atacmds.cpp 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/atacmds.cpp 2009-11-20 23:37:53.000000000 +0000 *************** *** 469,475 **** // the commands that failed clearly return empty (zeroed) data // structures if (getsdata) { ! if (command==CHECK_POWER_MODE) data[0]=0; else memset(data, '\0', 512); --- 469,475 ---- // the commands that failed clearly return empty (zeroed) data // structures if (getsdata) { ! if (command==CHECK_POWER_MODE || command==WRITE_LOG) data[0]=0; else memset(data, '\0', 512); *************** *** 526,531 **** --- 526,532 ---- in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR; in.in_regs.lba_low = select; in.set_data_out(data, 1); + in.out_needed.sector_count = in.out_needed.lba_low = true; // Error Recovery Control returns current value here, s_c = LSB, lba_l = MSB break; case ENABLE: in.in_regs.features = ATA_SMART_ENABLE; *************** *** 575,580 **** --- 576,591 ---- default: retval = 0; break; + case WRITE_LOG: + data[0] = out.out_regs.error; // These are only for Error Recovery Control + data[1] = out.out_regs.sector_count; // should be LSB of ERC deciseconds time + data[2] = out.out_regs.lba_low; // should be MSB of ERC deciseconds time + data[3] = out.out_regs.lba_mid; // should be 0 + data[4] = out.out_regs.lba_high; // should be 0 + // dont bother with device/head + data[5] = out.out_regs.status; // should be 0x50 + retval = 0; + break; case CHECK_POWER_MODE: data[0] = out.out_regs.sector_count; retval = 0; *************** *** 2260,2265 **** --- 2271,2374 ---- return 0; } + // Get SCT Error Recovery Control + int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short *timer) + { + // Check initial status + ata_sct_status_response sts; + if (ataReadSCTStatus(device, &sts)) + return -1; + + // Do nothing if other SCT command is executing + if (sts.ext_status_code == 0xffff) { + pout("Another SCT command is executing, abort Feature Control\n" + "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + + ata_sct_error_recovery_control_command cmd; memset(&cmd, 0, sizeof(cmd)); + // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) + cmd.action_code = 3; // Error Recovery Control command + cmd.function_code = 2; // Get timer (1 is set timer) + cmd.selection_code= type; // 1=Read timer, 2=Write timer + + // write command via SMART log page 0xe0 + if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ // this has access to the result of a would be Get + syserror("Error Write SCT Error Recovery Control Command failed"); + return -1; + } + + unsigned char *res=(unsigned char*)&cmd; + // res[0] = Error => Should be 0 + // res[1] = SectCount => LSB ERC time + // res[2] = LBA_Low => MSB ERC time + // res[3] = _Mid => 0 + // res[4] = _High=> 0 + // res[5] = Status => should be 0x50 + + if ( res[0] != 0 || res[5] != 0x50 ) { + pout("Error unexcepted SCT ERC status 0x%02x, Extended Error Code 0x%04x (action_code=3, function_code=2)\n",res[5], (res[2]<<8) | res[1] ); + return -1; + } + + *timer=res[1] | (((unsigned short)res[2])<<8); // pass result to caller + + // re-read and check SCT status + if (ataReadSCTStatus(device, &sts)) + return -1; + + if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == 2)) { + pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + return 0; + } + + // Set SCT Error Recovery Control + int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned interval) + { + // Check initial status + ata_sct_status_response sts; + if (ataReadSCTStatus(device, &sts)) + return -1; + + // Do nothing if other SCT command is executing + if (sts.ext_status_code == 0xffff) { + pout("Another SCT command is executing, abort Feature Control\n" + "(SCT ext_status_code 0x%04x, action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + + ata_sct_error_recovery_control_command cmd; memset(&cmd, 0, sizeof(cmd)); + // CAUTION: DO NOT CHANGE THIS VALUE (SOME ACTION CODES MAY ERASE DISK) + cmd.action_code = 3; // Error Recovery Control command + cmd.function_code = 1; // Set timer (2 is get timer) + cmd.selection_code= type; // 1=Read timer, 2=Write timer + cmd.value = interval; // in deciseconds + + // write command via SMART log page 0xe0 + if (smartcommandhandler(device, WRITE_LOG, 0xe0, (char *)&cmd)){ // this has access to the result of a would be Get + syserror("Error Write SCT Error Recovery Control Command failed"); + return -1; + } + + // re-read and check SCT status + if (ataReadSCTStatus(device, &sts)) + return -1; + + //pout("Set status code 0x%04x\n",sts.ext_status_code); + + if (!(sts.ext_status_code == 0 && sts.action_code == 3 && sts.function_code == 1)) { + pout("Error unexcepted SCT status 0x%04x (action_code=%u, function_code=%u)\n", + sts.ext_status_code, sts.action_code, sts.function_code); + return -1; + } + return 0; + } + // Print one self-test log entry. // Returns true if self-test showed an error. bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type, diff -bw -N -r -c smartmontools-5.39/atacmds.h smartmontools-5.39/atacmds.h *** smartmontools-5.39/atacmds.h 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/atacmds.h 2009-11-12 23:05:56.000000000 +0000 *************** *** 558,563 **** --- 558,578 ---- #pragma pack() ASSERT_SIZEOF_STRUCT(ata_sct_feature_control_command, 512); + // SCT Error Recovery Control command (send with SMART_WRITE_LOG page 0xe0) added by greg + // Hitachi docs for 2TB, pointing to ATA spec. + #pragma pack(1) + struct ata_sct_error_recovery_control_command + { + unsigned short action_code; // 3 = Error Recovery Control + unsigned short function_code; // 1 = Set, 2 = Return + unsigned short selection_code; // 1 = Read Timer, 2 = Write Timer + unsigned short value; // deciseconds for Set + unsigned short words004_255[252]; // reserved + } ATTR_PACKED; + #pragma pack() + ASSERT_SIZEOF_STRUCT(ata_sct_error_recovery_control_command, 512); + + // SCT Data Table command (send with SMART_WRITE_LOG page 0xe0) // Table 73 of T13/1699-D Revision 3f #pragma pack(1) *************** *** 725,730 **** --- 740,749 ---- // Set SCT temperature logging interval int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent); + // Read/Set SCT Error Recovery Control + int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short *timer); + int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned interval); + /* Enable/Disable SMART on device */ int ataEnableSmart (ata_device * device); *************** *** 787,798 **** --- 806,821 ---- inline bool isSCTCapable(const ata_identify_device *drive) { return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support + inline bool isSCTErrorRecoveryControlCapable(const ata_identify_device *drive) + { return ((drive->words088_255[206-88] & 0x9) == 0x9); } // 0x08 = SCT Error Recovery Control support + inline bool isSCTFeatureControlCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x11) == 0x11); } // 0x10 = SCT Feature Control support inline bool isSCTDataTableCapable(const ata_identify_device *drive) { return ((drive->words088_255[206-88] & 0x21) == 0x21); } // 0x20 = SCT Data Table support + int ataSmartTest(ata_device * device, int testtype, const ata_selective_selftest_args & args, const ata_smart_values * sv, uint64_t num_sectors); Binary files smartmontools-5.39/atacmds.o and smartmontools-5.39/atacmds.o differ diff -bw -N -r -c smartmontools-5.39/ataprint.cpp smartmontools-5.39/ataprint.cpp *** smartmontools-5.39/ataprint.cpp 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/ataprint.cpp 2009-11-20 01:32:00.000000000 +0000 *************** *** 876,881 **** --- 876,883 ---- if (!(sctcaps & 0x01)) return; pout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps); + if (sctcaps & 0x08) + pout("\t\t\t\t\tSCT Error Recovery Control supported.\n"); if (sctcaps & 0x10) pout("\t\t\t\t\tSCT Feature Control supported.\n"); if (sctcaps & 0x20) *************** *** 1685,1690 **** --- 1687,1704 ---- return 0; } + // Print SCT Error Recovery Control timers + static int ataPrintSCTErrorRecoveryControl( unsigned short read_timer, unsigned short write_timer ) + { + pout("SCT Error Recovery Control:\n"); + pout(" Read: "); + if(read_timer==0) pout("Disabled\n"); else pout("%6d (%0.1f seconds)\n",read_timer, ((float)read_timer)/10 ); + pout(" Write: "); + if(write_timer==0) pout("Disabled\n"); else pout("%6d (%0.1f seconds)\n",write_timer, ((float)write_timer)/10 ); + + return 0; + } + // Compares failure type to policy in effect, and either exits or // simply returns to the calling routine. *************** *** 2333,2338 **** --- 2347,2392 ---- } } + if (options.sct_erc_set) { + if (!isSCTCapable(&drive)) { + pout("Warning: device does not support SCT Commands\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else if (!isSCTErrorRecoveryControlCapable(&drive)) { + pout("Warning: device does not support SCT Error Recovery Control command\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else { + if( ataSetSCTErrorRecoveryControltime(device, 1, options.sct_erc_readtime) || + ataSetSCTErrorRecoveryControltime(device, 2, options.sct_erc_writetime) ) + { + pout("Warning: device does not support SCT (Set) Error Recovery Control command\n"); + pout("Suggest common arguments: scterc,70,70 to enable ERC or sct,0,0 to disable\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + } + } + + if (options.sct_erc_get || (options.sct_erc_set && !(returnval&FAILSMART)) ) { + if (!isSCTCapable(&drive)) { + pout("Warning: device does not support SCT Commands\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else if (!isSCTErrorRecoveryControlCapable(&drive)) { + pout("Warning: device does not support SCT Error Recovery Control command\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } else { + unsigned short read_timer, write_timer; + if( ataGetSCTErrorRecoveryControltime(device, 1, &read_timer) || + ataGetSCTErrorRecoveryControltime(device, 2, &write_timer) ) + { + pout("Warning: device does not support SCT (Get) Error Recovery Control command\n"); + failuretest(OPTIONAL_CMD, returnval|=FAILSMART); + } + else + { + ataPrintSCTErrorRecoveryControl( read_timer, write_timer ); + } + } + } + // Print SATA Phy Event Counters if (options.sataphy) { unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true); diff -bw -N -r -c smartmontools-5.39/ataprint.h smartmontools-5.39/ataprint.h *** smartmontools-5.39/ataprint.h 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/ataprint.h 2009-11-12 23:11:55.000000000 +0000 *************** *** 63,68 **** --- 63,71 ---- std::vector log_requests; bool sct_temp_sts, sct_temp_hist; + bool sct_erc_get; + bool sct_erc_set; + int sct_erc_readtime, sct_erc_writetime; bool sataphy, sataphy_reset; bool smart_disable, smart_enable; *************** *** 97,102 **** --- 100,108 ---- smart_ext_selftest_log(0), retry_error_log(false), retry_selftest_log(false), sct_temp_sts(false), sct_temp_hist(false), + sct_erc_get(false), + sct_erc_set(false), + sct_erc_readtime(-1), sct_erc_writetime(-1), sataphy(false), sataphy_reset(false), smart_disable(false), smart_enable(false), smart_auto_offl_disable(false), smart_auto_offl_enable(false), diff -bw -N -r -c smartmontools-5.39/dev_ata_cmd_set.cpp smartmontools-5.39/dev_ata_cmd_set.cpp *** smartmontools-5.39/dev_ata_cmd_set.cpp 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/dev_ata_cmd_set.cpp 2009-11-20 01:13:05.000000000 +0000 *************** *** 105,110 **** --- 105,118 ---- } switch (command) { + case WRITE_LOG: + out.out_regs.error = data[0+4]; + out.out_regs.sector_count = data[1+4]; + out.out_regs.lba_low = data[2+4]; + out.out_regs.lba_mid = data[3+4]; + out.out_regs.lba_high = data[4+4]; + out.out_regs.status = data[6+4]; + break; case CHECK_POWER_MODE: out.out_regs.sector_count = data[0]; break; diff -bw -N -r -c smartmontools-5.39/os_linux.cpp smartmontools-5.39/os_linux.cpp *** smartmontools-5.39/os_linux.cpp 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/os_linux.cpp 2009-11-20 01:22:12.000000000 +0000 *************** *** 375,380 **** --- 375,384 ---- pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); return -1; } + + // task request structure back to user data + memcpy(data,task, 512); + return 0; } *************** *** 2499,2505 **** return -1; } ! if (command==WRITE_LOG) { unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512]; unsigned int *hpt = (unsigned int *)task; ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]); --- 2503,2546 ---- return -1; } ! if( command==WRITE_LOG && select==0xe0) { // handle reading specific log page ! int retval; ! unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512]; ! unsigned int *hpt = (unsigned int *)task; ! ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]); ! task_struct_t *taskfile = (task_struct_t *)reqtask->io_ports; ! ! memset(task, 0, sizeof(task)); ! ! hpt[0] = m_hpt_data[0]; // controller id ! hpt[1] = m_hpt_data[1]; // channel number ! hpt[3] = m_hpt_data[2]; // pmport number ! hpt[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl ! ! taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; ! taskfile->sector_count = 1; ! taskfile->sector_number = select; ! taskfile->low_cylinder = 0x4f; ! taskfile->high_cylinder = 0xc2; ! taskfile->device_head = 0; ! taskfile->command = ATA_SMART_CMD; ! ! reqtask->data_phase = TASKFILE_OUT; ! reqtask->req_cmd = IDE_DRIVE_TASK_OUT; ! reqtask->out_size = 512; ! reqtask->in_size = 0; ! ! memcpy(task+sizeof(ide_task_request_t)+4*sizeof(int), data, 512); ! ! if ((retval=ioctl(get_fd(), HPTIO_CTL, task))) { ! if( retval==-EPERM ) ! pout("Highpoint driver missing patch?\n"); ! return -1; ! } ! memcpy(data,task+4*sizeof(int), 512); ! return 0; ! } ! else if (command==WRITE_LOG) { unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512]; unsigned int *hpt = (unsigned int *)task; ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]); *************** *** 2532,2537 **** --- 2573,2580 ---- if ((retval=ioctl(get_fd(), HPTIO_CTL, task))) { if (retval==-EINVAL) pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); + if( retval==-EPERM ) + pout("Highpoint driver missing patch?\n"); return -1; } return 0; diff -bw -N -r -c smartmontools-5.39/smartctl.cpp smartmontools-5.39/smartctl.cpp *** smartmontools-5.39/smartctl.cpp 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/smartctl.cpp 2009-11-13 02:21:40.000000000 +0000 *************** *** 126,132 **** " -l TYPE, --log=TYPE\n" " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" " background, sasphy[,reset], sataphy[,reset],\n" ! " scttemp[sts,hist],\n" " gplog,N[,RANGE], smartlog,N[,RANGE],\n" " xerror[,N][,error], xselftest[,N][,selftest]\n\n" " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" --- 126,132 ---- " -l TYPE, --log=TYPE\n" " Show device log. TYPE: error, selftest, selective, directory[,g|s],\n" " background, sasphy[,reset], sataphy[,reset],\n" ! " scttemp[sts,hist], scterc[,N,M],\n" " gplog,N[,RANGE], smartlog,N[,RANGE],\n" " xerror[,N][,error], xselftest[,N][,selftest]\n\n" " -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n" *************** *** 178,184 **** case 'S': return "on, off"; case 'l': ! return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], " "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], " "xerror[,N][,error], xselftest[,N][,selftest]"; case 'P': --- 178,184 ---- case 'S': return "on, off"; case 'l': ! return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], scterc[,N,M], " "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], " "xerror[,N][,error], xselftest[,N][,selftest]"; case 'P': *************** *** 430,435 **** --- 430,437 ---- ataopts.sataphy = ataopts.sataphy_reset = true; } else if (!strcmp(optarg,"background")) { scsiopts.smart_background_log = true; + } else if (!strcmp(optarg,"scterc")) { + ataopts.sct_erc_get = true; } else if (!strcmp(optarg,"scttemp")) { ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; } else if (!strcmp(optarg,"scttempsts")) { *************** *** 467,472 **** --- 469,489 ---- else badarg = true; + } else if ( !strncmp(optarg, "scterc,", sizeof("scterc,")-1) ) { + int readtime=-1, writetime=-1; + sscanf(optarg,"%*[a-z],%d,%d",&readtime,&writetime); + if(readtime<0 || readtime>999 || writetime<0 || writetime>999 ) + { + sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); + badarg = true; + } + else + { + ataopts.sct_erc_set = true; + ataopts.sct_erc_readtime = readtime; + ataopts.sct_erc_writetime = writetime; + } + } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; *************** *** 526,531 **** --- 543,549 ---- ataopts.smart_selective_selftest_log = true; ataopts.smart_logdir = ataopts.gp_logdir = true; ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; + ataopts.sct_erc_get = true; ataopts.sataphy = true; scsiopts.smart_background_log = true; scsiopts.sasphy = true; *** smartmontools-5.39/smartctl.8.in 2009-11-05 12:37:21.000000000 +0000 --- smartmontools-5.39/smartctl.8.in 2009-11-21 01:32:54.000000000 +0000 *************** *** 189,195 **** devices this is equivalent to .nf \'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective ! \-l directory \-l scttemp \-l sataphy\'. .fi and for SCSI, this is equivalent to .nf --- 189,195 ---- devices this is equivalent to .nf \'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective ! \-l directory \-l scttemp \-l scterc \-l sataphy\'. .fi and for SCSI, this is equivalent to .nf *************** *** 974,979 **** --- 974,989 ---- The SCT commands are specified in the proposed ATA\-8 Command Set (ACS), and are already implemented in some recent ATA\-7 disks. + .I scterc[,READTIME,WRITETIME] + \- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values + and descriptions of the SCT Error Recovery Control settings. These + are equivalent to TLER (as used by Western Digital), CCTL (as used + by Samsung and Hitachi) and ERC (as used by Seagate. READTIME and + WRITETIME arguments (deciseconds) set the specified values. Values of 0 + disable the feature, other values less than 65 are probably not + supported. For RAID configurations, this is typically set to + 70,70 deciseconds. + .I sataphy[,reset] \- [SATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values and descriptions of the SATA Phy Event Counters (General Purpose Log