]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - dev_interface.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / dev_interface.cpp
index 01dc01fe2384f8bb1d11591218f46493d39d7523..a09799038aba2c142cce09343cc3d8b96ef83122 100644 (file)
@@ -1,51 +1,68 @@
 /*
  * dev_interface.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net>
- *
- * 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
+
 #include "dev_interface.h"
+#include "dev_intelliprop.h"
 #include "dev_tunnelled.h"
+#include "atacmds.h" // ATA_SMART_CMD/STATUS
+#include "scsicmds.h" // scsi_cmnd_io
 #include "utility.h"
 
+#include <errno.h>
+#include <stdarg.h>
 #include <stdexcept>
 
-const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 3015 2009-12-30 16:25:59Z chrfranke $"
+#if defined(HAVE_GETTIMEOFDAY)
+#include <sys/time.h>
+#elif defined(HAVE_FTIME)
+#include <sys/timeb.h>
+#endif
+
+const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4848 2018-12-05 18:30:46Z chrfranke $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
 // smart_device
 
+int smart_device::s_num_objects = 0;
+
 smart_device::smart_device(smart_interface * intf, const char * dev_name,
     const char * dev_type, const char * req_type)
 : m_intf(intf), m_info(dev_name, dev_type, req_type),
-  m_ata_ptr(0), m_scsi_ptr(0)
+  m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
 {
+  s_num_objects++;
 }
 
 smart_device::smart_device(do_not_use_in_implementation_classes)
-: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0)
+: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0)
 {
   throw std::logic_error("smart_device: wrong constructor called in implementation class");
 }
 
 smart_device::~smart_device() throw()
 {
+  s_num_objects--;
+}
+
+bool smart_device::is_syscall_unsup() const
+{
+  if (get_errno() == ENOSYS)
+    return true;
+#ifdef ENOTSUP
+  if (get_errno() == ENOTSUP)
+    return true;
+#endif
+  return false;
 }
 
 bool smart_device::set_err(int no, const char * msg, ...)
@@ -61,8 +78,7 @@ bool smart_device::set_err(int no, const char * msg, ...)
 
 bool smart_device::set_err(int no)
 {
-  smi()->set_err_var(&m_err, no);
-  return false;
+  return smi()->set_err_var(&m_err, no);
 }
 
 smart_device * smart_device::autodetect_open()
@@ -71,6 +87,11 @@ smart_device * smart_device::autodetect_open()
   return this;
 }
 
+bool smart_device::is_powered_down()
+{
+  return false;
+}
+
 bool smart_device::owns(const smart_device * /*dev*/) const
 {
   return false;
@@ -122,10 +143,8 @@ bool ata_device::ata_pass_through(const ata_cmd_in & in)
   return ata_pass_through(in, dummy);
 }
 
-bool ata_device::ata_cmd_is_ok(const ata_cmd_in & in,
-  bool data_out_support /*= false*/,
-  bool multi_sector_support /*= false*/,
-  bool ata_48bit_support /*= false*/)
+bool ata_device::ata_cmd_is_supported(const ata_cmd_in & in,
+  unsigned flags, const char * type /* = 0 */)
 {
   // Check DATA IN/OUT
   switch (in.direction) {
@@ -151,12 +170,25 @@ bool ata_device::ata_cmd_is_ok(const ata_cmd_in & in,
   }
 
   // Check features
-  if (in.direction == ata_cmd_in::data_out && !data_out_support)
-    return set_err(ENOSYS, "DATA OUT ATA commands not supported");
-  if (!(in.size == 0 || in.size == 512) && !multi_sector_support)
-    return set_err(ENOSYS, "Multi-sector ATA commands not supported");
-  if (in.in_regs.is_48bit_cmd() && !ata_48bit_support)
-    return set_err(ENOSYS, "48-bit ATA commands not supported");
+  const char * errmsg = 0;
+  if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out))
+    errmsg = "DATA OUT ATA commands not implemented";
+  else if (   in.out_needed.is_set() && !(flags & supports_output_regs)
+           && !(   in.in_regs.command == ATA_SMART_CMD
+                && in.in_regs.features == ATA_SMART_STATUS
+                && (flags & supports_smart_status)))
+    errmsg = "Read of ATA output registers not implemented";
+  else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
+    errmsg = "Multi-sector ATA commands not implemented";
+  else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit)))
+    errmsg = "48-bit ATA commands not implemented";
+  else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
+    errmsg = "48-bit ATA commands not fully implemented";
+
+  if (errmsg)
+    return set_err(ENOSYS, "%s%s%s%s", errmsg,
+                   (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
+
   return true;
 }
 
@@ -165,6 +197,52 @@ bool ata_device::ata_identify_is_cached() const
   return false;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+// scsi_device
+
+bool scsi_device::scsi_pass_through_and_check(scsi_cmnd_io * iop,
+                                              const char * msg)
+{
+  // Provide sense buffer
+  unsigned char sense[32] = {0, };
+  iop->sensep = sense;
+  iop->max_sense_len = sizeof(sense);
+  iop->timeout = SCSI_TIMEOUT_DEFAULT;
+
+  // Run cmd
+  if (!scsi_pass_through(iop)) {
+    if (scsi_debugmode > 0)
+      pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
+           msg, get_errno(), get_errmsg());
+    return false;
+  }
+
+  // Check sense
+  scsi_sense_disect sinfo;
+  scsi_do_sense_disect(iop, &sinfo);
+  int err = scsiSimpleSenseFilter(&sinfo);
+  if (err) {
+    if (scsi_debugmode > 0)
+      pout("%sscsi error: %s\n", msg, scsiErrString(err));
+    return set_err(EIO, "scsi error %s", scsiErrString(err));
+  }
+
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// nvme_device
+
+bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */)
+{
+  if (!status)
+    throw std::logic_error("nvme_device: set_nvme_err() called with status=0");
+
+  out.status = status;
+  out.status_valid = true;
+  return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status);
+}
+
 
 /////////////////////////////////////////////////////////////////////////////
 // tunnelled_device_base
@@ -230,7 +308,9 @@ std::string smart_interface::get_valid_dev_types_str()
 {
   // default
   std::string s =
-    "ata, scsi, sat[,N][+TYPE], usbcypress[,X], usbjmicron[,x][,N], usbsunplus";
+    "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
+    "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntjmicron[,NSID], "
+    "intelliprop,N[+TYPE]";
   // append custom
   std::string s2 = get_valid_custom_dev_types_str();
   if (!s2.empty()) {
@@ -244,28 +324,64 @@ std::string smart_interface::get_app_examples(const char * /*appname*/)
   return "";
 }
 
-void smart_interface::set_err(int no, const char * msg, ...)
+int64_t smart_interface::get_timer_usec()
 {
-  if (!msg) {
-    set_err(no); return;
+#if defined(HAVE_GETTIMEOFDAY)
+ #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+  {
+    static bool have_clock_monotonic = true;
+    if (have_clock_monotonic) {
+      struct timespec ts;
+      if (!clock_gettime(CLOCK_MONOTONIC, &ts))
+        return ts.tv_sec * 1000000LL + ts.tv_nsec/1000;
+      have_clock_monotonic = false;
+    }
   }
+ #endif
+  {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+  }
+#elif defined(HAVE_FTIME)
+  {
+    struct timeb tb;
+    ftime(&tb);
+    return tb.time * 1000000LL + tb.millitm * 1000;
+  }
+#else
+  return -1;
+#endif
+}
+
+bool smart_interface::disable_system_auto_standby(bool /*disable*/)
+{
+  return set_err(ENOSYS);
+}
+
+bool smart_interface::set_err(int no, const char * msg, ...)
+{
+  if (!msg)
+    return set_err(no);
   m_err.no = no;
   va_list ap; va_start(ap, msg);
   m_err.msg = vstrprintf(msg, ap);
   va_end(ap);
+  return false;
 }
 
-void smart_interface::set_err(int no)
+bool smart_interface::set_err(int no)
 {
-  set_err_var(&m_err, no);
+  return set_err_var(&m_err, no);
 }
 
-void smart_interface::set_err_var(smart_device::error_info * err, int no)
+bool smart_interface::set_err_var(smart_device::error_info * err, int no)
 {
   err->no = no;
   err->msg = get_msg_for_errno(no);
   if (err->msg.empty() && no != 0)
     err->msg = strprintf("Unknown error %d", no);
+  return false;
 }
 
 const char * smart_interface::get_msg_for_errno(int no)
@@ -280,14 +396,18 @@ const char * smart_interface::get_msg_for_errno(int no)
 smart_device * smart_interface::get_smart_device(const char * name, const char * type)
 {
   clear_err();
+
+  // Call platform specific autodetection if no device type specified
+  smart_device * dev;
   if (!type || !*type) {
-    smart_device * dev = autodetect_smart_device(name);
+    dev = autodetect_smart_device(name);
     if (!dev && !get_errno())
       set_err(EINVAL, "Unable to detect device type");
     return dev;
   }
 
-  smart_device * dev = get_custom_smart_device(name, type);
+  // First check for platform specific device types
+  dev = get_custom_smart_device(name, type);
   if (dev || get_errno())
     return dev;
 
@@ -296,8 +416,20 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
   else if (!strcmp(type, "scsi"))
     dev = get_scsi_device(name, type);
 
-  else if (  ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
-           || (!strncmp(type, "usb", 3)))) {
+  else if (str_starts_with(type, "nvme")) {
+    int n1 = -1, n2 = -1, len = strlen(type);
+    unsigned nsid = 0; // invalid namespace id -> use default
+    sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2);
+    if (!(n1 == len || n2 == len)) {
+      set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
+      return 0;
+    }
+    dev = get_nvme_device(name, type, nsid);
+  }
+
+  else if (  (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
+           || str_starts_with(type, "scsi+")
+           || str_starts_with(type, "usb")                                        ) {
     // Split "sat...+base..." -> ("sat...", "base...")
     unsigned satlen = strcspn(type, "+");
     std::string sattype(type, satlen);
@@ -316,11 +448,42 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
       return 0;
     }
     // Attach SAT tunnel
-    ata_device * satdev = get_sat_device(sattype.c_str(), basedev->to_scsi());
-    if (!satdev)
+    return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
+  }
+
+  else if (str_starts_with(type, "snt")) {
+    smart_device_auto_ptr basedev( get_smart_device(name, "scsi") );
+    if (!basedev) {
+      set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
+      return 0;
+    }
+
+    return get_snt_device(type, basedev.release()->to_scsi());
+  }
+
+  else if (str_starts_with(type, "intelliprop")) {
+    // Parse "intelliprop,N[+base...]"
+    unsigned phydrive = ~0; int n = -1; char c = 0;
+    sscanf(type, "intelliprop,%u%n%c", &phydrive, &n, &c);
+    if (!((n == (int)strlen(type) || c == '+') && phydrive <= 3)) {
+      set_err(EINVAL, "Option '-d intelliprop,N' requires N between 0 and 3");
+      return 0;
+    }
+    const char * basetype = (type[n] ? type + n + 1 : "");
+    // Recurse to allocate base device, default is standard ATA
+    if (!*basetype)
+      basetype = "ata";
+    smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
+    if (!basedev) {
+      set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
+      return 0;
+    }
+    // Result must be ATA
+    if (!basedev->is_ata()) {
+      set_err(EINVAL, "Type '%s': Device type '%s' is not ATA", type, basetype);
       return 0;
-    basedev.release();
-    return satdev;
+    }
+    return get_intelliprop_device(this, phydrive, basedev.release()->to_ata());
   }
 
   else {
@@ -332,6 +495,37 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
   return dev;
 }
 
+bool smart_interface::scan_smart_devices(smart_device_list & /*devlist*/,
+  const char * /*type*/, const char * /*pattern*/ /* = 0 */)
+{
+  return set_err(ENOSYS);
+}
+
+bool smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const smart_devtype_list & types, const char * pattern /* = 0 */)
+{
+  unsigned n = types.size();
+  if (n == 0)
+    return scan_smart_devices(devlist, (const char *)0, pattern);
+  if (n == 1)
+    return scan_smart_devices(devlist, types.front().c_str(), pattern);
+
+  for (unsigned i = 0; i < n; i++) {
+    smart_device_list tmplist;
+    if (!scan_smart_devices(tmplist, types[i].c_str(), pattern))
+      return false;
+    devlist.append(tmplist);
+  }
+
+  return true;
+}
+
+nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/)
+{
+  set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools");
+  return 0;
+}
+
 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
 {
   return 0;
@@ -341,3 +535,12 @@ std::string smart_interface::get_valid_custom_dev_types_str()
 {
   return "";
 }
+
+smart_device * smart_interface::get_scsi_passthrough_device(const char * type, scsi_device * scsidev)
+{
+  if (!strncmp(type, "snt", 3)) {
+    return get_snt_device(type, scsidev);
+  }
+
+  return get_sat_device(type, scsidev);
+}