Diff for /embedaddon/smartmontools/os_win32.cpp between versions 1.1.1.2 and 1.1.1.4

version 1.1.1.2, 2012/10/09 09:36:45 version 1.1.1.4, 2013/10/14 07:54:03
Line 3 Line 3
  *   *
  * Home page of code is: http://smartmontools.sourceforge.net   * Home page of code is: http://smartmontools.sourceforge.net
  *   *
 * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net> * Copyright (C) 2004-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2012    Hank Wu <hank@areca.com.tw>   * Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
  *   *
  * This program is free software; you can redistribute it and/or modify   * This program is free software; you can redistribute it and/or modify
Line 28 Line 28
   
 #include "dev_interface.h"  #include "dev_interface.h"
 #include "dev_ata_cmd_set.h"  #include "dev_ata_cmd_set.h"
   #include "dev_areca.h"
   
 #include "os_win32/wmiquery.h"  #include "os_win32/wmiquery.h"
   
Line 48 Line 49
 #include <windows.h>  #include <windows.h>
   
 #if HAVE_NTDDDISK_H  #if HAVE_NTDDDISK_H
// i686-w64-mingw32, x86_64-w64-mingw32// i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32
 // (Missing: FILE_DEVICE_SCSI)  // (Missing: FILE_DEVICE_SCSI)
 #include <devioctl.h>  #include <devioctl.h>
 #include <ntdddisk.h>  #include <ntdddisk.h>
 #include <ntddscsi.h>  #include <ntddscsi.h>
 #include <ntddstor.h>  #include <ntddstor.h>
 #elif HAVE_DDK_NTDDDISK_H  #elif HAVE_DDK_NTDDDISK_H
// i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc// older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)  // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
 #include <ddk/ntdddisk.h>  #include <ddk/ntdddisk.h>
 #include <ddk/ntddscsi.h>  #include <ddk/ntddscsi.h>
Line 67 Line 68
 #include <winioctl.h>  #include <winioctl.h>
 #endif  #endif
   
   #ifndef _WIN32
   // csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin
   #define _WIN32
   #endif
   
 // CSMI support  // CSMI support
 #include "csmisas.h"  #include "csmisas.h"
   
#ifdef __CYGWIN__// Silence -Wunused-local-typedefs warning from g++ >= 4.8
#include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR#if __GNUC__ >= 4
 #define ATTR_UNUSED __attribute__((unused))
 #else
 #define ATTR_UNUSED /**/
 #endif  #endif
   
 // Macro to check constants at compile time using a dummy typedef  // Macro to check constants at compile time using a dummy typedef
 #define ASSERT_CONST(c, n) \  #define ASSERT_CONST(c, n) \
  typedef char assert_const_##c[((c) == (n)) ? 1 : -1]  typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED
 #define ASSERT_SIZEOF(t, n) \  #define ASSERT_SIZEOF(t, n) \
  typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]  typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED
   
 #ifndef _WIN64  #ifndef _WIN64
 #define SELECT_WIN_32_64(x32, x64) (x32)  #define SELECT_WIN_32_64(x32, x64) (x32)
Line 88 Line 97
   
 const char * os_win32_cpp_cvsid = "$Id$";  const char * os_win32_cpp_cvsid = "$Id$";
   
 // Disable Win9x/ME specific code if no longer supported by compiler.  
 #ifdef _WIN64  
   #undef WIN9X_SUPPORT  
 #elif !defined(WIN9X_SUPPORT)  
   #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)  
     // Win9x/ME support was dropped in Cygwin 1.7  
   #elif defined(_MSC_VER) && (_MSC_VER >= 1500)  
     // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)  
   #else  
     #define WIN9X_SUPPORT 1  
   #endif  
 #endif  
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 // Windows I/O-controls, some declarations are missing in the include files  // Windows I/O-controls, some declarations are missing in the include files
   
Line 320  namespace os_win32 { // no need to publish anything, n Line 316  namespace os_win32 { // no need to publish anything, n
 #pragma warning(disable:4250)  #pragma warning(disable:4250)
 #endif  #endif
   
 // Running on Win9x/ME ?  
 #if WIN9X_SUPPORT  
 // Set true in win9x_smart_interface ctor.  
 static bool win9x = false;  
 #else  
 // Never true (const allows compiler to remove dead code).  
 const  bool win9x = false;  
 #endif  
   
   
 class win_smart_device  class win_smart_device
 : virtual public /*implements*/ smart_device  : virtual public /*implements*/ smart_device
 {  {
Line 382  class win_ata_device (private) Line 368  class win_ata_device (private)
   std::string m_options;    std::string m_options;
   bool m_usr_options; // options set by user?    bool m_usr_options; // options set by user?
   bool m_admin; // open with admin access?    bool m_admin; // open with admin access?
     int m_phydrive; // PhysicalDriveN or -1
   bool m_id_is_cached; // ata_identify_is_cached() return value.    bool m_id_is_cached; // ata_identify_is_cached() return value.
  bool m_is_3ware; // AMCC/3ware controller detected?  bool m_is_3ware; // LSI/3ware controller detected?
  int m_drive, m_port;  int m_port; // LSI/3ware port
   int m_smartver_state;    int m_smartver_state;
 };  };
   
Line 409  class win_scsi_device (private) Line 396  class win_scsi_device (private)
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
   
 #if WIN9X_SUPPORT  
   
 class win_aspi_device  
 : public /*implements*/ scsi_device  
 {  
 public:  
   win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type);  
   
   virtual bool is_open() const;  
   
   virtual bool open();  
   
   virtual bool close();  
   
   virtual bool scsi_pass_through(scsi_cmnd_io * iop);  
   
 private:  
   int m_adapter;  
   unsigned char m_id;  
 };  
   
 #endif // WIN9X_SUPPORT  
   
   
 //////////////////////////////////////////////////////////////////////  
   
 class csmi_device  class csmi_device
 : virtual public /*extends*/ smart_device  : virtual public /*extends*/ smart_device
 {  {
Line 535  class win_tw_cli_device (private) Line 496  class win_tw_cli_device (private)
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 /// Areca RAID support  /// Areca RAID support
   
/* ARECA IO CONTROL CODE*////////////////////////////////////////////////////////////////////
#define ARCMSR_IOCTL_READ_RQBUFFER           0x90002004// SATA(ATA) device behind Areca RAID Controller
#define ARCMSR_IOCTL_WRITE_WQBUFFER          0x90002008class win_areca_ata_device
#define ARCMSR_IOCTL_CLEAR_RQBUFFER          0x9000200C: public /*implements*/ areca_ata_device,
#define ARCMSR_IOCTL_CLEAR_WQBUFFER          0x90002010  public /*extends*/ win_smart_device
#define ARCMSR_IOCTL_RETURN_CODE_3F          0x90002018 
#define ARECA_SIG_STR              "ARCMSR" 
 
 
// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver 
typedef struct _SRB_IO_CONTROL 
 {  {
  unsigned int HeaderLength;public:
  unsigned char Signature[8];  win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
  unsigned int Timeout;  virtual bool open();
  unsigned int ControlCode;  virtual smart_device * autodetect_open();
  unsigned int ReturnCode;  virtual bool arcmsr_lock();
  unsigned int Length;  virtual bool arcmsr_unlock();
} sSRB_IO_CONTROL;  virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
   
typedef struct _SRB_BUFFERprivate:
{  HANDLE m_mutex;
  sSRB_IO_CONTROL srbioctl;};
  unsigned char   ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware 
} sSRB_BUFFER; 
   
class win_areca_device///////////////////////////////////////////////////////////////////
: public /*implements*/ ata_device,// SAS(SCSI) device behind Areca RAID Controller
 class win_areca_scsi_device
 : public /*implements*/ areca_scsi_device,
   public /*extends*/ win_smart_device    public /*extends*/ win_smart_device
 {  {
 public:  public:
  win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum = 1);  win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
 
  static int arcmsr_command_handler(HANDLE fh, unsigned long arcmsr_cmd, unsigned char *data, int data_len); 
 
protected: 
   virtual bool open();    virtual bool open();
     virtual smart_device * autodetect_open();
     virtual bool arcmsr_lock();
     virtual bool arcmsr_unlock();
     virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
   
   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);  
   
   bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);  
   
 private:  private:
  int m_disknum; ///< Disk number.  HANDLE m_mutex;
  int m_encnum;  ///< Enclosure number. 
 };  };
   
   
 //////////////////////////////////////////////////////////////////////  //////////////////////////////////////////////////////////////////////
// Platform specific interfaces// Platform specific interface
   
 // Common to all windows flavors  
 class win_smart_interface  class win_smart_interface
 : public /*implements part of*/ smart_interface  : public /*implements part of*/ smart_interface
 {  {
Line 599  class win_smart_interface (public) Line 548  class win_smart_interface (public)
   virtual int64_t get_timer_usec();    virtual int64_t get_timer_usec();
 #endif  #endif
   
 //virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,  
 //  const char * pattern = 0);  
   
 protected:  
   virtual ata_device * get_ata_device(const char * name, const char * type);  
   
 //virtual scsi_device * get_scsi_device(const char * name, const char * type);  
   
   virtual smart_device * autodetect_smart_device(const char * name);  
 };  
   
 #if WIN9X_SUPPORT  
   
 // Win9x/ME reduced functionality  
 class win9x_smart_interface  
 : public /*extends*/ win_smart_interface  
 {  
 public:  
   win9x_smart_interface()  
     { win9x = true; }  
   
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,  
     const char * pattern = 0);  
   
 protected:  
   virtual scsi_device * get_scsi_device(const char * name, const char * type);  
   
 private:  
   bool ata_scan(smart_device_list & devlist);  
   
   bool scsi_scan(smart_device_list & devlist);  
 };  
   
 #endif // WIN9X_SUPPORT  
   
 // WinNT,2000,XP,...  
 class winnt_smart_interface  
 : public /*extends*/ win_smart_interface  
 {  
 public:  
   virtual bool disable_system_auto_standby(bool disable);    virtual bool disable_system_auto_standby(bool disable);
   
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,    virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
     const char * pattern = 0);      const char * pattern = 0);
   
 protected:  protected:
     virtual ata_device * get_ata_device(const char * name, const char * type);
   
   virtual scsi_device * get_scsi_device(const char * name, const char * type);    virtual scsi_device * get_scsi_device(const char * name, const char * type);
   
   virtual smart_device * autodetect_smart_device(const char * name);    virtual smart_device * autodetect_smart_device(const char * name);
Line 693  std::string win_smart_interface::get_os_version_str() Line 604  std::string win_smart_interface::get_os_version_str()
       return vstr;        return vstr;
   }    }
   
  if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)  const char * w = 0;
    return vstr;  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
   
  const char * w;    if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
  switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {      // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the
    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:      // actual OS version, see:
      w = (vi.szCSDVersion[1] == 'B' ||      // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
           vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:      ULONGLONG major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
      w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;      for (unsigned major = vi.dwMajorVersion; major <= 9; major++) {
    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;        OSVERSIONINFOEXA vi2; memset(&vi2, 0, sizeof(vi2));
  //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;        vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;
    case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;        if (!VerifyVersionInfo(&vi2, VER_MAJORVERSION, major_equal))
    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;          continue;
    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:        if (vi.dwMajorVersion < major) {
      w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"          vi.dwMajorVersion = major; vi.dwMinorVersion = 0;
                                                   :   "xp-mc"); break;        }
    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
      w = (!GetSystemMetrics(89/*SM_SERVERR2*/)    ?   "2003"        ULONGLONG minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
                                                   :   "2003r2"); break;        for (unsigned minor = vi.dwMinorVersion; minor <= 9; minor++) {
    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0:          memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);
      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "vista"          vi2.dwMinorVersion = minor;
                                                   :   "2008" );  break;          if (!VerifyVersionInfo(&vi2, VER_MINORVERSION, minor_equal))
    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 1:            continue;
      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win7"          vi.dwMinorVersion = minor;
                                                   :   "2008r2"); break;          break;
    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 2:        }
      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win8"
                                                   :   "2012"); break;        break;
    default: w = 0; break;      }
     }
 
     if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
       bool ws = (vi.wProductType <= VER_NT_WORKSTATION);
       switch (vi.dwMajorVersion << 4 | vi.dwMinorVersion) {
         case 0x50: w =       "2000";              break;
         case 0x51: w =       "xp";                break;
         case 0x52: w = (!GetSystemMetrics(89/*SM_SERVERR2*/)
                            ? "2003"  : "2003r2"); break;
         case 0x60: w = (ws ? "vista" : "2008"  ); break;
         case 0x61: w = (ws ? "win7"  : "2008r2"); break;
         case 0x62: w = (ws ? "win8"  : "2012"  ); break;
         case 0x63: w = (ws ? "win8.1": "2012r2"); break;
       }
     }
   }    }
   
   const char * w64 = "";    const char * w64 = "";
Line 732  std::string win_smart_interface::get_os_version_str() Line 658  std::string win_smart_interface::get_os_version_str()
 #endif  #endif
   
   if (!w)    if (!w)
    snprintf(vptr, vlen, "-%s%lu.%lu%s",    snprintf(vptr, vlen, "-%s%u.%u%s",
      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
      vi.dwMajorVersion, vi.dwMinorVersion, w64);      (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
   else if (vi.wServicePackMinor)    else if (vi.wServicePackMinor)
     snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);      snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
   else if (vi.wServicePackMajor)    else if (vi.wServicePackMajor)
Line 819  ata_device * win_smart_interface::get_ata_device(const Line 745  ata_device * win_smart_interface::get_ata_device(const
   return new win_ata_device(this, name, type);    return new win_ata_device(this, name, type);
 }  }
   
#ifdef WIN9X_SUPPORTscsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type)
 
scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type) 
 {  {
  return new win_aspi_device(this, name, type);  return new win_scsi_device(this, name, type);
 }  }
   
#endifstatic int sdxy_to_phydrive(const char (& xy)[2+1])
 
scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type) 
 {  {
  const char * testname = skipdev(name);  int phydrive = xy[0] - 'a';
  if (!strncmp(testname, "scsi", 4))  if (xy[1])
#if WIN9X_SUPPORT    phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
    return new win_aspi_device(this, name, type);  return phydrive;
#else 
    return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0); 
#endif 
  return new win_scsi_device(this, name, type); 
 }  }
   
 static win_dev_type get_dev_type(const char * name, int & phydrive)  static win_dev_type get_dev_type(const char * name, int & phydrive)
Line 857  static win_dev_type get_dev_type(const char * name, in Line 775  static win_dev_type get_dev_type(const char * name, in
     return (type != DEV_UNKNOWN ? type : DEV_SCSI);      return (type != DEV_UNKNOWN ? type : DEV_SCSI);
   }    }
   
  char drive[1+1] = "";  char drive[2+1] = "";
  if (sscanf(name, "sd%1[a-z]", drive) == 1) {  if (sscanf(name, "sd%2[a-z]", drive) == 1) {
    phydrive = drive[0] - 'a';    phydrive = sdxy_to_phydrive(drive);
     return get_phy_drive_type(phydrive);      return get_phy_drive_type(phydrive);
   }    }
   
Line 869  static win_dev_type get_dev_type(const char * name, in Line 787  static win_dev_type get_dev_type(const char * name, in
   return DEV_UNKNOWN;    return DEV_UNKNOWN;
 }  }
   
smart_device * win_smart_interface::autodetect_smart_device(const char * name)smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type)
 {  {
   const char * testname = skipdev(name);  
   if (!strncmp(testname, "hd", 2))  
     return new win_ata_device(this, name, "");  
 #if WIN9X_SUPPORT  
   if (!strncmp(testname, "scsi", 4))  
     return new win_aspi_device(this, name, "");  
 #endif  
   if (!strncmp(testname, "tw_cli", 6))  
     return new win_tw_cli_device(this, name, "");  
   return 0;  
 }  
   
   
 smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, const char * type)  
 {  
   // Areca?    // Areca?
   int disknum = -1, n1 = -1, n2 = -1;    int disknum = -1, n1 = -1, n2 = -1;
   int encnum = 1;    int encnum = 1;
   HANDLE fh = INVALID_HANDLE_VALUE;  
   char devpath[32];    char devpath[32];
   
   if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {    if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
Line 911  smart_device * winnt_smart_interface::get_custom_smart Line 813  smart_device * winnt_smart_interface::get_custom_smart
        1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and         1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
        2. map arcmsrX into "\\\\.\\scsiX"         2. map arcmsrX into "\\\\.\\scsiX"
       */        */
      for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {     for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
         memset(devpath, 0, sizeof(devpath));          memset(devpath, 0, sizeof(devpath));
        sprintf(devpath, "\\\\.\\scsi%d:", idx);        snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx);
        if ( (fh = CreateFile( devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,        win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum);
                               NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE ) {        if(arcdev->arcmsr_probe()) {
          if (win_areca_device::arcmsr_command_handler(fh, ARCMSR_IOCTL_RETURN_CODE_3F, NULL, 0) == 0) {          if(ctlrindex-- == 0) {
            if (ctlrindex-- == 0) {            return arcdev;
              return new win_areca_device(this, devpath, fh, disknum, encnum); 
            } 
           }            }
           CloseHandle(fh);  
         }          }
           delete arcdev;
       }        }
       set_err(ENOENT, "No Areca controller found");        set_err(ENOENT, "No Areca controller found");
     }      }
Line 933  smart_device * winnt_smart_interface::get_custom_smart Line 833  smart_device * winnt_smart_interface::get_custom_smart
   return 0;    return 0;
 }  }
   
std::string winnt_smart_interface::get_valid_custom_dev_types_str()std::string win_smart_interface::get_valid_custom_dev_types_str()
 {  {
   return "areca,N[/E]";    return "areca,N[/E]";
 }  }
   
   
smart_device * winnt_smart_interface::autodetect_smart_device(const char * name)smart_device * win_smart_interface::autodetect_smart_device(const char * name)
 {  {
  smart_device * dev = win_smart_interface::autodetect_smart_device(name);  const char * testname = skipdev(name);
  if (dev)  if (str_starts_with(testname, "hd"))
    return dev;    return new win_ata_device(this, name, "");
   
  if (!strncmp(skipdev(name), "csmi", 4))  if (str_starts_with(testname, "tw_cli"))
     return new win_tw_cli_device(this, name, "");
 
   if (str_starts_with(testname, "csmi"))
     return new win_csmi_device(this, name, "");      return new win_csmi_device(this, name, "");
   
   int phydrive = -1;    int phydrive = -1;
Line 975  smart_device * winnt_smart_interface::autodetect_smart Line 878  smart_device * winnt_smart_interface::autodetect_smart
 }  }
   
   
#if WIN9X_SUPPORT// Scan for devices
   
// Scan for devices on Win9x/MEbool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
 
bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist, 
   const char * type, const char * pattern /* = 0*/)    const char * type, const char * pattern /* = 0*/)
 {  {
   if (pattern) {    if (pattern) {
Line 987  bool win9x_smart_interface::scan_smart_devices(smart_d Line 888  bool win9x_smart_interface::scan_smart_devices(smart_d
     return false;      return false;
   }    }
   
  if (!type || !strcmp(type, "ata")) {  // Check for "[*,]pd" type
    if (!ata_scan(devlist))  bool pd = false;
      return false;  char type2[16+1] = "";
   if (type) {
     int nc = -1;
     if (!strcmp(type, "pd")) {
       pd = true;
       type = 0;
     }
     else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 &&
              nc == (int)strlen(type)) {
       pd = true;
       type = type2;
     }
   }    }
   
   if (!type || !strcmp(type, "scsi")) {  
     if (!scsi_scan(devlist))  
       return false;  
   }  
   return true;  
 }  
   
 #endif  // WIN9X_SUPPORT  
   
   
 // Scan for devices  
   
 bool winnt_smart_interface::scan_smart_devices(smart_device_list & devlist,  
   const char * type, const char * pattern /* = 0*/)  
 {  
   if (pattern) {  
     set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");  
     return false;  
   }  
   
   // Set valid types    // Set valid types
   bool ata, scsi, usb, csmi;    bool ata, scsi, usb, csmi;
   if (!type) {    if (!type) {
Line 1028  bool winnt_smart_interface::scan_smart_devices(smart_d Line 920  bool winnt_smart_interface::scan_smart_devices(smart_d
     else if (!strcmp(type, "csmi"))      else if (!strcmp(type, "csmi"))
       csmi = true;        csmi = true;
     else {      else {
      set_err(EINVAL, "Invalid type '%s', valid arguments are: ata, scsi, usb, csmi", type);      set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], usb[,pd], csmi, pd", type);
       return false;        return false;
     }      }
   }    }
   
   // Scan up to 10 drives and 2 3ware controllers  
   const int max_raid = 2;  
   bool raid_seen[max_raid] = {false, false};  
   
   char name[20];    char name[20];
   for (int i = 0; i <= 9; i++) {  
     sprintf(name, "/dev/sd%c", 'a'+i);  
     GETVERSIONINPARAMS_EX vers_ex;  
   
    switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {  if (ata || scsi || usb) {
      case DEV_ATA:    // Scan up to 128 drives and 2 3ware controllers
        // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA    const int max_raid = 2;
        if (!ata)    bool raid_seen[max_raid] = {false, false};
          continue; 
   
        // Interpret RAID drive map if present    for (int i = 0; i < 128; i++) {
        if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {      if (pd)
          // Skip if too many controllers or logical drive from this controller already seen        snprintf(name, sizeof(name), "/dev/pd%d", i);
          if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))      else if (i + 'a' <= 'z')
         snprintf(name, sizeof(name), "/dev/sd%c", i + 'a');
       else
         snprintf(name, sizeof(name), "/dev/sd%c%c",
                  i / ('z'-'a'+1) - 1 + 'a',
                  i % ('z'-'a'+1)     + 'a');
 
       GETVERSIONINPARAMS_EX vers_ex;
 
       switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {
         case DEV_ATA:
           // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
           if (!ata)
             continue;              continue;
          raid_seen[vers_ex.wControllerId] = true;
          // Add physical drives          // Interpret RAID drive map if present
          int len = strlen(name);          if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
          for (int pi = 0; pi < 32; pi++) {            // Skip if too many controllers or logical drive from this controller already seen
            if (vers_ex.dwDeviceMapEx & (1L << pi)) {            if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
              sprintf(name+len, ",%u", pi);              continue;
              devlist.push_back( new win_ata_device(this, name, "ata") );            raid_seen[vers_ex.wControllerId] = true;
             // Add physical drives
             int len = strlen(name);
             for (int pi = 0; pi < 32; pi++) {
               if (vers_ex.dwDeviceMapEx & (1L << pi)) {
                 snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
                 devlist.push_back( new win_ata_device(this, name, "ata") );
               }
             }              }
           }            }
        }          else {
        else {            devlist.push_back( new win_ata_device(this, name, "ata") );
          devlist.push_back( new win_ata_device(this, name, "ata") );          }
        }          break;
        break; 
   
      case DEV_SCSI:        case DEV_SCSI:
        // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...          // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
        if (!scsi)          if (!scsi)
          continue;            continue;
        devlist.push_back( new win_scsi_device(this, name, "scsi") );          devlist.push_back( new win_scsi_device(this, name, "scsi") );
        break;          break;
   
      case DEV_USB:        case DEV_USB:
        // STORAGE_QUERY_PROPERTY returned USB          // STORAGE_QUERY_PROPERTY returned USB
        if (!usb)          if (!usb)
          continue; 
        { 
          // TODO: Use common function for this and autodetect_smart_device() 
          // Get USB bridge ID 
          unsigned short vendor_id = 0, product_id = 0; 
          if (!get_usb_id(i, vendor_id, product_id)) 
             continue;              continue;
          // Get type name for this ID          {
          const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);            // TODO: Use common function for this and autodetect_smart_device()
          if (!usbtype)            // Get USB bridge ID
            continue;            unsigned short vendor_id = 0, product_id = 0;
          // Return SAT/USB device for this type            if (!get_usb_id(i, vendor_id, product_id))
          ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, ""));              continue;
          if (!dev)            // Get type name for this ID
            continue;            const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
          devlist.push_back(dev);            if (!usbtype)
        }              continue;
        break;            // Return SAT/USB device for this type
             ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, ""));
             if (!dev)
               continue;
             devlist.push_back(dev);
           }
           break;
   
      default:        default:
        // Unknown type          // Unknown type
        break;          break;
       }
     }      }
   }    }
   
Line 1132  std::string win_smart_interface::get_app_examples(cons Line 1035  std::string win_smart_interface::get_app_examples(cons
   if (strcmp(appname, "smartctl"))    if (strcmp(appname, "smartctl"))
     return "";      return "";
   return "=================================================== SMARTCTL EXAMPLES =====\n\n"    return "=================================================== SMARTCTL EXAMPLES =====\n\n"
         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"         "  smartctl -a /dev/sda                       (Prints all SMART information)\n\n"
         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
          "                                              (Enables SMART on first disk)\n\n"           "                                              (Enables SMART on first disk)\n\n"
         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"         "  smartctl -t long /dev/sda              (Executes extended disk self-test)\n\n"
         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
          "                                      (Prints Self-Test & Attribute errors)\n"           "                                      (Prints Self-Test & Attribute errors)\n"
 #if WIN9X_SUPPORT  
          "  smartctl -a /dev/scsi21\n"  
          "             (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"  
 #endif  
          "  smartctl -a /dev/sda\n"           "  smartctl -a /dev/sda\n"
         "             (Prints all information for SCSI disk on PhysicalDrive 0)\n"         "             (Prints all information for disk on PhysicalDrive 0)\n"
          "  smartctl -a /dev/pd3\n"           "  smartctl -a /dev/pd3\n"
         "             (Prints all information for SCSI disk on PhysicalDrive 3)\n"         "             (Prints all information for disk on PhysicalDrive 3)\n"
          "  smartctl -a /dev/tape1\n"           "  smartctl -a /dev/tape1\n"
          "             (Prints all information for SCSI tape on Tape 1)\n"           "             (Prints all information for SCSI tape on Tape 1)\n"
          "  smartctl -A /dev/hdb,3\n"           "  smartctl -A /dev/hdb,3\n"
Line 1159  std::string win_smart_interface::get_app_examples(cons Line 1058  std::string win_smart_interface::get_app_examples(cons
          "  ATA SMART access methods and ordering may be specified by modifiers\n"           "  ATA SMART access methods and ordering may be specified by modifiers\n"
          "  following the device name: /dev/hdX:[saicm], where\n"           "  following the device name: /dev/hdX:[saicm], where\n"
          "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"           "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
         "  'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"         "  'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
         "  'f': IOCTL_STORAGE_*,        'm': IOCTL_SCSI_MINIPORT_*.\n"         "  'm': IOCTL_SCSI_MINIPORT_*.\n"
       + strprintf(        + strprintf(
          "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()           "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
         );          );
 }  }
   
   
bool winnt_smart_interface::disable_system_auto_standby(bool disable)bool win_smart_interface::disable_system_auto_standby(bool disable)
 {  {
   if (disable) {    if (disable) {
     SYSTEM_POWER_STATUS ps;      SYSTEM_POWER_STATUS ps;
Line 1199  bool winnt_smart_interface::disable_system_auto_standb Line 1098  bool winnt_smart_interface::disable_system_auto_standb
 static void print_ide_regs(const IDEREGS * r, int out)  static void print_ide_regs(const IDEREGS * r, int out)
 {  {
   pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",    pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
  (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,    (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
  r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);    r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
 }  }
   
 static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)  static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
Line 1224  static int smart_get_version(HANDLE hdevice, GETVERSIO Line 1123  static int smart_get_version(HANDLE hdevice, GETVERSIO
   if (!DeviceIoControl(hdevice, SMART_GET_VERSION,    if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
     NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {      NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
     if (ata_debugmode)      if (ata_debugmode)
      pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());      pout("  SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
     errno = ENOSYS;      errno = ENOSYS;
     return -1;      return -1;
   }    }
   assert(num_out == sizeof(GETVERSIONINPARAMS));    assert(num_out == sizeof(GETVERSIONINPARAMS));
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  SMART_GET_VERSION suceeded, bytes returned: %lu\n"    pout("  SMART_GET_VERSION suceeded, bytes returned: %u\n"
         "    Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",         "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
      num_out, vers.bVersion, vers.bRevision,      (unsigned)num_out, vers.bVersion, vers.bRevision,
      vers.fCapabilities, vers.bIDEDeviceMap);      (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
     if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)      if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
      pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",      pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
      vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);      vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx);
   }    }
   
   if (ata_version_ex)    if (ata_version_ex)
Line 1250  static int smart_get_version(HANDLE hdevice, GETVERSIO Line 1149  static int smart_get_version(HANDLE hdevice, GETVERSIO
   
 // call SMART_* ioctl  // call SMART_* ioctl
   
static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
 {  {
   SENDCMDINPARAMS inpar;    SENDCMDINPARAMS inpar;
   SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;    SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
Line 1263  static int smart_ioctl(HANDLE hdevice, int drive, IDER Line 1162  static int smart_ioctl(HANDLE hdevice, int drive, IDER
   
   memset(&inpar, 0, sizeof(inpar));    memset(&inpar, 0, sizeof(inpar));
   inpar.irDriveRegs = *regs;    inpar.irDriveRegs = *regs;
   // drive is set to 0-3 on Win9x only  
   inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);  
   inpar.bDriveNumber = drive;  
   
     // Older drivers may require bits 5 and 7 set
     // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
     inpar.irDriveRegs.bDriveHeadReg |= 0xa0;
   
     // Drive number 0-3 was required on Win9x/ME only
     //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
     //inpar.bDriveNumber = drive;
   
   if (port >= 0) {    if (port >= 0) {
     // Set RAID port      // Set RAID port
     inpar_ex.wIdentifier = SMART_VENDOR_3WARE;      inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
Line 1294  static int smart_ioctl(HANDLE hdevice, int drive, IDER Line 1198  static int smart_ioctl(HANDLE hdevice, int drive, IDER
   
   if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,    if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
     outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {      outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
    // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()    // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
     long err = GetLastError();      long err = GetLastError();
     if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {      if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
       pout("  %s failed, Error=%ld\n", name, err);        pout("  %s failed, Error=%ld\n", name, err);
Line 1320  static int smart_ioctl(HANDLE hdevice, int drive, IDER Line 1224  static int smart_ioctl(HANDLE hdevice, int drive, IDER
   }    }
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,    pout("  %s suceeded, bytes returned: %u (buffer %u)\n", name,
      num_out, outpar->cBufferSize);      (unsigned)num_out, (unsigned)outpar->cBufferSize);
     print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?      print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
       (const IDEREGS *)(outpar->bBuffer) : NULL));        (const IDEREGS *)(outpar->bBuffer) : NULL));
   }    }
Line 1397  static int ide_pass_through_ioctl(HANDLE hdevice, IDER Line 1301  static int ide_pass_through_ioctl(HANDLE hdevice, IDER
     if (   num_out != size      if (   num_out != size
         || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {          || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
       if (ata_debugmode) {        if (ata_debugmode) {
        pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",        pout("  IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
          num_out, buf->DataBufferSize);          (unsigned)num_out, (unsigned)buf->DataBufferSize);
         print_ide_regs_io(regs, &buf->IdeReg);          print_ide_regs_io(regs, &buf->IdeReg);
       }        }
       VirtualFree(buf, 0, MEM_RELEASE);        VirtualFree(buf, 0, MEM_RELEASE);
Line 1409  static int ide_pass_through_ioctl(HANDLE hdevice, IDER Line 1313  static int ide_pass_through_ioctl(HANDLE hdevice, IDER
   }    }
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
      num_out, buf->DataBufferSize);      (unsigned)num_out, (unsigned)buf->DataBufferSize);
     print_ide_regs_io(regs, &buf->IdeReg);      print_ide_regs_io(regs, &buf->IdeReg);
   }    }
   *regs = buf->IdeReg;    *regs = buf->IdeReg;
Line 1516  static int ata_pass_through_ioctl(HANDLE hdevice, IDER Line 1420  static int ata_pass_through_ioctl(HANDLE hdevice, IDER
     if (   num_out != size      if (   num_out != size
         || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {          || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
       if (ata_debugmode) {        if (ata_debugmode) {
        pout("  IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);        pout("  IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
         print_ide_regs_io(regs, ctfregs);          print_ide_regs_io(regs, ctfregs);
       }        }
       errno = EIO;        errno = EIO;
Line 1526  static int ata_pass_through_ioctl(HANDLE hdevice, IDER Line 1430  static int ata_pass_through_ioctl(HANDLE hdevice, IDER
   }    }
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, ctfregs);      print_ide_regs_io(regs, ctfregs);
   }    }
   *regs = *ctfregs;    *regs = *ctfregs;
Line 1538  static int ata_pass_through_ioctl(HANDLE hdevice, IDER Line 1442  static int ata_pass_through_ioctl(HANDLE hdevice, IDER
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)  
   
 // undocumented SCSI opcode to for ATA passthrough  
 #define SCSIOP_ATA_PASSTHROUGH    0xCC  
   
 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)  
 {  
   typedef struct {  
     SCSI_PASS_THROUGH spt;  
     ULONG Filler;  
     UCHAR ucSenseBuf[32];  
     UCHAR ucDataBuf[512];  
   } SCSI_PASS_THROUGH_WITH_BUFFERS;  
   
   SCSI_PASS_THROUGH_WITH_BUFFERS sb;  
   IDEREGS * cdbregs;  
   unsigned int size;  
   DWORD num_out;  
   const unsigned char magic = 0xcf;  
   
   memset(&sb, 0, sizeof(sb));  
   sb.spt.Length = sizeof(SCSI_PASS_THROUGH);  
   //sb.spt.PathId = 0;  
   sb.spt.TargetId = 1;  
   //sb.spt.Lun = 0;  
   sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;  
   sb.spt.TimeOutValue = 10;  
   sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);  
   size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);  
   sb.spt.DataBufferOffset = size;  
   
   if (datasize) {  
     if (datasize > sizeof(sb.ucDataBuf)) {  
       errno = EINVAL;  
       return -1;  
     }  
     sb.spt.DataIn = SCSI_IOCTL_DATA_IN;  
     sb.spt.DataTransferLength = datasize;  
     size += datasize;  
     sb.ucDataBuf[0] = magic;  
   }  
   else {  
     sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;  
     //sb.spt.DataTransferLength = 0;  
   }  
   
   // Use pseudo SCSI command followed by registers  
   sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;  
   cdbregs = (IDEREGS *)(sb.spt.Cdb+2);  
   *cdbregs = *regs;  
   
   if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,  
     &sb, size, &sb, size, &num_out, NULL)) {  
     long err = GetLastError();  
     if (ata_debugmode)  
       pout("  ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);  
     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);  
     return -1;  
   }  
   
   // Cannot check ATA status, because command does not return IDEREGS  
   
   // Check and copy data  
   if (datasize) {  
     if (   num_out != size  
         || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {  
       if (ata_debugmode) {  
         pout("  ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);  
         print_ide_regs_io(regs, NULL);  
       }  
       errno = EIO;  
       return -1;  
     }  
     memcpy(data, sb.ucDataBuf, datasize);  
   }  
   
   if (ata_debugmode > 1) {  
     pout("  ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);  
     print_ide_regs_io(regs, NULL);  
   }  
   return 0;  
 }  
   
   
 /////////////////////////////////////////////////////////////////////////////  
 // SMART IOCTL via SCSI MINIPORT ioctl  // SMART IOCTL via SCSI MINIPORT ioctl
   
 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI  // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
Line 1724  static int ata_via_scsi_miniport_smart_ioctl(HANDLE hd Line 1543  static int ata_via_scsi_miniport_smart_ioctl(HANDLE hd
   // Check result    // Check result
   if (sb.srbc.ReturnCode) {    if (sb.srbc.ReturnCode) {
     if (ata_debugmode) {      if (ata_debugmode) {
      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode);
       print_ide_regs_io(regs, NULL);        print_ide_regs_io(regs, NULL);
     }      }
     errno = EIO;      errno = EIO;
Line 1742  static int ata_via_scsi_miniport_smart_ioctl(HANDLE hd Line 1561  static int ata_via_scsi_miniport_smart_ioctl(HANDLE hd
   }    }
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name,
      num_out, sb.params.out.cBufferSize);      (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
     print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?      print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
                              (const IDEREGS *)(sb.params.out.bBuffer) : 0));                               (const IDEREGS *)(sb.params.out.bBuffer) : 0));
   }    }
Line 1775  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice Line 1594  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
     return -1;      return -1;
   }    }
   memset(&sb, 0, sizeof(sb));    memset(&sb, 0, sizeof(sb));
  strcpy((char *)sb.srbc.Signature, "<3ware>");  strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
   sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);    sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
   sb.srbc.Timeout = 60; // seconds    sb.srbc.Timeout = 60; // seconds
   sb.srbc.ControlCode = 0xA0000000;    sb.srbc.ControlCode = 0xA0000000;
Line 1798  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice Line 1617  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
   
   if (sb.srbc.ReturnCode) {    if (sb.srbc.ReturnCode) {
     if (ata_debugmode) {      if (ata_debugmode) {
      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode);
       print_ide_regs_io(regs, NULL);        print_ide_regs_io(regs, NULL);
     }      }
     errno = EIO;      errno = EIO;
Line 1810  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice Line 1629  static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
     memcpy(data, sb.buffer, datasize);      memcpy(data, sb.buffer, datasize);
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, &sb.regs);      print_ide_regs_io(regs, &sb.regs);
   }    }
   *regs = sb.regs;    *regs = sb.regs;
Line 1828  static int update_3ware_devicemap_ioctl(HANDLE hdevice Line 1647  static int update_3ware_devicemap_ioctl(HANDLE hdevice
 {  {
   SRB_IO_CONTROL srbc;    SRB_IO_CONTROL srbc;
   memset(&srbc, 0, sizeof(srbc));    memset(&srbc, 0, sizeof(srbc));
  strcpy((char *)srbc.Signature, "<3ware>");  strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
   srbc.HeaderLength = sizeof(SRB_IO_CONTROL);    srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
   srbc.Timeout = 60; // seconds    srbc.Timeout = 60; // seconds
   srbc.ControlCode = 0xCC010014;    srbc.ControlCode = 0xCC010014;
Line 1846  static int update_3ware_devicemap_ioctl(HANDLE hdevice Line 1665  static int update_3ware_devicemap_ioctl(HANDLE hdevice
   }    }
   if (srbc.ReturnCode) {    if (srbc.ReturnCode) {
     if (ata_debugmode)      if (ata_debugmode)
      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode);
     errno = EIO;      errno = EIO;
     return -1;      return -1;
   }    }
Line 2135  static int storage_query_property_ioctl(HANDLE hdevice Line 1954  static int storage_query_property_ioctl(HANDLE hdevice
   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,    if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
     &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {      &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
     if (ata_debugmode > 1 || scsi_debugmode > 1)      if (ata_debugmode > 1 || scsi_debugmode > 1)
      pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());      pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
     errno = ENOSYS;      errno = ENOSYS;
     return -1;      return -1;
   }    }
Line 2173  static int storage_predict_failure_ioctl(HANDLE hdevic Line 1992  static int storage_predict_failure_ioctl(HANDLE hdevic
   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,    if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
     0, 0, &pred, sizeof(pred), &num_out, NULL)) {      0, 0, &pred, sizeof(pred), &num_out, NULL)) {
     if (ata_debugmode > 1)      if (ata_debugmode > 1)
      pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());      pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
     errno = ENOSYS;      errno = ENOSYS;
     return -1;      return -1;
   }    }
   
   if (ata_debugmode > 1) {    if (ata_debugmode > 1) {
     pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"      pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
         "    PredictFailure: 0x%08lx\n"         "    PredictFailure: 0x%08x\n"
          "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",           "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
         pred.PredictFailure,         (unsigned)pred.PredictFailure,
          pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],           pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
          pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]           pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
     );      );
Line 2330  static int get_identify_from_device_property(HANDLE hd Line 2149  static int get_identify_from_device_property(HANDLE hd
   return 0;    return 0;
 }  }
   
   // Get Serial Number in IDENTIFY from WMI
   static bool get_serial_from_wmi(int drive, ata_identify_device * id)
   {
     bool debug = (ata_debugmode > 1);
   
     wbem_services ws;
     if (!ws.connect()) {
       if (debug)
         pout("WMI connect failed\n");
       return false;
     }
   
     wbem_object wo;
     if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
                        "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
       return false;
   
     std::string serial = wo.get_str("SerialNumber");
     if (debug)
       pout("  WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str());
   
     copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no));
     return true;
   }
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 // USB ID detection using WMI  // USB ID detection using WMI
   
Line 2416  static bool get_usb_id(int drive, unsigned short & ven Line 2260  static bool get_usb_id(int drive, unsigned short & ven
         continue;          continue;
       }        }
   
      // Fail if previos USB bridge is associated to other controller or ID is unknown      // Fail if previous USB bridge is associated to other controller or ID is unknown
       if (!(ant == prev_usb_ant && prev_usb_venid)) {        if (!(ant == prev_usb_ant && prev_usb_venid)) {
         if (debug)          if (debug)
           pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());            pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
Line 2459  static bool get_usb_id(int drive, unsigned short & ven Line 2303  static bool get_usb_id(int drive, unsigned short & ven
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
   
// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)// Call GetDevicePowerState()
 // returns: 1=active, 0=standby, -1=error  // returns: 1=active, 0=standby, -1=error
 // (This would also work for SCSI drives)  // (This would also work for SCSI drives)
   
 static int get_device_power_state(HANDLE hdevice)  static int get_device_power_state(HANDLE hdevice)
 {  {
   static bool unsupported = false;  
   if (unsupported) {  
     errno = ENOSYS;  
     return -1;  
   }  
   
 #ifdef __CYGWIN__  
   static DWORD kernel_dll_pid = 0;  
 #endif  
   static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;  
   
   if (!GetDevicePowerState_p  
 #ifdef __CYGWIN__  
       || kernel_dll_pid != GetCurrentProcessId() // detect fork()  
 #endif  
      ) {  
     if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *))  
           GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) {  
       if (ata_debugmode)  
         pout("  GetDevicePowerState() not found, Error=%ld\n", GetLastError());  
       unsupported = true;  
       errno = ENOSYS;  
       return -1;  
     }  
 #ifdef __CYGWIN__  
     kernel_dll_pid = GetCurrentProcessId();  
 #endif  
   }  
   
   BOOL state = TRUE;    BOOL state = TRUE;
  if (!GetDevicePowerState_p(hdevice, &state)) {  if (!GetDevicePowerState(hdevice, &state)) {
     long err = GetLastError();      long err = GetLastError();
     if (ata_debugmode)      if (ata_debugmode)
       pout("  GetDevicePowerState() failed, Error=%ld\n", err);        pout("  GetDevicePowerState() failed, Error=%ld\n", err);
Line 2513  static int get_device_power_state(HANDLE hdevice) Line 2328  static int get_device_power_state(HANDLE hdevice)
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
   
 #if WIN9X_SUPPORT  
 // Print SMARTVSD error message, return errno  
   
 static int smartvsd_error()  
 {  
   char path[MAX_PATH];  
   unsigned len;  
   if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))  
     return ENOENT;  
   // SMARTVSD.VXD present?  
   strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");  
   if (!access(path, 0)) {  
     // Yes, standard IDE driver used?  
     HANDLE h;  
     if (   (h = CreateFileA("\\\\.\\ESDI_506",  
                  GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,  
                  NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE  
         && GetLastError() == ERROR_FILE_NOT_FOUND                             ) {  
       pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");  
       return ENOENT;  
     }  
     else {  
       if (h != INVALID_HANDLE_VALUE) // should not happen  
         CloseHandle(h);  
       pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");  
       return ENOSYS;  
     }  
   }  
   else {  
     strcpy(path+len, "\\SMARTVSD.VXD");  
     if (!access(path, 0)) {  
       // Some Windows versions install SMARTVSD.VXD in SYSTEM directory  
       // (http://support.microsoft.com/kb/265854/en-us).  
       path[len] = 0;  
       pout("SMART driver is not properly installed,\n"  
          " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"  
          " and reboot Windows.\n", path, path);  
     }  
     else {  
       // Some Windows versions do not provide SMARTVSD.VXD  
       // (http://support.microsoft.com/kb/199886/en-us).  
       path[len] = 0;  
       pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);  
     }  
     return ENOSYS;  
   }  
 }  
   
 #endif // WIN9X_SUPPORT  
   
 // Get default ATA device options  // Get default ATA device options
   
 static const char * ata_get_def_options()  static const char * ata_get_def_options()
 {  {
  DWORD ver = GetVersion();  return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
  if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME                   // STORAGE_*, SCSI_MINIPORT_*
    return "s"; // SMART_* only 
  else if ((ver & 0xff) == 4) // WinNT4 
    return "sc"; // SMART_*, SCSI_PASS_THROUGH 
  else // WinXP, 2003, Vista 
    return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH, 
                     // STORAGE_*, SCSI_MINIPORT_* 
 }  }
   
   
Line 2606  win_ata_device::win_ata_device(smart_interface * intf, Line 2365  win_ata_device::win_ata_device(smart_interface * intf,
 : smart_device(intf, dev_name, "ata", req_type),  : smart_device(intf, dev_name, "ata", req_type),
   m_usr_options(false),    m_usr_options(false),
   m_admin(false),    m_admin(false),
     m_phydrive(-1),
   m_id_is_cached(false),    m_id_is_cached(false),
   m_is_3ware(false),    m_is_3ware(false),
   m_drive(0),  
   m_port(-1),    m_port(-1),
   m_smartver_state(0)    m_smartver_state(0)
 {  {
Line 2624  win_ata_device::~win_ata_device() throw() Line 2383  win_ata_device::~win_ata_device() throw()
 bool win_ata_device::open()  bool win_ata_device::open()
 {  {
   const char * name = skipdev(get_dev_name()); int len = strlen(name);    const char * name = skipdev(get_dev_name()); int len = strlen(name);
  // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options  // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
  char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;  char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
  if (   sscanf(name, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1  if (   sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1
      && ((n1 == len && !options[0]) || n2 == len)                                       ) {      && ((n1 == len && !options[0]) || n2 == len)                                   ) {
    return open(drive[0] - 'a', -1, options, -1);    return open(sdxy_to_phydrive(drive), -1, options, -1);
   }    }
  // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options  // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
   drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;    drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
   unsigned port = ~0;    unsigned port = ~0;
  if (   sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2  if (   sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2
      && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                     ) {      && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                  ) {
    return open(drive[0] - 'a', -1, options, port);    return open(sdxy_to_phydrive(drive), -1, options, port);
   }    }
   // pd<m>,N => Physical drive <m>, RAID port N    // pd<m>,N => Physical drive <m>, RAID port N
   int phydrive = -1; port = ~0; n1 = -1; n2 = -1;    int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
Line 2655  bool win_ata_device::open() Line 2414  bool win_ata_device::open()
   
 bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)  bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
 {  {
  // path depends on Windows Version  m_phydrive = -1;
   char devpath[30];    char devpath[30];
  if (win9x && 0 <= phydrive && phydrive <= 7)  if (0 <= phydrive && phydrive <= 255)
    // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive));
    strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));  else if (0 <= logdrive && logdrive <= 'Z'-'A')
  else if (!win9x && 0 <= phydrive && phydrive <= 255) 
    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive); 
  else if (!win9x && 0 <= logdrive && logdrive <= 'Z'-'A') 
     snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);      snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
   else    else
     return set_err(ENOENT);      return set_err(ENOENT);
   
   // Open device    // Open device
   HANDLE h = INVALID_HANDLE_VALUE;    HANDLE h = INVALID_HANDLE_VALUE;
  if (win9x || !(*options && !options[strspn(options, "fp")])) {  if (!(*options && !options[strspn(options, "fp")])) {
     // Open with admin rights      // Open with admin rights
     m_admin = true;      m_admin = true;
     h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,      h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
       FILE_SHARE_READ|FILE_SHARE_WRITE,        FILE_SHARE_READ|FILE_SHARE_WRITE,
       NULL, OPEN_EXISTING, 0, 0);        NULL, OPEN_EXISTING, 0, 0);
   }    }
  if (!win9x && h == INVALID_HANDLE_VALUE) {  if (h == INVALID_HANDLE_VALUE) {
     // Open without admin rights      // Open without admin rights
     m_admin = false;      m_admin = false;
     h = CreateFileA(devpath, 0,      h = CreateFileA(devpath, 0,
Line 2685  bool win_ata_device::open(int phydrive, int logdrive,  Line 2441  bool win_ata_device::open(int phydrive, int logdrive, 
   }    }
   if (h == INVALID_HANDLE_VALUE) {    if (h == INVALID_HANDLE_VALUE) {
     long err = GetLastError();      long err = GetLastError();
 #if WIN9X_SUPPORT  
     if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND)  
       smartvsd_error();  
 #endif  
     if (err == ERROR_FILE_NOT_FOUND)      if (err == ERROR_FILE_NOT_FOUND)
       set_err(ENOENT, "%s: not found", devpath);        set_err(ENOENT, "%s: not found", devpath);
     else if (err == ERROR_ACCESS_DENIED)      else if (err == ERROR_ACCESS_DENIED)
Line 2725  bool win_ata_device::open(int phydrive, int logdrive,  Line 2477  bool win_ata_device::open(int phydrive, int logdrive, 
     m_options = def_options;      m_options = def_options;
   }    }
   
  // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call  // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
  m_drive = 0; m_port = port;  m_port = port;
  if (!win9x && port < 0)  if (port < 0)
     return true;      return true;
   
  // Win9X/ME: Get drive map  // 3ware RAID: Get port map
  // RAID: Get port map 
   GETVERSIONINPARAMS_EX vers_ex;    GETVERSIONINPARAMS_EX vers_ex;
   int devmap = smart_get_version(h, &vers_ex);    int devmap = smart_get_version(h, &vers_ex);
   
Line 2760  bool win_ata_device::open(int phydrive, int logdrive,  Line 2511  bool win_ata_device::open(int phydrive, int logdrive, 
   }    }
   m_smartver_state = 1;    m_smartver_state = 1;
   
  if (port >= 0) {  {
     // 3ware RAID: update devicemap first      // 3ware RAID: update devicemap first
   
     if (!update_3ware_devicemap_ioctl(h)) {      if (!update_3ware_devicemap_ioctl(h)) {
Line 2775  bool win_ata_device::open(int phydrive, int logdrive,  Line 2526  bool win_ata_device::open(int phydrive, int logdrive, 
         return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);          return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
       }        }
     }      }
     return true;  
   }    }
   
   // Win9x/ME: Check device presence & type  
   if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) {  
     unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10;  
     // Win9x drive existence check may not work as expected  
     // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01  
     // (The related KB Article Q196120 is no longer available)  
     if (!is_permissive()) {  
       close();  
       return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",  
         devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap);  
     }  
   }  
   // Drive number must be passed to ioctl  
   m_drive = (phydrive & 0x3);  
   return true;    return true;
 }  }
   
   
 #if WIN9X_SUPPORT  
   
 // Scan for ATA drives on Win9x/ME  
   
 bool win9x_smart_interface::ata_scan(smart_device_list & devlist)  
 {  
   // Open device  
   const char devpath[] = "\\\\.\\SMARTVSD";  
   HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,  
     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);  
   if (h == INVALID_HANDLE_VALUE) {  
     if (ata_debugmode > 1)  
       pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());  
     return true; // SMARTVSD.VXD missing or no ATA devices  
   }  
   
   // Get drive map  
   int devmap = smart_get_version(h);  
   CloseHandle(h);  
   if (devmap < 0)  
     return true; // Should not happen  
   
   // Check ATA device presence, remove ATAPI devices  
   devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf);  
   char name[20];  
   for (int i = 0; i < 4; i++) {  
     if (!(devmap & (1 << i)))  
       continue;  
     sprintf(name, "/dev/hd%c", 'a'+i);  
     devlist.push_back( new win_ata_device(this, name, "ata") );  
   }  
   return true;  
 }  
   
 #endif // WIN9X_SUPPORT  
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
   
 // Interface to ATA devices  // Interface to ATA devices
Line 2840  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2539  bool win_ata_device::ata_pass_through(const ata_cmd_in
 {  {
   // No multi-sector support for now, see above    // No multi-sector support for now, see above
   // warning about IOCTL_ATA_PASS_THROUGH    // warning about IOCTL_ATA_PASS_THROUGH
  if (!ata_cmd_is_ok(in,  if (!ata_cmd_is_supported(in,
    true, // data_out_support    ata_device::supports_data_out |
    false, // !multi_sector_support    ata_device::supports_output_regs |
    true) // ata_48bit_support    ata_device::supports_48bit)
   )    )
     return false;      return false;
   
Line 2861  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2560  bool win_ata_device::ata_pass_through(const ata_cmd_in
     case ATA_IDENTIFY_PACKET_DEVICE:      case ATA_IDENTIFY_PACKET_DEVICE:
       // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE        // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
       // and SCSI_MINIPORT_* if requested by user        // and SCSI_MINIPORT_* if requested by user
      valid_options = (m_usr_options ? "saicmf" : "saicf");      valid_options = (m_usr_options ? "saimf" : "saif");
       break;        break;
   
     case ATA_CHECK_POWER_MODE:      case ATA_CHECK_POWER_MODE:
Line 2879  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2578  bool win_ata_device::ata_pass_through(const ata_cmd_in
         case ATA_SMART_AUTO_OFFLINE:          case ATA_SMART_AUTO_OFFLINE:
           // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE            // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
           // and SCSI_MINIPORT_* if requested by user            // and SCSI_MINIPORT_* if requested by user
          valid_options = (m_usr_options ? "saicmf" : "saicf");          valid_options = (m_usr_options ? "saimf" : "saif");
           break;            break;
   
         case ATA_SMART_IMMEDIATE_OFFLINE:          case ATA_SMART_IMMEDIATE_OFFLINE:
          // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME          // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
          valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ?          valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ?
                           "saicm3" : "aicm3");                           "saim3" : "aim3");
           break;            break;
   
         case ATA_SMART_READ_LOG_SECTOR:          case ATA_SMART_READ_LOG_SECTOR:
          // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME          // SMART_RCV_DRIVE_DATA does not support READ_LOG
           // Try SCSI_MINIPORT also to skip buggy class driver            // Try SCSI_MINIPORT also to skip buggy class driver
           // SMART functions do not support multi sector I/O.            // SMART functions do not support multi sector I/O.
           if (in.size == 512)            if (in.size == 512)
            valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3");            valid_options = (m_usr_options ? "saim3" : "aim3");
           else            else
             valid_options = "a";              valid_options = "a";
           break;            break;
Line 2905  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2604  bool win_ata_device::ata_pass_through(const ata_cmd_in
           break;            break;
   
         case ATA_SMART_STATUS:          case ATA_SMART_STATUS:
          // May require lba_mid,lba_high register return          valid_options = (m_usr_options ? "saimf" : "saif");
          if (in.out_needed.is_set()) 
            valid_options = (m_usr_options ? "saimf" : "saif"); 
          else 
            valid_options = (m_usr_options ? "saicmf" : "saicf"); 
           break;            break;
   
         default:          default:
Line 2930  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2625  bool win_ata_device::ata_pass_through(const ata_cmd_in
          ||  in.in_regs.is_48bit_cmd()                               )           ||  in.in_regs.is_48bit_cmd()                               )
       // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only        // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
       valid_options = "a";        valid_options = "a";
     else if (in.out_needed.is_set())  
       // Need output registers: ATA/IDE_PASS_THROUGH  
       valid_options = "ai";  
     else      else
      valid_options = "aic";      // ATA/IDE_PASS_THROUGH
       valid_options = "ai";
   }    }
   
   if (!m_admin) {    if (!m_admin) {
Line 3047  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2740  bool win_ata_device::ata_pass_through(const ata_cmd_in
   
           m_smartver_state = 1;            m_smartver_state = 1;
         }          }
        rc = smart_ioctl(get_fh(), m_drive, &regs, data, datasize, m_port);        rc = smart_ioctl(get_fh(), &regs, data, datasize, m_port);
         out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);          out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
        id_is_cached = (m_port < 0 && !win9x); // Not cached by 3ware or Win9x/ME driver        id_is_cached = (m_port < 0); // Not cached by 3ware driver
         break;          break;
       case 'm':        case 'm':
         rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);          rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
        id_is_cached = (m_port < 0 && !win9x);        id_is_cached = (m_port < 0);
         break;          break;
       case 'a':        case 'a':
         rc = ata_pass_through_ioctl(get_fh(), &regs,          rc = ata_pass_through_ioctl(get_fh(), &regs,
Line 3065  bool win_ata_device::ata_pass_through(const ata_cmd_in Line 2758  bool win_ata_device::ata_pass_through(const ata_cmd_in
         rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);          rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
         out_regs_set = true;          out_regs_set = true;
         break;          break;
       case 'c':  
         rc = ata_via_scsi_pass_through_ioctl(get_fh(), &regs, data, datasize);  
         break;  
       case 'f':        case 'f':
         if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {          if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
             rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);              rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
               if (rc == 0 && m_phydrive >= 0)
                 get_serial_from_wmi(m_phydrive, (ata_identify_device *)data);
             id_is_cached = true;              id_is_cached = true;
         }          }
         else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {          else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
Line 3261  bool csmi_device::select_phy(unsigned phy_no) Line 2953  bool csmi_device::select_phy(unsigned phy_no)
   
 bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)  bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 {  {
  if (!ata_cmd_is_ok(in,  if (!ata_cmd_is_supported(in,
    true, // data_out_support    ata_device::supports_data_out |
    true, // multi_sector_support    ata_device::supports_output_regs |
    true) // ata_48bit_support    ata_device::supports_multi_sector |
     ata_device::supports_48bit,
     "CMSI")
   )    )
     return false;      return false;
   
Line 3479  bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_ Line 3173  bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_
   // Check result    // Check result
   if (csmi_buffer->ReturnCode) {    if (csmi_buffer->ReturnCode) {
     if (scsi_debugmode) {      if (scsi_debugmode) {
      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",      pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
        code, csmi_buffer->ReturnCode);        code, (unsigned)csmi_buffer->ReturnCode);
     }      }
    return set_err(EIO, "CSMI(%u) failed with ReturnCode=%lu", code, csmi_buffer->ReturnCode);    return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode);
   }    }
   
   if (scsi_debugmode > 1)    if (scsi_debugmode > 1)
    pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code, num_out);    pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out);
   
   return true;    return true;
 }  }
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 // ASPI Interface (for SCSI devices on 9x/ME)  
 /////////////////////////////////////////////////////////////////////////////  
   
 #if WIN9X_SUPPORT  
   
 #pragma pack(1)  
   
 #define ASPI_SENSE_SIZE 18  
   
 // ASPI SCSI Request block header  
   
 typedef struct {  
   unsigned char cmd;             // 00: Command code  
   unsigned char status;          // 01: ASPI status  
   unsigned char adapter;         // 02: Host adapter number  
   unsigned char flags;           // 03: Request flags  
   unsigned char reserved[4];     // 04: 0  
 } ASPI_SRB_HEAD;  
   
 // SRB for host adapter inquiry  
   
 typedef struct {  
   ASPI_SRB_HEAD h;               // 00: Header  
   unsigned char adapters;        // 08: Number of adapters  
   unsigned char target_id;       // 09: Target ID ?  
   char manager_id[16];           // 10: SCSI manager ID  
   char adapter_id[16];           // 26: Host adapter ID  
   unsigned char parameters[16];  // 42: Host adapter unique parmameters  
 } ASPI_SRB_INQUIRY;  
   
 // SRB for get device type  
   
 typedef struct {  
   ASPI_SRB_HEAD h;               // 00: Header  
   unsigned char target_id;       // 08: Target ID  
   unsigned char lun;             // 09: LUN  
   unsigned char devtype;         // 10: Device type  
   unsigned char reserved;        // 11: Reserved  
 } ASPI_SRB_DEVTYPE;  
   
 // SRB for SCSI I/O  
   
 typedef struct {  
   ASPI_SRB_HEAD h;               // 00: Header  
   unsigned char target_id;       // 08: Target ID  
   unsigned char lun;             // 09: LUN  
   unsigned char reserved[2];     // 10: Reserved  
   unsigned long data_size;       // 12: Data alloc. lenght  
   void * data_addr;              // 16: Data buffer pointer  
   unsigned char sense_size;      // 20: Sense alloc. length  
   unsigned char cdb_size;        // 21: CDB length  
   unsigned char host_status;     // 22: Host status  
   unsigned char target_status;   // 23: Target status  
   void * event_handle;           // 24: Event handle  
   unsigned char workspace[20];   // 28: ASPI workspace  
   unsigned char cdb[16+ASPI_SENSE_SIZE];  
 } ASPI_SRB_IO;  
   
 // Macro to retrieve start of sense information  
 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)  
   
 // SRB union  
   
 typedef union {  
   ASPI_SRB_HEAD h;       // Common header  
   ASPI_SRB_INQUIRY q;    // Inquiry  
   ASPI_SRB_DEVTYPE t;    // Device type  
   ASPI_SRB_IO i;         // I/O  
 } ASPI_SRB;  
   
 #pragma pack()  
   
 // ASPI commands  
 #define ASPI_CMD_ADAPTER_INQUIRE        0x00  
 #define ASPI_CMD_GET_DEVICE_TYPE        0x01  
 #define ASPI_CMD_EXECUTE_IO             0x02  
 #define ASPI_CMD_ABORT_IO               0x03  
   
 // Request flags  
 #define ASPI_REQFLAG_DIR_TO_HOST        0x08  
 #define ASPI_REQFLAG_DIR_TO_TARGET      0x10  
 #define ASPI_REQFLAG_DIR_NO_XFER        0x18  
 #define ASPI_REQFLAG_EVENT_NOTIFY       0x40  
   
 // ASPI status  
 #define ASPI_STATUS_IN_PROGRESS         0x00  
 #define ASPI_STATUS_NO_ERROR            0x01  
 #define ASPI_STATUS_ABORTED             0x02  
 #define ASPI_STATUS_ABORT_ERR           0x03  
 #define ASPI_STATUS_ERROR               0x04  
 #define ASPI_STATUS_INVALID_COMMAND     0x80  
 #define ASPI_STATUS_INVALID_ADAPTER     0x81  
 #define ASPI_STATUS_INVALID_TARGET      0x82  
 #define ASPI_STATUS_NO_ADAPTERS         0xE8  
   
 // Adapter (host) status  
 #define ASPI_HSTATUS_NO_ERROR           0x00  
 #define ASPI_HSTATUS_SELECTION_TIMEOUT  0x11  
 #define ASPI_HSTATUS_DATA_OVERRUN       0x12  
 #define ASPI_HSTATUS_BUS_FREE           0x13  
 #define ASPI_HSTATUS_BUS_PHASE_ERROR    0x14  
 #define ASPI_HSTATUS_BAD_SGLIST         0x1A  
   
 // Target status  
 #define ASPI_TSTATUS_NO_ERROR           0x00  
 #define ASPI_TSTATUS_CHECK_CONDITION    0x02  
 #define ASPI_TSTATUS_BUSY               0x08  
 #define ASPI_TSTATUS_RESERV_CONFLICT    0x18  
   
   
 static HINSTANCE h_aspi_dll; // DLL handle  
 static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint  
 static unsigned num_aspi_adapters;  
   
 #ifdef __CYGWIN__  
 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()  
 static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()  
 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))  
 #else  
 #define aspi_entry_valid() (!!aspi_entry)  
 #endif  
   
   
 static int aspi_call(ASPI_SRB * srb)  
 {  
   int i;  
   aspi_entry(srb);  
   i = 0;  
   while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {  
     if (++i > 100/*10sek*/) {  
       pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);  
       aspi_entry = 0;  
       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;  
       errno = EIO;  
       return -1;  
     }  
     if (scsi_debugmode > 1)  
       pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);  
     Sleep(100);  
   }  
   return 0;  
 }  
   
   
 // Get ASPI entrypoint from wnaspi32.dll  
   
 static FARPROC aspi_get_address(const char * name, int verbose)  
 {  
   FARPROC addr;  
   assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);  
   
   if (!(addr = GetProcAddress(h_aspi_dll, name))) {  
     if (verbose)  
       pout("Missing %s() in WNASPI32.DLL\n", name);  
     aspi_entry = 0;  
     FreeLibrary(h_aspi_dll);  
     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;  
     errno = ENOSYS;  
     return 0;  
   }  
   return addr;  
 }  
   
   
 static int aspi_open_dll(int verbose)  
 {  
   UINT (*aspi_info)(void);  
   UINT info, rc;  
   
   assert(!aspi_entry_valid());  
   
   // Check structure layout  
   assert(sizeof(ASPI_SRB_HEAD) == 8);  
   assert(sizeof(ASPI_SRB_INQUIRY) == 58);  
   assert(sizeof(ASPI_SRB_DEVTYPE) == 12);  
   assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);  
   assert(offsetof(ASPI_SRB,h.cmd) == 0);  
   assert(offsetof(ASPI_SRB,h.flags) == 3);  
   assert(offsetof(ASPI_SRB_IO,lun) == 9);  
   assert(offsetof(ASPI_SRB_IO,data_addr) == 16);  
   assert(offsetof(ASPI_SRB_IO,workspace) == 28);  
   assert(offsetof(ASPI_SRB_IO,cdb) == 48);  
   
   if (h_aspi_dll == INVALID_HANDLE_VALUE) {  
     // do not retry  
     errno = ENOENT;  
     return -1;  
   }  
   
   // Load ASPI DLL  
   if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {  
     if (verbose)  
       pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());  
     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;  
     errno = ENOENT;  
     return -1;  
   }  
   if (scsi_debugmode > 1) {  
     // Print full path of WNASPI32.DLL  
     char path[MAX_PATH];  
     if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))  
       strcpy(path, "*unknown*");  
     pout("Using ASPI interface \"%s\"\n", path);  
   }  
   
   // Get ASPI entrypoints  
   if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))  
     return -1;  
   if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))  
     return -1;  
   
   // Init ASPI manager and get number of adapters  
   info = (aspi_info)();  
   if (scsi_debugmode > 1)  
     pout("GetASPI32SupportInfo() returns 0x%04x\n", info);  
   rc = (info >> 8) & 0xff;  
   if (rc == ASPI_STATUS_NO_ADAPTERS) {  
     num_aspi_adapters = 0;  
   }  
   else if (rc == ASPI_STATUS_NO_ERROR) {  
     num_aspi_adapters = info & 0xff;  
   }  
   else {  
     if (verbose)  
       pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);  
     aspi_entry = 0;  
     FreeLibrary(h_aspi_dll);  
     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;  
     errno = ENOENT;  
     return -1;  
   }  
   
   if (scsi_debugmode)  
     pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));  
   
 #ifdef __CYGWIN__  
   // save PID to detect fork() in aspi_entry_valid()  
   aspi_dll_pid = GetCurrentProcessId();  
 #endif  
   assert(aspi_entry_valid());  
   return 0;  
 }  
   
   
 static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)  
 {  
   HANDLE event;  
   // Create event  
   if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {  
     pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;  
   }  
   srb->i.event_handle = event;  
   srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;  
   // Start ASPI request  
   aspi_entry(srb);  
   if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {  
     // Wait for event  
     DWORD rc = WaitForSingleObject(event, timeout*1000L);  
     if (rc != WAIT_OBJECT_0) {  
       if (rc == WAIT_TIMEOUT) {  
         pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",  
           srb->h.adapter, srb->i.target_id, timeout);  
       }  
       else {  
         pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",  
           (unsigned long)(ULONG_PTR)event, rc, rc, GetLastError());  
       }  
       // TODO: ASPI_ABORT_IO command  
       aspi_entry = 0;  
       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;  
       return -EIO;  
     }  
   }  
   CloseHandle(event);  
   return 0;  
 }  
   
   
 win_aspi_device::win_aspi_device(smart_interface * intf,  
   const char * dev_name, const char * req_type)  
 : smart_device(intf, dev_name, "scsi", req_type),  
   m_adapter(-1), m_id(0)  
 {  
 }  
   
 bool win_aspi_device::is_open() const  
 {  
   return (m_adapter >= 0);  
 }  
   
 bool win_aspi_device::open()  
 {  
   // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0  
   unsigned adapter = ~0, id = ~0; int n1 = -1;  
   const char * name = skipdev(get_dev_name());  
   if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name)  
         && adapter <= 9 && id < 16))  
     return set_err(EINVAL);  
   
   if (!aspi_entry_valid()) {  
     if (aspi_open_dll(1/*verbose*/))  
       return set_err(ENOENT);  
   }  
   
   // Adapter OK?  
   if (adapter >= num_aspi_adapters) {  
     pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",  
       adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));  
     if (!is_permissive())  
       return set_err(ENOENT);  
   }  
   
   // Device present ?  
   ASPI_SRB srb;  
   memset(&srb, 0, sizeof(srb));  
   srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;  
   srb.h.adapter = adapter; srb.i.target_id = id;  
   if (aspi_call(&srb))  
     return set_err(EIO);  
   if (srb.h.status != ASPI_STATUS_NO_ERROR) {  
     pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);  
     if (!is_permissive())  
       return set_err(srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);  
   }  
   else if (scsi_debugmode)  
     pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);  
   
   m_adapter = (int)adapter; m_id = (unsigned char)id;  
   return true;  
 }  
   
   
 bool win_aspi_device::close()  
 {  
   // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads  
   return true;  
 }  
   
   
 // Scan for ASPI drives  
   
 bool win9x_smart_interface::scsi_scan(smart_device_list & devlist)  
 {  
   if (!aspi_entry_valid()) {  
     if (aspi_open_dll(scsi_debugmode/*default is quiet*/))  
       return true;  
   }  
   
   for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {  
     ASPI_SRB srb;  
   
     if (ad > 9) {  
       if (scsi_debugmode)  
         pout(" ASPI Adapter %u: Ignored\n", ad);  
       continue;  
     }  
   
     // Get adapter name  
     memset(&srb, 0, sizeof(srb));  
     srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;  
     srb.h.adapter = ad;  
     if (aspi_call(&srb))  
       break;  
   
     if (srb.h.status != ASPI_STATUS_NO_ERROR) {  
       if (scsi_debugmode)  
         pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);  
       continue;  
     }  
   
     if (scsi_debugmode) {  
       for (int i = 1; i < 16 && srb.q.adapter_id[i]; i++)  
         if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))  
           srb.q.adapter_id[i] = '?';  
       pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);  
     }  
   
     bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5);  
   
     for (unsigned id = 0; id <= 7; id++) {  
       // Get device type  
       memset(&srb, 0, sizeof(srb));  
       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;  
       srb.h.adapter = ad; srb.i.target_id = id;  
       if (aspi_call(&srb))  
         return 0;  
       if (srb.h.status != ASPI_STATUS_NO_ERROR) {  
         if (scsi_debugmode > 1)  
           pout("  ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);  
         continue;  
       }  
   
       if (!ignore && srb.t.devtype == 0x00/*HDD*/) {  
         if (scsi_debugmode)  
           pout("  ID %u: Device Type=0x%02x\n", id, srb.t.devtype);  
         char name[20];  
         sprintf(name, "/dev/scsi%u%u", ad, id);  
         devlist.push_back( new win_aspi_device(this, name, "scsi") );  
       }  
       else if (scsi_debugmode)  
         pout("  ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);  
     }  
   }  
   return true;  
 }  
   
   
 // Interface to ASPI SCSI devices  
 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)  
 {  
   int report = scsi_debugmode; // TODO  
   
   if (m_adapter < 0) {  
     set_err(EBADF);  
     return false;  
   }  
   
   if (!aspi_entry_valid()) {  
     set_err(EBADF);  
     return false;  
   }  
   
   if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {  
     set_err(EINVAL, "bad CDB length");  
     return false;  
   }  
   
   if (report > 0) {  
     // From os_linux.c  
     int k, j;  
     const unsigned char * ucp = iop->cmnd;  
     const char * np;  
     char buff[256];  
     const int sz = (int)sizeof(buff);  
   
     np = scsi_get_opcode_name(ucp[0]);  
     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");  
     for (k = 0; k < (int)iop->cmnd_len; ++k)  
       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);  
     if ((report > 1) &&  
       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {  
       int trunc = (iop->dxfer_len > 256) ? 1 : 0;  
   
       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "  
               "data, len=%d%s:\n", (int)iop->dxfer_len,  
               (trunc ? " [only first 256 bytes shown]" : ""));  
       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);  
     }  
     else  
       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");  
     pout("%s", buff);  
   }  
   
   ASPI_SRB srb;  
   memset(&srb, 0, sizeof(srb));  
   srb.h.cmd = ASPI_CMD_EXECUTE_IO;  
   srb.h.adapter = m_adapter;  
   srb.i.target_id = m_id;  
   //srb.i.lun = 0;  
   srb.i.sense_size = ASPI_SENSE_SIZE;  
   srb.i.cdb_size = iop->cmnd_len;  
   memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);  
   
   switch (iop->dxfer_dir) {  
     case DXFER_NONE:  
       srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;  
       break;  
     case DXFER_FROM_DEVICE:  
       srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;  
       srb.i.data_size = iop->dxfer_len;  
       srb.i.data_addr = iop->dxferp;  
       break;  
     case DXFER_TO_DEVICE:  
       srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;  
       srb.i.data_size = iop->dxfer_len;  
       srb.i.data_addr = iop->dxferp;  
       break;  
     default:  
       set_err(EINVAL, "bad dxfer_dir");  
       return false;  
   }  
   
   iop->resp_sense_len = 0;  
   iop->scsi_status = 0;  
   iop->resid = 0;  
   
   if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {  
     // Timeout  
     set_err(EIO, "ASPI Timeout"); return false;  
   }  
   
   if (srb.h.status != ASPI_STATUS_NO_ERROR) {  
     if (   srb.h.status        == ASPI_STATUS_ERROR  
         && srb.i.host_status   == ASPI_HSTATUS_NO_ERROR  
         && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {  
       // Sense valid  
       const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);  
       int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);  
       iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;  
       if (len > 0 && iop->sensep) {  
         memcpy(iop->sensep, sense, len);  
         iop->resp_sense_len = len;  
         if (report > 1) {  
           pout("  >>> Sense buffer, len=%d:\n", (int)len);  
           dStrHex(iop->sensep, len , 1);  
         }  
       }  
       if (report) {  
         pout("  sense_key=%x asc=%x ascq=%x\n",  
          sense[2] & 0xf, sense[12], sense[13]);  
       }  
       return true;  
     }  
     else {  
       if (report)  
         pout("  ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);  
       set_err(EIO);  
       return false;  
     }  
   }  
   
   if (report > 0)  
     pout("  OK\n");  
   
   if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {  
      int trunc = (iop->dxfer_len > 256) ? 1 : 0;  
      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,  
         (trunc ? " [only first 256 bytes shown]" : ""));  
         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);  
   }  
   
   return true;  
 }  
   
 #endif // WIN9X_SUPPORT  
   
 /////////////////////////////////////////////////////////////////////////////  
 // SPT Interface (for SCSI devices and ATA devices behind SATLs)  // SPT Interface (for SCSI devices and ATA devices behind SATLs)
 // Only supported in NT and later  // Only supported in NT and later
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
Line 4043  win_scsi_device::win_scsi_device(smart_interface * int Line 3200  win_scsi_device::win_scsi_device(smart_interface * int
 bool win_scsi_device::open()  bool win_scsi_device::open()
 {  {
   const char * name = skipdev(get_dev_name()); int len = strlen(name);    const char * name = skipdev(get_dev_name()); int len = strlen(name);
  // sd[a-z],N => Physical drive 0-26, RAID port N  // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
  char drive[1+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;  char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
  if (   sscanf(name, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1  if (   sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
       && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {        && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
    return open(drive[0] - 'a', -1, -1, sub_addr);    return open(sdxy_to_phydrive(drive), -1, -1, sub_addr);
   }    }
   // pd<m>,N => Physical drive <m>, RAID port N    // pd<m>,N => Physical drive <m>, RAID port N
   int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;    int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
Line 4098  bool win_scsi_device::open(int pd_num, int ld_num, int Line 3255  bool win_scsi_device::open(int pd_num, int ld_num, int
            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,             FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
            OPEN_EXISTING, 0, 0);             OPEN_EXISTING, 0, 0);
   if (h == INVALID_HANDLE_VALUE) {    if (h == INVALID_HANDLE_VALUE) {
    set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());    set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError());
     return false;      return false;
   }    }
   set_fh(h);    set_fh(h);
Line 4411  static long scsi_pass_through_direct(HANDLE fd, UCHAR  Line 3568  static long scsi_pass_through_direct(HANDLE fd, UCHAR 
   return 0;    return 0;
 }  }
   
   // Areca RAID Controller(SAS Device)
   win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
   : smart_device(intf, dev_name, "areca", "areca")
   {
       set_fh(INVALID_HANDLE_VALUE);
       set_disknum(disknum);
       set_encnum(encnum);
       set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
   }
   
#if 0 // For debugging areca codebool win_areca_scsi_device::open()
 
static void dumpdata(unsigned char *block, int len) 
 {  {
  int ln = (len / 16) + 1;   // total line#  HANDLE hFh;
  unsigned char c; 
  int pos = 0; 
   
  printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);  if( is_open() )
  printf("      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      ASCII      \n"); 
  printf("=====================================================================\n"); 
 
  for ( int l = 0; l < ln && len; l++ ) 
   {    {
    // printf the line# and the HEX data    return true;
    // if a line data length < 16 then append the space to the tail of line to reach 16 chars  }
    printf("%02X | ", l);  hFh = CreateFile( get_dev_name(),
    for ( pos = 0; pos < 16 && len; pos++, len-- )                    GENERIC_READ|GENERIC_WRITE,
    {                    FILE_SHARE_READ|FILE_SHARE_WRITE,
      c = block[l*16+pos];                    NULL,
      printf("%02X ", c);                    OPEN_EXISTING,
    }                    0,
                     NULL );
   if(hFh == INVALID_HANDLE_VALUE)
   {
     return false;
   }
   
    if ( pos < 16 )  set_fh(hFh);
    {  return true;
      for ( int loop = pos; loop < 16; loop++ )}
      { 
        printf("   "); 
      } 
    } 
   
    // print ASCII charsmart_device * win_areca_scsi_device::autodetect_open()
    for ( int loop = 0; loop < pos; loop++ ){
    {  return this;
      c = block[l*16+loop]; 
      if ( c >= 0x20 && c <= 0x7F ) 
      { 
        printf("%c", c); 
      } 
      else 
      { 
        printf("."); 
      } 
    } 
    printf("\n"); 
  } 
  printf("=====================================================================\n"); 
 }  }
   
#endifint win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
 
// PURPOSE 
//   This is an interface routine meant to isolate the OS dependent 
//   parts of the code, and to provide a debugging interface.  Each 
//   different port and OS needs to provide it's own interface.  This 
//   is the Windows interface to the Areca "arcmsr" driver.  It allows ATA 
//   commands to be passed through the SCSI driver. 
// DETAILED DESCRIPTION OF ARGUMENTS 
//   fd: is the file descriptor provided by open() 
//   disknum is the disk number (0 to 127) in the RAID array 
//   command: defines the different operations. 
//   select: additional input data if needed (which log, which type of 
//           self-test). 
//   data:   location to write output data, if needed (512 bytes). 
//   Note: not all commands use all arguments. 
// RETURN VALUES 
//  -1 if the command failed 
//   0 if the command succeeded, 
//   STATUS_CHECK routine: 
//  -1 if the command failed 
//   0 if the command succeeded and disk SMART status is "OK" 
//   1 if the command succeeded and disk SMART status is "FAILING" 
int win_areca_device::arcmsr_command_handler(HANDLE fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len) 
 {  {
  int ioctlreturn = 0;   int ioctlreturn = 0;
  sSRB_BUFFER sBuf; 
  struct scsi_cmnd_io io_hdr; 
  int dir = DXFER_TO_DEVICE; 
   
  UINT8 cdb[10];   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
  UINT8 sense[32];   if ( ioctlreturn || iop->scsi_status )
    {
      ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
      if ( ioctlreturn || iop->scsi_status )
      {
        // errors found
        return -1;
      }
    }
   
  unsigned char *areca_return_packet;   return ioctlreturn;
  int total = 0;}
  int expected = -1; 
  unsigned char return_buff[2048]; 
  unsigned char *ptr = &return_buff[0]; 
  memset(return_buff, 0, sizeof(return_buff)); 
   
  memset((unsigned char *)&sBuf, 0, sizeof(sBuf));bool win_areca_scsi_device::arcmsr_lock()
  memset(&io_hdr, 0, sizeof(io_hdr));{
  memset(cdb, 0, sizeof(cdb));#define    SYNCOBJNAME "Global\\SynIoctlMutex"
  memset(sense, 0, sizeof(sense));  int ctlrnum = -1;
   char mutexstr[64];
   
     if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
       return set_err(EINVAL, "unable to parse device name");
   
  sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL);  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
  memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
  sBuf.srbioctl.Timeout = 10000;  if ( m_mutex == NULL )
  sBuf.srbioctl.ControlCode = arcmsr_cmd; 
 
  switch ( arcmsr_cmd ) 
   {    {
  // command for writing data to driver    return set_err(EIO, "CreateMutex failed");
  case ARCMSR_IOCTL_WRITE_WQBUFFER: 
    if ( data && data_len ) 
    { 
      sBuf.srbioctl.Length = data_len; 
      memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); 
    } 
    // commands for clearing related buffer of driver 
  case ARCMSR_IOCTL_CLEAR_RQBUFFER: 
  case ARCMSR_IOCTL_CLEAR_WQBUFFER: 
    cdb[0] = 0x3B; //SCSI_WRITE_BUF command; 
    break; 
  // command for reading data from driver 
  case ARCMSR_IOCTL_READ_RQBUFFER: 
  // command for identifying driver 
  case ARCMSR_IOCTL_RETURN_CODE_3F: 
    cdb[0] = 0x3C; //SCSI_READ_BUF command; 
    dir = DXFER_FROM_DEVICE; 
    break; 
  default: 
    // unknown arcmsr commands 
    return -1; 
   }    }
   
  cdb[1] = 0x01;  // atomic access to driver
  cdb[2] = 0xf0;  WaitForSingleObject(m_mutex, INFINITE);
   
  io_hdr.dxfer_dir = dir;  return true;
  io_hdr.dxfer_len = sizeof(sBuf);}
  io_hdr.dxferp = (unsigned char *)&sBuf; 
  io_hdr.cmnd = cdb; 
  io_hdr.cmnd_len = sizeof(cdb); 
  io_hdr.sensep = sense; 
  io_hdr.max_sense_len = sizeof(sense); 
  io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; 
   
   while ( 1 )  
   {  
     ioctlreturn = scsi_pass_through_direct(fd, 16, &io_hdr);  
     if ( ioctlreturn || io_hdr.scsi_status )  
     {  
       ioctlreturn = scsi_pass_through_direct(fd, 127, &io_hdr);  
       if ( ioctlreturn || io_hdr.scsi_status )  
       {  
         // errors found  
         break;  
       }  
     }  
   
    if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER )bool win_areca_scsi_device::arcmsr_unlock()
    {{
      // if succeeded, just returns the length of outgoing data  if( m_mutex != NULL)
      return data_len; 
    } 
 
    if ( sBuf.srbioctl.Length ) 
    { 
      //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); 
      memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); 
      ptr += sBuf.srbioctl.Length; 
      total += sBuf.srbioctl.Length; 
      // the returned bytes enough to compute payload length ? 
      if ( expected < 0 && total >= 5 ) 
      { 
        areca_return_packet = (unsigned char *)&return_buff[0]; 
        if ( areca_return_packet[0] == 0x5E && 
           areca_return_packet[1] == 0x01 && 
           areca_return_packet[2] == 0x61 ) 
        { 
          // valid header, let's compute the returned payload length, 
          // we expected the total length is 
          // payload + 3 bytes header + 2 bytes length + 1 byte checksum 
          expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; 
        } 
      } 
 
      if ( total >= 7 && total >= expected ) 
      { 
        //printf("total bytes received = %d, expected length = %d\n", total, expected); 
 
        // ------ Okay! we received enough -------- 
        break; 
      } 
    } 
  } 
 
  // Deal with the different error cases 
  if ( arcmsr_cmd == ARCMSR_IOCTL_RETURN_CODE_3F ) 
   {    {
    // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...)      ReleaseMutex(m_mutex);
    return -4;      CloseHandle(m_mutex);
   }    }
   
  if ( ioctlreturn )  return true;
  { 
    pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn); 
    return -2; 
  } 
 
  if ( io_hdr.scsi_status ) 
  { 
    pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status); 
    return -3; 
  } 
 
  if ( data ) 
  { 
    memcpy(data, return_buff, total); 
  } 
 
  return total; 
 }  }
   
   
win_areca_device::win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum)// Areca RAID Controller(SATA Disk)
: smart_device(intf, dev_name, "areca", "areca"),win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
  m_disknum(disknum),: smart_device(intf, dev_name, "areca", "areca")
  m_encnum(encnum) 
 {  {
  set_fh(fh);  set_fh(INVALID_HANDLE_VALUE);
   set_disknum(disknum);
   set_encnum(encnum);
   set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);    set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 }  }
   
bool win_areca_device::open()bool win_areca_ata_device::open()
 {  {
   HANDLE hFh;    HANDLE hFh;
   
Line 4645  bool win_areca_device::open() Line 3678  bool win_areca_device::open()
   {    {
     return true;      return true;
   }    }
   
   hFh = CreateFile( get_dev_name(),    hFh = CreateFile( get_dev_name(),
                     GENERIC_READ|GENERIC_WRITE,                      GENERIC_READ|GENERIC_WRITE,
                     FILE_SHARE_READ|FILE_SHARE_WRITE,                      FILE_SHARE_READ|FILE_SHARE_WRITE,
Line 4662  bool win_areca_device::open() Line 3694  bool win_areca_device::open()
   return true;    return true;
 }  }
   
// Areca RAID Controllersmart_device * win_areca_ata_device::autodetect_open()
bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 
 {  {
  // ATA input registers  int is_ata = 1;
  typedef struct _ATA_INPUT_REGISTERS 
  { 
    unsigned char features; 
    unsigned char sector_count; 
    unsigned char sector_number; 
    unsigned char cylinder_low; 
    unsigned char cylinder_high; 
    unsigned char device_head; 
    unsigned char command; 
    unsigned char reserved[8]; 
    unsigned char data[512]; // [in/out] buffer for outgoing/incoming data 
  } sATA_INPUT_REGISTERS; 
   
  // ATA output registers  // autodetect device type
  // Note: The output registers is re-sorted for areca internal use only  is_ata = arcmsr_get_dev_type();
  typedef struct _ATA_OUTPUT_REGISTERS  if(is_ata < 0)
   {    {
    unsigned char error;    set_err(EIO);
    unsigned char status;    return this;
    unsigned char sector_count; 
    unsigned char sector_number; 
    unsigned char cylinder_low; 
    unsigned char cylinder_high; 
  } sATA_OUTPUT_REGISTERS; 
 
  // Areca packet format for outgoing: 
  // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 
  // B[3~4] : 2 bytes command length + variant data length, little endian 
  // B[5]   : 1 bytes areca defined command code, ATA passthrough command code is 0x1c 
  // B[6~last-1] : variant bytes payload data 
  // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) 
  // 
  // 
  //   header 3 bytes  length 2 bytes   cmd 1 byte    payload data x bytes  cs 1 byte 
  // +--------------------------------------------------------------------------------+ 
  // + 0x5E 0x01 0x61 |   0x00 0x00   |     0x1c   | .................... |   0x00    | 
  // +--------------------------------------------------------------------------------+ 
  // 
 
  //Areca packet format for incoming: 
  // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 
  // B[3~4] : 2 bytes payload length, little endian 
  // B[5~last-1] : variant bytes returned payload data 
  // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) 
  // 
  // 
  //   header 3 bytes  length 2 bytes   payload data x bytes  cs 1 byte 
  // +-------------------------------------------------------------------+ 
  // + 0x5E 0x01 0x61 |   0x00 0x00   | .................... |   0x00    | 
  // +-------------------------------------------------------------------+ 
  unsigned char    areca_packet[640]; 
  int areca_packet_len = sizeof(areca_packet); 
  unsigned char cs = 0; 
 
  sATA_INPUT_REGISTERS *ata_cmd; 
 
  // For debugging 
#if 0 
  memset(sInq, 0, sizeof(sInq)); 
  scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq)); 
  dumpdata((unsigned char *)sInq, sizeof(sInq)); 
#endif 
  memset(areca_packet, 0, areca_packet_len); 
 
  // ----- BEGIN TO SETUP HEADERS ------- 
  areca_packet[0] = 0x5E; 
  areca_packet[1] = 0x01; 
  areca_packet[2] = 0x61; 
  areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); 
  areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); 
  areca_packet[5] = 0x1c;  // areca defined code for ATA passthrough command 
 
  // ----- BEGIN TO SETUP PAYLOAD DATA ----- 
  memcpy(&areca_packet[7], "SmrT", 4);  // areca defined password 
  ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; 
 
  // Set registers 
  { 
    const ata_in_regs & r = in.in_regs; 
    ata_cmd->features      = r.features; 
    ata_cmd->sector_count  = r.sector_count; 
    ata_cmd->sector_number = r.lba_low; 
    ata_cmd->cylinder_low  = r.lba_mid; 
    ata_cmd->cylinder_high = r.lba_high; 
    ata_cmd->device_head   = r.device; 
    ata_cmd->command       = r.command; 
   }    }
   bool readdata = false;  
   if (in.direction == ata_cmd_in::data_in) {  
       readdata = true;  
       // the command will read data  
       areca_packet[6] = 0x13;  
   }  
   else if ( in.direction == ata_cmd_in::no_data )  
   {  
     // the commands will return no data  
     areca_packet[6] = 0x15;  
   }  
   else if (in.direction == ata_cmd_in::data_out)  
   {  
     // the commands will write data  
     memcpy(ata_cmd->data, in.buffer, in.size);  
     areca_packet[6] = 0x14;  
   }  
   else {  
       // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE  
       return set_err(ENOSYS);  
   }  
   
  areca_packet[11] = m_disknum - 1;  // disk#  if(is_ata == 1)
  areca_packet[19] = m_encnum - 1;   // enc# 
 
  // ----- BEGIN TO SETUP CHECKSUM ----- 
  for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) 
   {    {
    cs += areca_packet[loop];    // SATA device
     return this;
   }    }
   areca_packet[areca_packet_len-1] = cs;  
   
  // ----- BEGIN TO SEND TO ARECA DRIVER ------  // SAS device
  int expected = 0;  smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
  unsigned char return_buff[2048];  close();
  memset(return_buff, 0, sizeof(return_buff));  delete this;
   newdev->open(); // TODO: Can possibly pass open fd
   
  expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0);  return newdev.release();
  if (expected==-3) {}
      return set_err(EIO); 
  } 
   
  expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0);int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
  expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len);{
  if ( expected > 0 )   int ioctlreturn = 0;
  { 
    expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff)); 
  } 
  if ( expected < 0 ) 
  { 
    return set_err(EIO); 
  } 
   
  // ----- VERIFY THE CHECKSUM -----   ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
  cs = 0;   if ( ioctlreturn || iop->scsi_status )
  for ( int loop = 3; loop < expected - 1; loop++ )   {
  {     ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
    cs += return_buff[loop];     if ( ioctlreturn || iop->scsi_status )
  } 
 
  if ( return_buff[expected - 1] != cs ) 
  { 
    return set_err(EIO); 
  } 
 
  sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; 
  if ( ata_out->status ) 
  { 
    if ( in.in_regs.command == ATA_IDENTIFY_DEVICE 
     && !nonempty((unsigned char *)in.buffer, in.size)) 
      {       {
        return set_err(ENODEV, "No drive on port %d", m_disknum);       // errors found
        return -1;
      }       }
  }   }
   
  // returns with data   return ioctlreturn;
  if (readdata) 
  { 
    memcpy(in.buffer, &return_buff[7], in.size); 
  } 
 
  // Return register values 
  { 
    ata_out_regs & r = out.out_regs; 
    r.error          = ata_out->error; 
    r.sector_count   = ata_out->sector_count; 
    r.lba_low        = ata_out->sector_number; 
    r.lba_mid        = ata_out->cylinder_low; 
    r.lba_high       = ata_out->cylinder_high; 
    r.status         = ata_out->status; 
  } 
  return true; 
 }  }
   
bool win_areca_ata_device::arcmsr_lock()
bool win_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 
 {  {
 #define    SYNCOBJNAME "Global\\SynIoctlMutex"  #define    SYNCOBJNAME "Global\\SynIoctlMutex"
   int ctlrnum = -1;    int ctlrnum = -1;
   char mutexstr[64];    char mutexstr[64];
   SECURITY_ATTRIBUTES sa;  
   PSECURITY_DESCRIPTOR pSD;  
   HANDLE hmutex;  
   
   if (!ata_cmd_is_ok(in,  
     true, // data_out_support  
     false, // TODO: multi_sector_support  
     true) // ata_48bit_support  
   )  
     return false;  
   
   // Support 48-bit commands with zero high bytes  
   if (in.in_regs.is_real_48bit_cmd())  
     return set_err(ENOSYS, "48-bit ATA commands not fully supported by Areca");  
   
   if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)    if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
     return set_err(EINVAL, "unable to parse device name");      return set_err(EINVAL, "unable to parse device name");
   
  memset(mutexstr, 0, sizeof(mutexstr));  snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
  sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum);  m_mutex = CreateMutex(NULL, FALSE, mutexstr);
  pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);  if ( m_mutex == NULL )
  if ( !InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) ) 
   {    {
     LocalFree((HLOCAL)pSD);  
     return set_err(EIO, "InitializeSecurityDescriptor failed");  
   }  
   
   if ( !SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE) )  
   {  
     LocalFree((HLOCAL)pSD);  
     return set_err(EIO, "SetSecurityDescriptor failed");  
   }  
   
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);  
   sa.lpSecurityDescriptor = pSD;  
   sa.bInheritHandle = TRUE;  
   hmutex = CreateMutex(&sa, FALSE, mutexstr);  
   if ( hmutex == NULL )  
   {  
     LocalFree((HLOCAL)pSD);  
     return set_err(EIO, "CreateMutex failed");      return set_err(EIO, "CreateMutex failed");
   }    }
   
   // atomic access to driver    // atomic access to driver
  WaitForSingleObject(hmutex, INFINITE);  WaitForSingleObject(m_mutex, INFINITE);
  bool ok = arcmsr_ata_pass_through(in,out); 
  ReleaseMutex(hmutex); 
   
  if(hmutex)  return true;
  {}
    CloseHandle(hmutex); 
  } 
   
  if ( (HLOCAL)pSD )
 bool win_areca_ata_device::arcmsr_unlock()
 {
   if( m_mutex != NULL)
   {    {
    LocalFree((HLOCAL)pSD);      ReleaseMutex(m_mutex);
       CloseHandle(m_mutex);
   }    }
   
  return ok;  return true;
 }  }
   
   
Line 4934  void smart_interface::init() Line 3793  void smart_interface::init()
       SetDllDirectoryA_p("");        SetDllDirectoryA_p("");
   }    }
   
  // Select interface for Windows flavor  static os_win32::win_smart_interface the_win_interface;
  if (GetVersion() & 0x80000000) {  smart_interface::set(&the_win_interface);
#if WIN9X_SUPPORT 
    static os_win32::win9x_smart_interface the_win9x_interface; 
    smart_interface::set(&the_win9x_interface); 
#else 
    throw std::runtime_error("Win9x/ME not supported"); 
#endif 
  } 
  else { 
    static os_win32::winnt_smart_interface the_winnt_interface; 
    smart_interface::set(&the_winnt_interface); 
  } 
 }  }
   
   

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>