diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:14:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:14:45 +0000 |
commit | 43e8530e93493bb978c446a2023134bdd4277e50 (patch) | |
tree | e8c0d3c0c394b17381f48fb2d288f166b4f22440 /dev_interface.h | |
parent | Initial commit. (diff) | |
download | smartmontools-43e8530e93493bb978c446a2023134bdd4277e50.tar.xz smartmontools-43e8530e93493bb978c446a2023134bdd4277e50.zip |
Adding upstream version 7.4.upstream/7.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dev_interface.h')
-rw-r--r-- | dev_interface.h | 1125 |
1 files changed, 1125 insertions, 0 deletions
diff --git a/dev_interface.h b/dev_interface.h new file mode 100644 index 0000000..519bc36 --- /dev/null +++ b/dev_interface.h @@ -0,0 +1,1125 @@ +/* + * dev_interface.h + * + * Home page of code is: https://www.smartmontools.org + * + * Copyright (C) 2008-22 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 5455 2023-02-12 05:13:17Z dpgilbert $\n" + +#include "utility.h" + +#include <stdexcept> +#include <string> +#include <vector> + +///////////////////////////////////////////////////////////////////////////// +// Common functionality for all device types + +// Forward declarations +class smart_interface; +class ata_device; +class scsi_device; +class nvme_device; + +/// Base class for all devices +class smart_device +{ +// Types +public: + /// Device info strings + struct device_info { + device_info() + { } + device_info(const char * d_name, const char * d_type, const char * r_type) + : dev_name(d_name), info_name(d_name), + dev_type(d_type), req_type(r_type) + { } + + std::string dev_name; ///< Device (path)name + std::string info_name; ///< Informal name + std::string dev_type; ///< Actual device type + std::string req_type; ///< Device type requested by user, empty if none + }; + + /// Error (number,message) pair + struct error_info { + explicit error_info(int n = 0) + : no(n) { } + error_info(int n, const char * m) + : no(n), msg(m) { } + void clear() + { no = 0; msg.erase(); } + + int no; ///< Error number + std::string msg; ///< Error message + }; + +// Construction +protected: + /// Constructor to init interface and device info. + /// Must be called in implementation classes. + smart_device(smart_interface * intf, const char * dev_name, + const char * dev_type, const char * req_type); + + /// Dummy enum for dummy constructor. + enum do_not_use_in_implementation_classes { never_called }; + /// Dummy constructor for abstract classes. + /// Must never be called in implementation classes. + explicit smart_device(do_not_use_in_implementation_classes); + +public: + virtual ~smart_device(); + +// Attributes +public: + /////////////////////////////////////////////// + // Dynamic downcasts to actual device flavor + + /// Return true if ATA device + bool is_ata() const + { return !!m_ata_ptr; } + /// 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() + { return m_ata_ptr; } + /// Downcast to ATA device (const). + const ata_device * to_ata() const + { return m_ata_ptr; } + /// Downcast to SCSI device. + scsi_device * to_scsi() + { return m_scsi_ptr; } + /// 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 + + /// Get device info struct. + const device_info & get_info() const + { return m_info; } + + /// Get device (path)name. + const char * get_dev_name() const + { return m_info.dev_name.c_str(); } + /// Get informal name. + const char * get_info_name() const + { return m_info.info_name.c_str(); } + /// Get device type. + const char * get_dev_type() const + { return m_info.dev_type.c_str(); } + /// Get type requested by user, empty if none. + const char * get_req_type() const + { return m_info.req_type.c_str(); } + +protected: + /// R/W access to device info struct. + device_info & set_info() + { return m_info; } + +public: + /////////////////////////////////////////////// + // Last error information + + /// Get last error info struct. + const error_info & get_err() const + { return m_err; } + /// Get last error number. + int get_errno() const + { return m_err.no; } + /// Get last error message. + 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); + + /// Set last error info struct. + bool set_err(const error_info & err) + { m_err = err; return false; } + + /// Clear last error info. + void clear_err() + { m_err.clear(); } + + /// Set last error number and default message. + /// 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: + /////////////////////////////////////////////// + // Device open/close + // Must be implemented in derived class + + /// Return true if device is open. + virtual bool is_open() const = 0; + + /// Open device, return false on error. + virtual bool open() = 0; + + /// Close device, return false on error. + virtual bool close() = 0; + + /// 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'. + 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 + + /// Return true if other device is owned by this device. + /// Default implementation returns false. + virtual bool owns(const smart_device * dev) const; + + /// Release ownership of other device. + /// Default implementation does nothing. + virtual void release(const smart_device * dev); + +protected: + /// Get interface which produced this object. + smart_interface * smi() + { return m_intf; } + /// Get interface which produced this object (const). + const smart_interface * smi() const + { return m_intf; } + +// Implementation +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; + friend class nvme_device; + nvme_device * m_nvme_ptr; + + // Number of objects. + static int s_num_objects; + + // Prevent copy/assignment + smart_device(const smart_device &); + void operator=(const smart_device &); +}; + + +///////////////////////////////////////////////////////////////////////////// +// ATA specific interface + +/// ATA register value and info whether it has ever been set +// (Automatically set by first assignment) +class ata_register +{ +public: + ata_register() + : m_val(0x00), m_is_set(false) { } + + ata_register & operator=(unsigned char x) + { m_val = x; m_is_set = true; return * this; } + + unsigned char val() const + { return m_val; } + operator unsigned char() const + { return m_val; } + + bool is_set() const + { return m_is_set; } + +private: + unsigned char m_val; ///< Register value + bool m_is_set; ///< true if set +}; + +/// ATA Input registers (for 28-bit commands) +struct ata_in_regs +{ + // ATA-6/7 register names // ATA-3/4/5 // ATA-8 + ata_register features; // features // features + ata_register sector_count; // sector count // count + ata_register lba_low; // sector number // ] + ata_register lba_mid; // cylinder low // ] lba + ata_register lba_high; // cylinder high // ] + ata_register device; // device/head // device + ata_register command; // command // command + + /// Return true if any register is set + bool is_set() const + { return (features.is_set() || sector_count.is_set() + || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set() + || device.is_set() || command.is_set()); } +}; + +/// ATA Output registers (for 28-bit commands) +struct ata_out_regs +{ + ata_register error; + ata_register sector_count; + ata_register lba_low; + ata_register lba_mid; + ata_register lba_high; + ata_register device; + ata_register status; + + /// Return true if any register is set + bool is_set() const + { return (error.is_set() || sector_count.is_set() + || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set() + || device.is_set() || status.is_set()); } +}; + + +/// 16-bit alias to a 8-bit ATA register pair. +class ata_reg_alias_16 +{ +public: + ata_reg_alias_16(ata_register & lo, ata_register & hi) + : m_lo(lo), m_hi(hi) { } + + ata_reg_alias_16 & operator=(unsigned short x) + { m_lo = (unsigned char) x; + m_hi = (unsigned char)(x >> 8); + return * this; } + + unsigned short val() const + { return m_lo | (m_hi << 8); } + operator unsigned short() const + { return m_lo | (m_hi << 8); } + +private: + ata_register & m_lo, & m_hi; + + // References must not be copied. + ata_reg_alias_16(const ata_reg_alias_16 &); + void operator=(const ata_reg_alias_16 &); +}; + + +/// 48-bit alias to six 8-bit ATA registers (for LBA). +class ata_reg_alias_48 +{ +public: + ata_reg_alias_48(ata_register & ll, ata_register & lm, ata_register & lh, + ata_register & hl, ata_register & hm, ata_register & hh) + : m_ll(ll), m_lm(lm), m_lh(lh), + m_hl(hl), m_hm(hm), m_hh(hh) + { } + + ata_reg_alias_48 & operator=(uint64_t x) + { + m_ll = (unsigned char) x; + m_lm = (unsigned char)(x >> 8); + m_lh = (unsigned char)(x >> 16); + m_hl = (unsigned char)(x >> 24); + m_hm = (unsigned char)(x >> 32); + m_hh = (unsigned char)(x >> 40); + return * this; + } + + uint64_t val() const + { + return ( (unsigned)m_ll + | ((unsigned)m_lm << 8) + | ((unsigned)m_lh << 16) + | ((unsigned)m_hl << 24) + | ((uint64_t)m_hm << 32) + | ((uint64_t)m_hh << 40)); + } + + operator uint64_t() const + { return val(); } + +private: + ata_register & m_ll, & m_lm, & m_lh, + & m_hl, & m_hm, & m_hh; + + // References must not be copied. + ata_reg_alias_48(const ata_reg_alias_48 &); + void operator=(const ata_reg_alias_48 &); +}; + + +/// ATA Input registers for 48-bit commands +// See section 4.14 of T13/1532D Volume 1 Revision 4b +// +// Uses ATA-6/7 method to specify 16-bit registers as +// recent (low byte) and previous (high byte) content of +// 8-bit registers. +// +// (ATA-8 ACS does not longer follow this scheme, it uses +// abstract registers with sufficient size and leaves the +// actual mapping to the transport layer.) +// +struct ata_in_regs_48bit +: public ata_in_regs // "most recently written" registers +{ + ata_in_regs prev; ///< "previous content" + + // 16-bit aliases for above pair. + ata_reg_alias_16 features_16; + ata_reg_alias_16 sector_count_16; + ata_reg_alias_16 lba_low_16; + ata_reg_alias_16 lba_mid_16; + ata_reg_alias_16 lba_high_16; + + // 48-bit alias to all 8-bit LBA registers. + ata_reg_alias_48 lba_48; + + /// Return true if 48-bit command + bool is_48bit_cmd() const + { return prev.is_set(); } + + /// Return true if 48-bit command with any nonzero high byte + bool is_real_48bit_cmd() const + { return ( prev.features || prev.sector_count + || prev.lba_low || prev.lba_mid || prev.lba_high); } + + ata_in_regs_48bit(); +}; + + +/// ATA Output registers for 48-bit commands +struct ata_out_regs_48bit +: public ata_out_regs // read with HOB=0 +{ + ata_out_regs prev; ///< read with HOB=1 + + // 16-bit aliases for above pair. + ata_reg_alias_16 sector_count_16; + ata_reg_alias_16 lba_low_16; + ata_reg_alias_16 lba_mid_16; + ata_reg_alias_16 lba_high_16; + + // 48-bit alias to all 8-bit LBA registers. + ata_reg_alias_48 lba_48; + + ata_out_regs_48bit(); +}; + + +/// Flags for each ATA output register +struct ata_out_regs_flags +{ + bool error, sector_count, lba_low, lba_mid, lba_high, device, status; + + /// Return true if any flag is set. + bool is_set() const + { return ( error || sector_count || lba_low + || lba_mid || lba_high || device || status); } + + /// Default constructor clears all flags. + ata_out_regs_flags() + : error(false), sector_count(false), lba_low(false), lba_mid(false), + lba_high(false), device(false), status(false) { } +}; + + +/// ATA pass through input parameters +struct ata_cmd_in +{ + ata_in_regs_48bit in_regs; ///< Input registers + ata_out_regs_flags out_needed; ///< True if output register value needed + enum { no_data = 0, data_in, data_out } direction; ///< I/O direction + void * buffer; ///< Pointer to data buffer + unsigned size; ///< Size of buffer + + /// Prepare for 28-bit DATA IN command + void set_data_in(void * buf, unsigned nsectors) + { + buffer = buf; + in_regs.sector_count = nsectors; + direction = data_in; + size = nsectors * 512; + } + + /// Prepare for 28-bit DATA OUT command + void set_data_out(const void * buf, unsigned nsectors) + { + buffer = const_cast<void *>(buf); + in_regs.sector_count = nsectors; + direction = data_out; + size = nsectors * 512; + } + + /// Prepare for 48-bit DATA IN command + void set_data_in_48bit(void * buf, unsigned nsectors) + { + buffer = buf; + // Note: This also sets 'in_regs.is_48bit_cmd()' + in_regs.sector_count_16 = nsectors; + direction = data_in; + size = nsectors * 512; + } + + ata_cmd_in(); +}; + +/// ATA pass through output parameters +struct ata_cmd_out +{ + ata_out_regs_48bit out_regs; ///< Output registers + + ata_cmd_out(); +}; + +/// ATA device access +class ata_device +: virtual public /*extends*/ smart_device +{ +public: + /// ATA pass through. + /// Return false on error. + /// Must be implemented in derived class. + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) = 0; + + /// ATA pass through without output registers. + /// Return false on error. + /// Calls ata_pass_through(in, dummy), cannot be reimplemented. + bool ata_pass_through(const ata_cmd_in & in); + + /// Return true if OS caches ATA identify sector. + /// Default implementation returns false. + 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) + { + 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) + { hide_ata(false); } +}; + + +///////////////////////////////////////////////////////////////////////////// +// SCSI specific interface + +struct scsi_cmnd_io; + +enum scsi_cmd_support +{ + SC_SUPPORT_UNKNOWN = 0, + SC_NO_SUPPORT, + SC_SUPPORT, +}; + +/// SCSI device access +class scsi_device +: virtual public /*extends*/ smart_device +{ +public: + /// SCSI pass through. + /// 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; } + + void set_spc4_or_higher() { spc4_or_above = true; } + + bool is_spc4_or_higher() const { return spc4_or_above; } + + bool query_cmd_support(); + + bool checked_cmd_support() const { return rsoc_queried; } + + enum scsi_cmd_support cmd_support_level(uint8_t opcode, bool sa_valid, + uint16_t sa, + bool for_lsense_spc = false) const; + +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), + rcap16_first(false), + spc4_or_above(false), + rsoc_queried(false), + rsoc_sup(SC_SUPPORT_UNKNOWN), + logsense_sup(SC_SUPPORT_UNKNOWN), + logsense_spc_sup(SC_SUPPORT_UNKNOWN), + rcap16_sup(SC_SUPPORT_UNKNOWN), + rdefect10_sup(SC_SUPPORT_UNKNOWN), + rdefect12_sup(SC_SUPPORT_UNKNOWN) + { hide_scsi(false); } + +private: + bool rcap16_first; + bool spc4_or_above; + + bool rsoc_queried; + scsi_cmd_support rsoc_sup; + scsi_cmd_support logsense_sup; + scsi_cmd_support logsense_spc_sup; + scsi_cmd_support rcap16_sup; + scsi_cmd_support rdefect10_sup; + scsi_cmd_support rdefect12_sup; +}; + + +///////////////////////////////////////////////////////////////////////////// +// NVMe specific interface + +/// 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); } + + // 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 +{ + 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) + { } +}; + +/// NVMe device access +class nvme_device +: virtual public /*extends*/ smart_device +{ +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; +}; + + +///////////////////////////////////////////////////////////////////////////// +/// Smart pointer class for device pointers + +template <class Dev> +class any_device_auto_ptr +{ +public: + typedef Dev device_type; + + /// Construct from optional pointer to device + /// and optional pointer to base device. + explicit any_device_auto_ptr(device_type * dev = 0, + smart_device * base_dev = 0) + : m_dev(dev), m_base_dev(base_dev) { } + + /// Destructor deletes device object. + ~any_device_auto_ptr() + { reset(); } + + /// Assign a new pointer. + /// Throws if a pointer is already assigned. + void operator=(device_type * dev) + { + if (m_dev) + fail(); + m_dev = dev; + } + + /// Delete device object and clear the pointer. + void reset() + { + if (m_dev) { + if (m_base_dev && m_dev->owns(m_base_dev)) + m_dev->release(m_base_dev); + delete m_dev; + m_dev = 0; + } + } + + /// Return the pointer and release ownership. + device_type * release() + { + device_type * dev = m_dev; + m_dev = 0; + return dev; + } + + /// Replace the pointer. + /// Used to call dev->autodetect_open(). + void replace(device_type * dev) + { m_dev = dev; } + + /// Return the pointer. + device_type * get() const + { return m_dev; } + + /// Pointer dereferencing. + device_type & operator*() const + { return *m_dev; } + + /// Pointer dereferencing. + device_type * operator->() const + { return m_dev; } + + /// For (ptr != 0) check. + operator bool() const + { return !!m_dev; } + + /// For (ptr == 0) check. + bool operator !() const + { return !m_dev; } + +private: + device_type * m_dev; + smart_device * m_base_dev; + + void fail() const + { throw std::logic_error("any_device_auto_ptr: wrong usage"); } + + // Prevent copy/assignment + any_device_auto_ptr(const any_device_auto_ptr<Dev> &); + void operator=(const any_device_auto_ptr<Dev> &); +}; + +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; + + +///////////////////////////////////////////////////////////////////////////// +// smart_device_list + +/// List of devices for DEVICESCAN +class smart_device_list +{ +// Construction +public: + smart_device_list() + { } + + ~smart_device_list() + { + for (unsigned i = 0; i < m_list.size(); i++) + delete m_list[i]; + } + +// Attributes + unsigned size() const + { return m_list.size(); } + +// Operations + void clear() + { + for (unsigned i = 0; i < m_list.size(); i++) + delete m_list[i]; + m_list.clear(); + } + + + void push_back(smart_device * dev) + { m_list.push_back(dev); } + + void push_back(smart_device_auto_ptr & dev) + { + m_list.push_back(dev.get()); + dev.release(); + } + + smart_device * at(unsigned i) + { return m_list.at(i); } + + const smart_device * at(unsigned i) const + { return m_list.at(i); } + + smart_device * release(unsigned i) + { + smart_device * dev = m_list.at(i); + m_list[i] = 0; + 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/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 + +/// The platform interface abstraction +class smart_interface +{ +public: + /// Initialize platform interface and register with smi(). + /// Must be implemented by platform module and register interface with set() + static void init(); + + smart_interface() + { } + + virtual ~smart_interface() + { } + + /// Return info string about build host and/or OS version. + /// Default implementation returns SMARTMONTOOLS_BUILD_HOST. + virtual std::string get_os_version_str(); + + /// Return valid args for device type option/directive. + /// Default implementation returns "ata, scsi, sat, usb*..." + /// concatenated with result from get_valid_custom_dev_types_str(). + virtual std::string get_valid_dev_types_str(); + + /// Return example string for program 'appname'. + /// Default implementation returns empty string. + /// For the migration of print_smartctl_examples(), + /// function is allowed to print examples to stdout. + /// TODO: Remove this hack. + virtual std::string get_app_examples(const char * appname); + + /// 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 + + /// Get last error info struct. + const smart_device::error_info & get_err() const + { return m_err; } + /// Get last error number. + int get_errno() const + { return m_err.no; } + /// Get last error message. + const char * get_errmsg() const + { return m_err.msg.c_str(); } + + /// 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); + + /// Set last error number and message. + /// Printf()-like formatting is supported. + /// Returns nullptr always to allow use as a return expression + /// of any pointer type. + // (Not using 'std::nullptr_t' because it requires <cstddef>) + decltype(nullptr) set_err_np(int no, const char * msg, ...) + __attribute_format_printf(3, 4); + + /// Set last error info struct. + bool set_err(const smart_device::error_info & err) + { m_err = err; return false; } + + /// Clear last error info. + void clear_err() + { m_err.clear(); } + + /// Set last error number and default message. + /// Message is retrieved from get_msg_for_errno(no). + bool set_err(int no); + + /// Set last error number and default message to any error_info. + /// Used by set_err(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). + virtual const char * get_msg_for_errno(int no); + + /////////////////////////////////////////////////////////////////////////// + // Device factory: + + /// Return device object for device 'name' with some 'type'. + /// 'type' is 0 if not specified by user. + /// Return 0 on error. + /// Default implementation selects between ata, scsi and custom device. + virtual smart_device * get_smart_device(const char * name, const char * type); + + /// 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); + + /// 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); + + /// Return unique device name which is (only) suitable for duplicate detection. + /// Default implementation resolves symlinks on POSIX systems and appends + /// " [type]" if is_raid_dev_type(type)' returns true. + virtual std::string get_unique_dev_name(const char * name, const char * type) const; + + /// Return true if the 'type' string contains a RAID drive number. + /// Default implementation returns true if 'type' starts with '[^,]+,[0-9]' + /// but not with 'sat,'. + virtual bool is_raid_dev_type(const char * type) const; + +protected: + /// Return standard ATA device. + virtual ata_device * get_ata_device(const char * name, const char * type) = 0; + + /// 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; + + /// Return device for platform specific 'type'. + /// Default implementation returns 0. + virtual smart_device * get_custom_smart_device(const char * name, const char * type); + + /// Return valid 'type' args accepted by above. + /// This is called in get_valid_dev_types_str(). + /// Default implementation returns empty string. + virtual std::string get_valid_custom_dev_types_str(); + + /// 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 } + + /// Return filter for Intelliprop controllers. + virtual ata_device * get_intelliprop_device(const char * type, ata_device * atadev); + //{ implemented in dev_intelliprop.cpp } + + /// Return JMB93x->ATA filter. + /// Device 'smartdev' is used for ATA or SCSI R/W access. + /// Return 0 and delete 'scsidev' on error. + /// Override only if platform needs special handling. + virtual ata_device * get_jmb39x_device(const char * type, smart_device * smartdev); + //{ implemented in dev_jmb39x_raid.cpp } + +public: + /// Try to detect a SAT device behind a SCSI interface. + /// Inquiry data can be passed if available. + /// Return appropriate device if yes, otherwise 0. + /// Override only if platform needs special handling. + virtual ata_device * autodetect_sat_device(scsi_device * scsidev, + const unsigned char * inqdata, unsigned inqsize); + //{ implemented in scsiata.cpp } + + /// Get type name for USB device with known VENDOR:PRODUCT ID. + /// Return name if device known and supported, otherwise 0. + virtual const char * get_usb_dev_type_by_id(int vendor_id, int product_id, + int version = -1); + //{ implemented in scsiata.cpp } + +protected: + /// Set interface to use, must be called from init(). + static void set(smart_interface * intf) + { s_instance = intf; } + +// Implementation +private: + smart_device::error_info m_err; + + friend smart_interface * smi(); // below + static smart_interface * s_instance; ///< Pointer to the interface object. + + // Prevent copy/assignment + smart_interface(const smart_interface &); + void operator=(const smart_interface &); +}; + + +///////////////////////////////////////////////////////////////////////////// +// smi() + +/// Global access to the (usually singleton) smart_interface +inline smart_interface * smi() + { return smart_interface::s_instance; } + +///////////////////////////////////////////////////////////////////////////// + +#endif // DEV_INTERFACE_H |