]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - dev_interface.h
import smartmontools 7.0
[mirror_smartmontools-debian.git] / dev_interface.h
index 716252f566a6647a895fb8cad7c4f4ed9eeaf32f..4d1aee14ba8f3e28b01531547c28034deace2bc4 100644 (file)
@@ -1,37 +1,24 @@
 /*
  * dev_interface.h
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-11 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
  */
 
 #ifndef DEV_INTERFACE_H
 #define DEV_INTERFACE_H
 
-#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 3256 2011-02-08 22:13:41Z chrfranke $\n"
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4848 2018-12-05 18:30:46Z chrfranke $\n"
+
+#include "utility.h"
 
 #include <stdexcept>
 #include <string>
 #include <vector>
 
-#if !defined(__GNUC__) && !defined(__attribute__)
-#define __attribute__(x)  /**/
-#endif
-
-#ifdef _MSC_VER // Disable MSVC warning
-#pragma warning(disable:4250) // 'class1' : inherits 'class2::member' via dominance
-#endif
-
 /////////////////////////////////////////////////////////////////////////////
 // Common functionality for all device types
 
@@ -39,6 +26,7 @@
 class smart_interface;
 class ata_device;
 class scsi_device;
+class nvme_device;
 
 /// Base class for all devices
 class smart_device
@@ -84,7 +72,7 @@ protected:
   enum do_not_use_in_implementation_classes { never_called };
   /// Dummy constructor for abstract classes.
   /// Must never be called in implementation classes.
-  smart_device(do_not_use_in_implementation_classes);
+  explicit smart_device(do_not_use_in_implementation_classes);
 
 public:
   virtual ~smart_device() throw();
@@ -100,6 +88,9 @@ public:
   /// Return true if SCSI device
   bool is_scsi() const
     { return !!m_scsi_ptr; }
+  /// Return true if NVMe device
+  bool is_nvme() const
+    { return !!m_nvme_ptr; }
 
   /// Downcast to ATA device.
   ata_device * to_ata()
@@ -113,6 +104,12 @@ public:
   /// Downcast to SCSI device (const).
   const scsi_device * to_scsi() const
     { return m_scsi_ptr; }
+  /// Downcast to NVMe device.
+  nvme_device * to_nvme()
+    { return m_nvme_ptr; }
+  /// Downcast to NVMe device (const).
+  const nvme_device * to_nvme() const
+    { return m_nvme_ptr; }
 
   ///////////////////////////////////////////////
   // Device information
@@ -153,11 +150,15 @@ public:
   const char * get_errmsg() const
     { return m_err.msg.c_str(); }
 
+  /// Return true if last error indicates an unsupported system call.
+  /// Default implementation returns true on ENOSYS and ENOTSUP.
+  virtual bool is_syscall_unsup() const;
+
   /// Set last error number and message.
   /// Printf()-like formatting is supported.
   /// Returns false always to allow use as a return expression.
   bool set_err(int no, const char * msg, ...)
-    __attribute__ ((format (printf, 3, 4)));
+    __attribute_format_printf(3, 4);
 
   /// Set last error info struct.
   bool set_err(const error_info & err)
@@ -171,6 +172,10 @@ public:
   /// Message is retrieved from interface's get_msg_for_errno(no).
   bool set_err(int no);
 
+  /// Get current number of allocated 'smart_device' objects.
+  static int get_num_objects()
+    { return s_num_objects; }
+
 // Operations
 public:
   ///////////////////////////////////////////////
@@ -189,9 +194,20 @@ public:
   /// Open device with autodetection support.
   /// May return another device for further access.
   /// In this case, the original pointer is no longer valid.
-  /// Default Implementation calls 'open()' and returns 'this'.
+  /// Default implementation calls 'open()' and returns 'this'.
   virtual smart_device * autodetect_open();
 
+  ///////////////////////////////////////////////
+  // Support for checking power mode reported by operating system
+
+  /// Early test if device is powered up or down.
+  /// Can be used without calling 'open()' first!
+  /// Return true when device is powered down, false when
+  /// powered up. If this function is not implemented or
+  /// the mode cannot be determined, return false.
+  /// Default implementation returns false.
+  virtual bool is_powered_down();
+
   ///////////////////////////////////////////////
   // Support for tunnelled devices
 
@@ -204,14 +220,6 @@ public:
   virtual void release(const smart_device * dev);
 
 protected:
-  /// Set dynamic downcast for ATA
-  void this_is_ata(ata_device * ata);
-    // {see below;}
-
-  /// Set dynamic downcast for SCSI
-  void this_is_scsi(scsi_device * scsi);
-    // {see below;}
-
   /// Get interface which produced this object.
   smart_interface * smi()
     { return m_intf; }
@@ -223,11 +231,21 @@ protected:
 private:
   smart_interface * m_intf;
   device_info m_info;
+  error_info m_err;
+
+  // Pointers for to_ata(), to_scsi(), to_nvme()
+  // set by ATA/SCSI/NVMe interface classes.
+  friend class ata_device;
   ata_device * m_ata_ptr;
+  friend class scsi_device;
   scsi_device * m_scsi_ptr;
-  error_info m_err;
+  friend class nvme_device;
+  nvme_device * m_nvme_ptr;
 
-  // Prevent copy/assigment
+  // Number of objects.
+  static int s_num_objects;
+
+  // Prevent copy/assignment
   smart_device(const smart_device &);
   void operator=(const smart_device &);
 };
@@ -511,17 +529,44 @@ public:
   virtual bool ata_identify_is_cached() const;
 
 protected:
+  /// Flags for ata_cmd_is_supported().
+  enum {
+    supports_data_out = 0x01, // PIO DATA OUT
+    supports_smart_status = 0x02, // read output registers for SMART STATUS only
+    supports_output_regs = 0x04, // read output registers for all commands
+    supports_multi_sector = 0x08, // more than one sector (1 DRQ/sector variant)
+    supports_48bit_hi_null = 0x10, // 48-bit commands with null high bytes only
+    supports_48bit = 0x20, // all 48-bit commands
+  };
+
   /// Check command input parameters.
+  /// Return false if required features are not implemented.
   /// Calls set_err(...) accordingly.
+  bool ata_cmd_is_supported(const ata_cmd_in & in, unsigned flags,
+    const char * type = 0);
+
+  /// Check command input parameters (old version).
+  // TODO: Remove if no longer used.
   bool 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_48bit_support = false)
+    {
+      return ata_cmd_is_supported(in,
+        (data_out_support ? supports_data_out : 0) |
+        supports_output_regs |
+        (multi_sector_support ? supports_multi_sector : 0) |
+        (ata_48bit_support ? supports_48bit : 0));
+    }
+
+  /// Hide/unhide ATA interface.
+  void hide_ata(bool hide = true)
+    { m_ata_ptr = (!hide ? this : 0); }
 
   /// Default constructor, registers device as ATA.
   ata_device()
     : smart_device(never_called)
-    { this_is_ata(this); }
+    { hide_ata(false); }
 };
 
 
@@ -539,29 +584,120 @@ public:
   /// Returns false on error.
   virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0;
 
+  // Call scsi_pass_through and check sense.
+  bool scsi_pass_through_and_check(scsi_cmnd_io * iop,
+                                   const char * msg = "");
+
+  /// Always try READ CAPACITY(10) (rcap10) first but once we know
+  /// rcap16 is needed, use it instead.
+  void set_rcap16_first()
+    { rcap16_first = true; }
+
+  bool use_rcap16() const
+    { return rcap16_first; }
+
 protected:
+  /// Hide/unhide SCSI interface.
+  void hide_scsi(bool hide = true)
+    { m_scsi_ptr = (!hide ? this : 0); }
+
   /// Default constructor, registers device as SCSI.
   scsi_device()
-    : smart_device(never_called)
-    { this_is_scsi(this); }
+    : smart_device(never_called),
+      rcap16_first(false)
+    { hide_scsi(false); }
+
+private:
+  bool rcap16_first;
 };
 
 
 /////////////////////////////////////////////////////////////////////////////
+// NVMe specific interface
 
-// Set dynamic downcasts
-// Note that due to virtual inheritance,
-// (ata == this) does not imply ((void*)ata == (void*)this))
+/// NVMe pass through input parameters
+struct nvme_cmd_in
+{
+  unsigned char opcode; ///< Opcode (CDW0 07:00)
+  unsigned nsid; ///< Namespace ID
+  unsigned cdw10, cdw11, cdw12, cdw13, cdw14, cdw15; ///< Cmd specific
+
+  void * buffer; ///< Pointer to data buffer
+  unsigned size; ///< Size of buffer
+
+  enum {
+    no_data = 0x0, data_out = 0x1, data_in = 0x2, data_io = 0x3
+  };
+
+  /// Get I/O direction from opcode
+  unsigned char direction() const
+    { return (opcode & 0x3); }
 
-inline void smart_device::this_is_ata(ata_device * ata)
+  // Prepare for DATA IN command
+  void set_data_in(unsigned char op, void * buf, unsigned sz)
+    {
+      opcode = op;
+      if (direction() != data_in)
+        throw std::logic_error("invalid opcode for DATA IN");
+      buffer = buf;
+      size = sz;
+    }
+
+  nvme_cmd_in()
+    : opcode(0), nsid(0),
+      cdw10(0), cdw11(0), cdw12(0), cdw13(0), cdw14(0), cdw15(0),
+      buffer(0), size(0)
+    { }
+};
+
+/// NVMe pass through output parameters
+struct nvme_cmd_out
 {
-  m_ata_ptr = (ata == this ? ata : 0);
-}
+   unsigned result; ///< Command specific result (DW0)
+   unsigned short status; ///< Status Field (DW3 31:17)
+   bool status_valid; ///< true if status is valid
+
+   nvme_cmd_out()
+     : result(0), status(0), status_valid(false)
+     { }
+};
 
-inline void smart_device::this_is_scsi(scsi_device * scsi)
+/// NVMe device access
+class nvme_device
+: virtual public /*extends*/ smart_device
 {
-  m_scsi_ptr = (scsi == this ? scsi : 0);
-}
+public:
+  /// NVMe pass through.
+  /// Return false on error.
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) = 0;
+
+  /// Get namespace id.
+  unsigned get_nsid() const
+    { return m_nsid; }
+
+protected:
+  /// Hide/unhide NVMe interface.
+  void hide_nvme(bool hide = true)
+    { m_nvme_ptr = (!hide ? this : 0); }
+
+  /// Constructor requires namespace ID, registers device as NVMe.
+  explicit nvme_device(unsigned nsid)
+    : smart_device(never_called),
+      m_nsid(nsid)
+    { hide_nvme(false); }
+
+  /// Set namespace id.
+  /// Should be called in open() function if get_nsid() returns 0.
+  void set_nsid(unsigned nsid)
+    { m_nsid = nsid; }
+
+  /// Set last error number and message if pass-through returns NVMe error status.
+  /// Returns false always to allow use as a return expression.
+  bool set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg = 0);
+
+private:
+  unsigned m_nsid;
+};
 
 
 /////////////////////////////////////////////////////////////////////////////
@@ -651,6 +787,7 @@ private:
 typedef any_device_auto_ptr<smart_device> smart_device_auto_ptr;
 typedef any_device_auto_ptr<ata_device>   ata_device_auto_ptr;
 typedef any_device_auto_ptr<scsi_device>  scsi_device_auto_ptr;
+typedef any_device_auto_ptr<nvme_device>  nvme_device_auto_ptr;
 
 
 /////////////////////////////////////////////////////////////////////////////
@@ -705,16 +842,31 @@ public:
       return dev;
     }
 
+  void append(smart_device_list & devlist)
+    {
+      for (unsigned i = 0; i < devlist.size(); i++) {
+        smart_device * dev = devlist.at(i);
+        if (!dev)
+          continue;
+        push_back(dev);
+        devlist.m_list.at(i) = 0;
+      }
+    }
+
 // Implementation
 private:
   std::vector<smart_device *> m_list;
 
-  // Prevent copy/assigment
+  // Prevent copy/assignment
   smart_device_list(const smart_device_list &);
   void operator=(const smart_device_list &);
 };
 
 
+/// List of types for DEVICESCAN
+typedef std::vector<std::string> smart_devtype_list;
+
+
 /////////////////////////////////////////////////////////////////////////////
 // smart_interface
 
@@ -748,6 +900,19 @@ public:
   /// TODO: Remove this hack.
   virtual std::string get_app_examples(const char * appname);
 
+  /// Get microseconds since some unspecified starting point.
+  /// Used only for command duration measurements in debug outputs.
+  /// Returns -1 if unsupported.
+  /// Default implementation uses clock_gettime(), gettimeofday() or ftime().
+  virtual int64_t get_timer_usec();
+
+  /// Disable/Enable system auto standby/sleep mode.
+  /// Return false if unsupported or if system is running
+  /// on battery.
+  /// Default implementation returns false.
+  virtual bool disable_system_auto_standby(bool disable);
+
+
   ///////////////////////////////////////////////
   // Last error information
 
@@ -763,12 +928,13 @@ public:
 
   /// Set last error number and message.
   /// Printf()-like formatting is supported.
-  void set_err(int no, const char * msg, ...)
-    __attribute__ ((format (printf, 3, 4)));
+  /// Returns false always to allow use as a return expression.
+  bool set_err(int no, const char * msg, ...)
+    __attribute_format_printf(3, 4);
 
   /// Set last error info struct.
-  void set_err(const smart_device::error_info & err)
-    { m_err = err; }
+  bool set_err(const smart_device::error_info & err)
+    { m_err = err; return false; }
 
   /// Clear last error info.
   void clear_err()
@@ -776,11 +942,11 @@ public:
 
   /// Set last error number and default message.
   /// Message is retrieved from get_msg_for_errno(no).
-  void set_err(int no);
+  bool set_err(int no);
 
   /// Set last error number and default message to any error_info.
   /// Used by set_err(no).
-  void set_err_var(smart_device::error_info * err, int no);
+  bool set_err_var(smart_device::error_info * err, int no);
 
   /// Convert error number into message, used by set_err(no).
   /// Default implementation returns strerror(no).
@@ -797,9 +963,20 @@ public:
 
   /// Fill 'devlist' with devices of some 'type' with device names
   /// specified by some optional 'pattern'.
+  /// Use platform specific default if 'type' is empty or 0.
   /// Return false on error.
+  /// Default implementation returns false;
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
-    const char * pattern = 0) = 0;
+    const char * pattern = 0);
+
+  /// Fill 'devlist' with devices of all 'types' with device names
+  /// specified by some optional 'pattern'.
+  /// Use platform specific default if 'types' is empty.
+  /// Return false on error.
+  /// Default implementation calls above function for all types
+  /// and concatenates the results.
+  virtual bool scan_smart_devices(smart_device_list & devlist,
+    const smart_devtype_list & types, const char * pattern = 0);
 
 protected:
   /// Return standard ATA device.
@@ -808,6 +985,11 @@ protected:
   /// Return standard SCSI device.
   virtual scsi_device * get_scsi_device(const char * name, const char * type) = 0;
 
+  /// Return standard NVMe device.
+  /// Default implementation returns 0.
+  virtual nvme_device * get_nvme_device(const char * name, const char * type,
+    unsigned nsid);
+
   /// Autodetect device if no device type specified.
   virtual smart_device * autodetect_smart_device(const char * name) = 0;
 
@@ -820,11 +1002,25 @@ protected:
   /// Default implementation returns empty string.
   virtual std::string get_valid_custom_dev_types_str();
 
-  /// Return ATA->SCSI filter for SAT or USB.
+  /// Return ATA->SCSI of NVMe->SCSI filter for a SAT, SNT or USB 'type'.
+  /// Uses get_sat_device and get_snt_device.
+  /// Return 0 and delete 'scsidev' on error.
+  virtual smart_device * get_scsi_passthrough_device(const char * type, scsi_device * scsidev);
+
+  /// Return ATA->SCSI filter for a SAT or USB 'type'.
+  /// Device 'scsidev' is used for SCSI access.
+  /// Return 0 and delete 'scsidev' on error.
   /// Override only if platform needs special handling.
   virtual ata_device * get_sat_device(const char * type, scsi_device * scsidev);
   //{ implemented in scsiata.cpp }
 
+  /// Return NVMe->SCSI filter for a SNT or USB 'type'.
+  /// Device 'scsidev' is used for SCSI access.
+  /// Return 0 and delete 'scsidev' on error.
+  /// Override only if platform needs special handling.
+  virtual nvme_device * get_snt_device(const char * type, scsi_device * scsidev);
+  //{ implemented in scsinvme.cpp }
+
 public:
   /// Try to detect a SAT device behind a SCSI interface.
   /// Inquiry data can be passed if available.
@@ -852,7 +1048,7 @@ private:
   friend smart_interface * smi(); // below
   static smart_interface * s_instance; ///< Pointer to the interface object.
 
-  // Prevent copy/assigment
+  // Prevent copy/assignment
   smart_interface(const smart_interface &);
   void operator=(const smart_interface &);
 };