From 4ac0377c20289d5b2b73d69d39d725874e17b651 Mon Sep 17 00:00:00 2001
From: Jonghwan Choi <jhbird.choi@gmail.com>
Date: Tue, 7 Mar 2017 14:31:02 +0900
Subject: [PATCH] smartcl: Add options '-g/s dsn' to get/set DSN feature

Signed-off-by: Jonghwan Choi <jhbird.choi@gmail.com>
---
 atacmds.h        |  1 +
 ataprint.cpp     | 27 +++++++++++++++++++++++++--
 ataprint.h       |  5 ++++-
 smartctl.8.in    |  4 ++++
 smartctl.cpp     | 23 ++++++++++++++++++-----
 smartd.conf.5.in |  3 +++
 smartd.cpp       | 21 +++++++++++++++++----
 7 files changed, 72 insertions(+), 12 deletions(-)

diff --git a/atacmds.h b/atacmds.h
index 7a32676..23b62cf 100644
--- a/atacmds.h
+++ b/atacmds.h
@@ -88,6 +88,7 @@ typedef enum {
 #define ATA_ENABLE_APM                  0x05
 #define ATA_ENABLE_WRITE_CACHE          0x02
 #define ATA_ENABLE_READ_LOOK_AHEAD      0xaa
+#define ATA_ENABLE_DISABLE_DSN          0x63
 
 // 48-bit commands
 #define ATA_READ_LOG_EXT                0x2F
diff --git a/ataprint.cpp b/ataprint.cpp
index 9e9cbbb..daebc93 100644
--- a/ataprint.cpp
+++ b/ataprint.cpp
@@ -2795,6 +2795,18 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
        !(drive.cfs_enable_1 & 0x0020) ? "Disabled" : "Enabled"); // word085
   }
 
+  // Print DSN status
+  unsigned short word120 = drive.words088_255[120-88];
+  if (options.get_dsn) {
+    if (!(drive.word086 & 0x8000) // word086
+       || ((word120 & 0xc000) != 0x4000)) // word120
+      pout("DSN feature is:   Unavailable\n");
+    else if (word120 & 0x200) // word120
+      pout("DSN feature is:   Enabled\n");
+    else
+      pout("DSN feature is:   Disabled\n");
+  }
+
   // Check for ATA Security LOCK
   unsigned short word128 = drive.words088_255[128-88];
   bool locked = ((word128 & 0x0007) == 0x0007); // LOCKED|ENABLED|SUPPORTED
@@ -2866,7 +2878,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       || options.smart_auto_offl_disable || options.smart_auto_offl_enable
       || options.set_aam || options.set_apm || options.set_lookahead
       || options.set_wcache || options.set_security_freeze || options.set_standby
-      || options.sct_wcache_reorder_set || options.sct_wcache_sct_set)
+      || options.sct_wcache_reorder_set || options.sct_wcache_sct_set || options.set_dsn)
     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
   
   // Enable/Disable AAM
@@ -2931,6 +2943,17 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       pout("Write cache %sabled\n", (enable ? "en" : "dis"));
   }
 
+  // Enable/Disable DSN
+  if (options.set_dsn) {
+    bool enable = (options.set_dsn > 0);
+    if (!ata_set_features(device, ATA_ENABLE_DISABLE_DSN, (enable ? 0x1 : 0x2))) {
+        pout("DSN %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg());
+        returnval |= FAILSMART;
+    }
+    else
+      pout("DSN %sabled\n", (enable ? "en" : "dis"));
+  }
+
   // Enable/Disable write cache reordering
   if (options.sct_wcache_reorder_set) {
     bool enable = (options.sct_wcache_reorder_set > 0);
@@ -3100,7 +3123,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       || options.smart_auto_offl_disable || options.smart_auto_offl_enable
       || options.set_aam || options.set_apm || options.set_lookahead
       || options.set_wcache || options.set_security_freeze || options.set_standby
-      || options.sct_wcache_reorder_set)
+      || options.sct_wcache_reorder_set || options.set_dsn)
     pout("\n");
 
   // START OF READ-ONLY OPTIONS APART FROM -V and -i
diff --git a/ataprint.h b/ataprint.h
index 1da201c..b8ccf1f 100644
--- a/ataprint.h
+++ b/ataprint.h
@@ -113,6 +113,8 @@ struct ata_print_options
   bool sct_wcache_sct_get; // print SCT Feature Control of write cache status
   int sct_wcache_sct_set; // determined by ata set features command(1), force enable(2), force disable(3)
   bool sct_wcache_sct_set_pers; // persistent or volatile
+  bool get_dsn; // print DSN status
+  int set_dsn; // disable(02h), enable(01h) DSN
 
   ata_print_options()
     : drive_info(false),
@@ -153,7 +155,8 @@ struct ata_print_options
       sct_wcache_reorder_get(false), sct_wcache_reorder_set(0),
       sct_wcache_reorder_set_pers(false),
       sct_wcache_sct_get(false), sct_wcache_sct_set(0),
-      sct_wcache_sct_set_pers(false)
+      sct_wcache_sct_set_pers(false),
+      get_dsn(false), set_dsn(0)
     { }
 };
 
diff --git a/smartctl.8.in b/smartctl.8.in
index df69e72..d55b21c 100644
--- a/smartctl.8.in
+++ b/smartctl.8.in
@@ -1005,6 +1005,10 @@ commands. If ´,p´ is specified, the setting is preserved across power cycles.
 \'Off\' value disables read cache (if supported).
 The read cache is usually enabled by default.
 
+.I dsn[,on|off]
+\- [ATA only] Gets/sets the DSN feature (if supported).
+The dsn is usually disabled by default.
+
 .TP
 .B SMART READ AND DISPLAY DATA OPTIONS:
 .TP
diff --git a/smartctl.cpp b/smartctl.cpp
index 233220a..d05b2b3 100644
--- a/smartctl.cpp
+++ b/smartctl.cpp
@@ -87,7 +87,7 @@ static void Usage()
 "  --identify[=[w][nvb]]\n"
 "         Show words and bits from IDENTIFY DEVICE data                (ATA)\n\n"
 "  -g NAME, --get=NAME\n"
-"        Get device setting: all, aam, apm, lookahead, security, wcache, rcache, wcreorder, wcache-sct\n\n"
+"        Get device setting: all, aam, apm, dsn, lookahead, security, wcache, rcache, wcreorder, wcache-sct\n\n"
 "  -a, --all\n"
 "         Show all SMART information for device\n\n"
 "  -x, --xall\n"
@@ -122,7 +122,7 @@ static void Usage()
 "        Enable/disable Attribute autosave on device (on/off)\n\n"
 "  -s NAME[,VALUE], --set=NAME[,VALUE]\n"
 "        Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n"
-"        lookahead,[on|off], security-freeze, standby,[N|off|now],\n"
+"        dsn,[on|off], lookahead,[on|off], security-freeze, standby,[N|off|now],\n"
 "        wcache,[on|off], rcache,[on|off], wcreorder,[on|off[,p]]\n"
 "        wcache-sct,[ata|on|off[,p]]\n\n"
   );
@@ -224,9 +224,9 @@ static std::string getvalidarglist(int opt)
   case 'f':
     return "old, brief, hex[,id|val]";
   case 'g':
-    return "aam, apm, lookahead, security, wcache, rcache, wcreorder, wcache-sct";
+    return "aam, apm, dsn, lookahead, security, wcache, rcache, wcreorder, wcache-sct";
   case opt_set:
-    return "aam,[N|off], apm,[N|off], lookahead,[on|off], security-freeze, "
+    return "aam,[N|off], apm,[N|off], dsn,[on|off], lookahead,[on|off], security-freeze, "
            "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off[,p]], "
            "wcache-sct,[ata|on|off[,p]]";
   case 's':
@@ -897,7 +897,7 @@ static const char * parse_options(int argc, char** argv,
           if (get && !strcmp(name, "all")) {
             ataopts.get_aam = ataopts.get_apm = true;
             ataopts.get_security = true;
-            ataopts.get_lookahead = ataopts.get_wcache = true;
+            ataopts.get_lookahead = ataopts.get_wcache = ataopts.get_dsn = true;
             scsiopts.get_rcd = scsiopts.get_wce = true;
           }
           else if (!strcmp(name, "aam")) {
@@ -1006,6 +1006,19 @@ static const char * parse_options(int argc, char** argv,
             else
               badarg = true;
           }
+          else if (!strcmp(name, "dsn")) {
+            if (get) {
+              ataopts.get_dsn = true;
+            }
+            else if (off) {
+              ataopts.set_dsn = -1;
+            }
+            else if (on) {
+              ataopts.set_dsn = 1;
+            }
+            else
+              badarg = true;
+          }
           else
             badarg = true;
         }
diff --git a/smartd.conf.5.in b/smartd.conf.5.in
index 95e35cf..be57541 100644
--- a/smartd.conf.5.in
+++ b/smartd.conf.5.in
@@ -742,6 +742,9 @@ IDLE mode.
 
 .I wcache,[on|off]
 \- [ATA only] Sets the volatile write cache feature.
+
+.I dsn,[on|off]
+\- [ATA only] Sets the DSN feature.
 .TP
 .B \-s REGEXP
 Run Self-Tests or Offline Immediate Tests, at scheduled times.  A
diff --git a/smartd.cpp b/smartd.cpp
index 8385cde..04dabdc 100644
--- a/smartd.cpp
+++ b/smartd.cpp
@@ -281,6 +281,7 @@ struct dev_config
   int set_standby; // set(1..255->0..254) standby timer
   bool set_security_freeze; // Freeze ATA security
   int set_wcache; // disable(-1), enable(1) write cache
+  int set_dsn; // disable(0x2), enable(0x1) DSN
 
   bool sct_erc_set;                       // set SCT ERC to:
   unsigned short sct_erc_readtime;        // ERC read time (deciseconds)
@@ -328,7 +329,7 @@ dev_config::dev_config()
   set_lookahead(0),
   set_standby(0),
   set_security_freeze(false),
-  set_wcache(0),
+  set_wcache(0), set_dsn(0),
   sct_erc_set(false),
   sct_erc_readtime(0), sct_erc_writetime(0),
   curr_pending_id(0), offl_pending_id(0),
@@ -1438,8 +1439,8 @@ static void Directives()
            "  -l TYPE Monitor SMART log or self-test status:\n"
            "          error, selftest, xerror, offlinests[,ns], selfteststs[,ns]\n"
            "  -l scterc,R,W  Set SCT Error Recovery Control\n"
-           "  -e      Change device setting: aam,[N|off], apm,[N|off], lookahead,[on|off],\n"
-           "          security-freeze, standby,[N|off], wcache,[on|off]\n"
+           "  -e      Change device setting: aam,[N|off], apm,[N|off], dsn,[on|off],\n"
+           "          lookahead,[on|off], security-freeze, standby,[N|off], wcache,[on|off]\n"
            "  -f      Monitor 'Usage' Attributes, report failures\n"
            "  -m ADD  Send email warning to address ADD\n"
            "  -M TYPE Modify email warning behavior (see man page)\n"
@@ -2114,6 +2115,10 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
     format_set_result_msg(msg, "Wr-cache", ata_set_features(atadev,
       (cfg.set_wcache > 0? ATA_ENABLE_WRITE_CACHE : ATA_DISABLE_WRITE_CACHE)), cfg.set_wcache);
 
+  if (cfg.set_dsn)
+    format_set_result_msg(msg, "DSN", ata_set_features(atadev,
+      ATA_ENABLE_DISABLE_DSN, (cfg.set_dsn > 0 ? 0x1 : 0x2)));
+
   if (cfg.set_security_freeze)
     format_set_result_msg(msg, "Security freeze",
       ata_nodata_command(atadev, ATA_SECURITY_FREEZE_LOCK));
@@ -3843,7 +3848,7 @@ static void printoutvaliddirectiveargs(int priority, char d)
     PrintOut(priority, "%s", get_valid_firmwarebug_args());
     break;
   case 'e':
-    PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], "
+    PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], dsn,[on|off] "
                        "security-freeze, standby,[N|off], wcache,[on|off]");
     break;
   }
@@ -4365,6 +4370,14 @@ static int ParseToken(char * token, dev_config & cfg, smart_devtype_list & scan_
           else
             badarg = true;
         }
+        else if (!strcmp(arg2, "dsn")) {
+          if (off)
+            cfg.set_dsn = -1;
+          else if (on)
+            cfg.set_dsn = 1;
+          else
+            badarg = true;
+        }
         else
           badarg = true;
       }
-- 
2.5.0

