diff --git a/smartmontools/CHANGELOG b/smartmontools/CHANGELOG
index d582fc824685e2c90819d17dac0e92d40ec0b11d..c56a114b30d57f2a56ebaaa30fde2c720b01fe04 100644
--- a/smartmontools/CHANGELOG
+++ b/smartmontools/CHANGELOG
@@ -43,6 +43,14 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] smartctl: Add option '-l scterc[,READTIME,WRITETIME]' to get/set
+       the SCT Error Recovery Control time limit (ticket #50).
+
+       Patch was provided by Richard Gregory:
+       http://www.csc.liv.ac.uk/~greg/projects/erc/
+       Modified for new ata_pass_through() interface.
+       Linux HPT fixes ommitted for now.
+
   [CF] Fix SCT temperature table commands on big endian CPUs.
 
   [MS] drivedb.h updates:
diff --git a/smartmontools/NEWS b/smartmontools/NEWS
index b01a77a8378702192198291d22848f2fe2ec30c9..3bf0cd04be1d520bd748558b8353119079b1ec4d 100644
--- a/smartmontools/NEWS
+++ b/smartmontools/NEWS
@@ -13,6 +13,8 @@ Summary: smartmontools release 5.40
 - Drive database is in a separate source file 'drivedb.h'
   which can be downloaded from SVN.
 - smartd libcap-ng support, option '-C, --capabilities'.
+- smartctl option '-l scterc[,...]' to get/set the
+  SCT Error Recovery Control time limit.
 - Fix SCT temperature table commands on big endian CPUs.
 
 Date 2010-01-28
diff --git a/smartmontools/atacmds.cpp b/smartmontools/atacmds.cpp
index 68f4c5d091c5405d110ea057023254061947f631..f4388e9699e8884b5b942c75425715d409814061 100644
--- a/smartmontools/atacmds.cpp
+++ b/smartmontools/atacmds.cpp
@@ -2310,6 +2310,96 @@ int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persisten
   return 0;
 }
 
+// Get/Set SCT Error Recovery Control
+static int ataGetSetSCTErrorRecoveryControltime(ata_device * device, unsigned type,
+                                                bool set, unsigned short & time_limit)
+{
+  // 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 Error Recovery 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  = (set ? 1 : 2); // 1=Set timer, 2=Get timer
+  cmd.selection_code = type; // 1=Read timer, 2=Write timer
+  if (set)
+    cmd.time_limit   = time_limit;
+
+  // swap endian order if needed
+  if (isbigendian()) {
+    swapx(&cmd.action_code);
+    swapx(&cmd.function_code);
+    swapx(&cmd.selection_code);
+    swapx(&cmd.time_limit);
+  }
+
+  // write command via SMART log page 0xe0
+  // TODO: Debug output
+  ata_cmd_in in;
+  in.in_regs.command = ATA_SMART_CMD;
+  in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW;
+  in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR;
+  in.in_regs.lba_low = 0xe0;
+  in.set_data_out(&cmd, 1);
+
+  if (!set)
+    // Time limit returned in ATA registers
+    in.out_needed.sector_count = in.out_needed.lba_low = true;
+
+  ata_cmd_out out;
+  if (!device->ata_pass_through(in, out)) {
+    pout("Error Write SCT Error Recovery Control Command failed: %s\n", device->get_errmsg());
+    return -1;
+  }
+
+  // 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 == (set ? 1 : 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;
+  }
+
+  if (!set) {
+    // Check whether registers are properly returned by ioctl()
+    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");
+      return -1;
+    }
+    // Return value to caller
+    time_limit = out.out_regs.sector_count | (out.out_regs.lba_low << 8);
+  }
+
+  return 0;
+}
+
+// Get SCT Error Recovery Control
+int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit)
+{
+  return ataGetSetSCTErrorRecoveryControltime(device, type, false/*get*/, time_limit);
+}
+
+// Set SCT Error Recovery Control
+int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit)
+{
+  return ataGetSetSCTErrorRecoveryControltime(device, type, true/*set*/, time_limit);
+}
+
+
 // Print one self-test log entry.
 // Returns true if self-test showed an error.
 bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
diff --git a/smartmontools/atacmds.h b/smartmontools/atacmds.h
index f5b8becaacb27be686830c2e477e809e942e9de3..4aa06b67a6ca9beceb90bac73b4c8619b066b970 100644
--- a/smartmontools/atacmds.h
+++ b/smartmontools/atacmds.h
@@ -543,6 +543,20 @@ struct ata_sct_status_response
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_sct_status_response, 512);
 
+// SCT Error Recovery Control command (send with SMART_WRITE_LOG page 0xe0)
+// Table 88 of T13/1699-D Revision 6a
+#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 time_limit;        // If set: Recovery time limit in 100ms units
+  unsigned short words004_255[252]; // reserved
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_sct_error_recovery_control_command, 512);
+
 // SCT Feature Control command (send with SMART_WRITE_LOG page 0xe0)
 // Table 72 of T13/1699-D Revision 3f
 #pragma pack(1)
@@ -552,8 +566,8 @@ struct ata_sct_feature_control_command
   unsigned short function_code;     // 1 = Set, 2 = Return, 3 = Return options
   unsigned short feature_code;      // 3 = Temperature logging interval
   unsigned short state;             // Interval
-  unsigned short option_flags;      // Bit 0: persistent, Bits 1-31: reserved
-  unsigned short words005_255[251]; // reserved 
+  unsigned short option_flags;      // Bit 0: persistent, Bits 1-15: reserved
+  unsigned short words005_255[251]; // reserved
 } ATTR_PACKED;
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_sct_feature_control_command, 512);
@@ -727,6 +741,10 @@ int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table *
 // Set SCT temperature logging interval
 int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent);
 
+// Get/Set SCT Error Recovery Control
+int ataGetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short & time_limit);
+int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsigned short time_limit);
+
 
 /* Enable/Disable SMART on device */
 int ataEnableSmart (ata_device * device);
@@ -789,6 +807,9 @@ int isSupportSelectiveSelfTest(const ata_smart_values * data);
 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] & 0x09) == 0x09); } // 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
 
diff --git a/smartmontools/ataprint.cpp b/smartmontools/ataprint.cpp
index c578d873c267cfe799c1daaf75bb8bb497993b81..9b185acaedc06f2f261525c564ace18faa6bb0b1 100644
--- a/smartmontools/ataprint.cpp
+++ b/smartmontools/ataprint.cpp
@@ -883,6 +883,8 @@ static void ataPrintSCTCapability(const ata_identify_device *drive)
   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)
@@ -1698,6 +1700,20 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
   return 0;
 }
 
+// Print SCT Error Recovery Control timers
+static void ataPrintSCTErrorRecoveryControl(unsigned short read_timer, unsigned short write_timer)
+{
+  pout("SCT Error Recovery Control:\n");
+  if (!read_timer)
+    pout("           Read: Disabled\n");
+  else
+    pout("           Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
+  if (!write_timer)
+    pout("          Write: Disabled\n");
+  else
+    pout("          Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
+}
+
 
 // Compares failure type to policy in effect, and either exits or
 // simply returns to the calling routine.
@@ -2285,14 +2301,21 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     }
   }
 
+  // SCT commands
+  bool sct_ok = false;
+  if (   options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int
+      || options.sct_erc_get  || options.sct_erc_set                          ) {
+    if (!isSCTCapable(&drive)) {
+      pout("Warning: device does not support SCT Commands\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else
+      sct_ok = true;
+  }
+
   // Print SCT status and temperature history table
-  if (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int) {
+  if (sct_ok && (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int)) {
     for (;;) {
-      if (!isSCTCapable(&drive)) {
-        pout("Warning: device does not support SCT Commands\n");
-        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-        break;
-      }
       if (options.sct_temp_sts || options.sct_temp_hist) {
         ata_sct_status_response sts;
         ata_sct_temperature_history_table tmh;
@@ -2340,6 +2363,44 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     }
   }
 
+  // SCT Error Recovery Control
+  if (sct_ok && (options.sct_erc_get || options.sct_erc_set)) {
+    if (!isSCTErrorRecoveryControlCapable(&drive)) {
+      pout("Warning: device does not support SCT Error Recovery Control command\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      bool sct_erc_get = options.sct_erc_get;
+      if (options.sct_erc_set) {
+        // Set SCT Error Recovery Control
+        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);
+          sct_erc_get = false;
+        }
+        else
+          sct_erc_get = true;
+      }
+
+      if (sct_erc_get) {
+        // Print SCT Error Recovery Control
+        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");
+          if (options.sct_erc_set)
+            pout("The previous SCT (Set) Error Recovery Control command succeeded\n");
+          failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+        }
+        else
+          ataPrintSCTErrorRecoveryControl(read_timer, write_timer);
+      }
+      pout("\n");
+    }
+  }
+
   // Print SATA Phy Event Counters
   if (options.sataphy) {
     unsigned nsectors = GetNumLogSectors(gplogdir, 0x11, true);
diff --git a/smartmontools/ataprint.h b/smartmontools/ataprint.h
index 6c8c5eef27bbd88143076ccd5ebf331e74015ea5..f6a461bdcecdbe80d27faea05ce05a47b3103bba 100644
--- a/smartmontools/ataprint.h
+++ b/smartmontools/ataprint.h
@@ -63,6 +63,9 @@ struct ata_print_options
   std::vector<ata_log_request> log_requests;
 
   bool sct_temp_sts, sct_temp_hist;
+  bool sct_erc_get;
+  bool sct_erc_set;
+  unsigned sct_erc_readtime, sct_erc_writetime;
   bool sataphy, sataphy_reset;
 
   bool smart_disable, smart_enable;
@@ -97,6 +100,9 @@ struct ata_print_options
       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(0), sct_erc_writetime(0),
       sataphy(false), sataphy_reset(false),
       smart_disable(false), smart_enable(false),
       smart_auto_offl_disable(false), smart_auto_offl_enable(false),
diff --git a/smartmontools/smartctl.8.in b/smartmontools/smartctl.8.in
index fc814c5c7a16b42e301ac90d98f2952495bcd6ff..a855c1cbb7278c9fec0647e66d87a0f56ec753bc 100644
--- a/smartmontools/smartctl.8.in
+++ b/smartmontools/smartctl.8.in
@@ -189,7 +189,7 @@ Prints all SMART and non-SMART information about the device. For ATA
 devices this is equivalent to
 .nf
 \'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective
-\-l directory \-l scttemp \-l sataphy\'.
+\-l directory \-l scttemp \-l scterc \-l sataphy\'.
 .fi
 and for SCSI, this is equivalent to
 .nf
@@ -974,6 +974,16 @@ configured with the \'\-t scttempint,N[,p]\' option, see below.
 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
diff --git a/smartmontools/smartctl.cpp b/smartmontools/smartctl.cpp
index ec701c2ed014338887be5af14e2d08706fcdada5..766224d60db3d0285f214a2f8b8d0de128f3fe39 100644
--- a/smartmontools/smartctl.cpp
+++ b/smartmontools/smartctl.cpp
@@ -126,7 +126,7 @@ void Usage (void){
 "  -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"
+"                               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,7 +178,7 @@ static std::string getvalidarglist(char opt)
   case 'S':
     return "on, off";
   case 'l':
-    return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], "
+    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,6 +430,8 @@ const char * parse_options(int argc, char** argv,
         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,6 +469,18 @@ const char * parse_options(int argc, char** argv,
         else
           badarg = true;
 
+      } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) {
+        unsigned rt = ~0, wt = ~0; int n = -1;
+        sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n);
+        if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) {
+          ataopts.sct_erc_set = true;
+          ataopts.sct_erc_readtime = rt;
+          ataopts.sct_erc_writetime = wt;
+        }
+        else {
+          sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n");
+          badarg = true;
+        }
       } 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,6 +540,7 @@ const char * parse_options(int argc, char** argv,
       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;