/*
* dev_interface.h
*
- * 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>
+ * Copyright (C) 2008-15 Christian Franke
*
* 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
#ifndef DEV_INTERFACE_H
#define DEV_INTERFACE_H
-#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 2915 2009-09-18 21:17:37Z chrfranke $\n"
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4152 2015-10-17 16:08:21Z chrfranke $\n"
-#include <stdarg.h>
+#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
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();
/// Downcast to SCSI device.
scsi_device * to_scsi()
{ return m_scsi_ptr; }
- /// Downcast to ATA device (const).
+ /// Downcast to SCSI device (const).
const scsi_device * to_scsi() const
{ return m_scsi_ptr; }
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)
/// 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();
///////////////////////////////////////////////
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; }
private:
smart_interface * m_intf;
device_info m_info;
+ error_info m_err;
+
+ // Pointers for to_ata(), to_scsi(),
+ // set by ATA/SCSI interface classes.
+ friend class ata_device;
ata_device * m_ata_ptr;
+ friend class scsi_device;
scsi_device * m_scsi_ptr;
- error_info m_err;
// Prevent copy/assigment
smart_device(const smart_device &);
/////////////////////////////////////////////////////////////////////////////
// ATA specific interface
-/// ATA register value and info whether is has been ever set
+/// ATA register value and info whether it has ever been set
// (Automatically set by first assignment)
class ata_register
{
ata_register()
: m_val(0x00), m_is_set(false) { }
- ata_register & operator=(unsigned char val)
- { m_val = val; m_is_set = true; return * this; }
+ ata_register & operator=(unsigned char x)
+ { m_val = x; m_is_set = true; return * this; }
unsigned char val() const
{ return m_val; }
ata_reg_alias_16(ata_register & lo, ata_register & hi)
: m_lo(lo), m_hi(hi) { }
- ata_reg_alias_16 & operator=(unsigned short val)
- { m_lo = (unsigned char) val;
- m_hi = (unsigned char)(val >> 8);
+ 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
};
+/// 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
//
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(); }
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();
};
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); }
};
virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0;
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); }
+ { hide_scsi(false); }
};
/////////////////////////////////////////////////////////////////////////////
+/// Smart pointer class for device pointers
-// Set dynamic downcasts
-// Note that due to virtual inheritance,
-// (ata == this) does not imply ((void*)ata == (void*)this))
-
-inline void smart_device::this_is_ata(ata_device * ata)
+template <class Dev>
+class any_device_auto_ptr
{
- m_ata_ptr = (ata == this ? ata : 0);
-}
+public:
+ typedef Dev device_type;
-inline void smart_device::this_is_scsi(scsi_device * scsi)
-{
- m_scsi_ptr = (scsi == this ? scsi : 0);
-}
+ /// 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() throw()
+ { 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;
/////////////////////////////////////////////////////////////////////////////
}
- void add(smart_device * dev)
- { m_list.push_back(dev); }
-
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); }
/// 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
/// 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()
/// 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).
/// 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 devices names.
+ /// Fill 'devlist' with devices of some 'type' with device names
/// specified by some optional 'pattern'.
/// Return false on error.
virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,