]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - os_win32.cpp
Change second dh_systemd_enable to _start
[mirror_smartmontools-debian.git] / os_win32.cpp
index 6c5fc5178d795eb36b9d62b43db198f7898ca3b6..dae4f66ea46fe5379f0f730185d8c6bedcf1b560 100644 (file)
@@ -1,10 +1,15 @@
 /*
  * os_win32.cpp
  *
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2004-15 Christian Franke
+ *
+ * Original AACRaid code:
+ *  Copyright (C) 2015    Nidhi Malhotra <nidhi.malhotra@pmcs.com>
+ *
+ * Original Areca code:
+ *  Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #endif
 
 #ifndef _WIN32
-// csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin
+// csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
+// (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
 #define _WIN32
 #endif
 
 // CSMI support
 #include "csmisas.h"
 
+// aacraid support
+#include "aacraid.h"
+
 // Silence -Wunused-local-typedefs warning from g++ >= 4.8
 #if __GNUC__ >= 4
 #define ATTR_UNUSED __attribute__((unused))
 #define SELECT_WIN_32_64(x32, x64) (x64)
 #endif
 
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3804 2013-03-27 20:39:41Z chrfranke $";
+// Cygwin does no longer provide strn?icmp() compatibility macros
+// MSVCRT does not provide strn?casecmp()
+#if defined(__CYGWIN__) && !defined(stricmp)
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4156 2015-10-18 12:20:40Z samm2 $";
 
 /////////////////////////////////////////////////////////////////////////////
 // Windows I/O-controls, some declarations are missing in the include files
@@ -306,6 +322,10 @@ ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204);
 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080);
 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168);
 
+// aacraid struct
+
+ASSERT_SIZEOF(SCSI_REQUEST_BLOCK, SELECT_WIN_32_64(64, 88));
+
 } // extern "C"
 
 /////////////////////////////////////////////////////////////////////////////
@@ -400,19 +420,19 @@ class csmi_device
 : virtual public /*extends*/ smart_device
 {
 public:
-  /// Get phy info
-  bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
-
-  /// Check physical drive existence
-  bool check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no);
+  /// Get bitmask of used ports
+  unsigned get_ports_used();
 
 protected:
   csmi_device()
     : smart_device(never_called)
     { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
 
+  /// Get phy info
+  bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
+
   /// Select physical drive
-  bool select_phy(unsigned phy_no);
+  bool select_port(int port);
 
   /// Get info for selected physical drive
   const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
@@ -465,7 +485,7 @@ protected:
 
 private:
   HANDLE m_fh; ///< Controller device handle
-  unsigned m_phy_no; ///< Physical drive number
+  int m_port; ///< Port number
 };
 
 
@@ -492,6 +512,34 @@ private:
   ata_smart_values m_smart_buf;
 };
 
+/////////////////////////////////////////////////////////////////////////////
+//// PMC aacraid Support
+
+class win_aacraid_device
+:public /*implements*/ scsi_device,
+public /*extends*/ win_smart_device
+{
+public:
+  win_aacraid_device(smart_interface *intf, const char *dev_name,unsigned int ctrnum, unsigned int target, unsigned int lun);
+
+  virtual ~win_aacraid_device() throw();
+
+  virtual bool open();
+
+  virtual bool scsi_pass_through(struct scsi_cmnd_io *iop);
+
+private:
+  //Device Host number
+  int m_ctrnum;
+
+  //Channel(Lun) of the device
+  int m_lun;
+
+  //Id of the device
+  int m_target;
+};
+
+
 
 /////////////////////////////////////////////////////////////////////////////
 /// Areca RAID support
@@ -595,45 +643,38 @@ std::string win_smart_interface::get_os_version_str()
   const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
   assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
 
-  OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
+  // Starting with Windows 8.1, GetVersionEx() does no longer report the
+  // actual OS version, see:
+  // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+
+  // RtlGetVersion() is not affected
+  LONG /*NTSTATUS*/ (WINAPI /*NTAPI*/ * RtlGetVersion_p)(LPOSVERSIONINFOEXW) =
+    (LONG (WINAPI *)(LPOSVERSIONINFOEXW))
+    GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
+
+  OSVERSIONINFOEXW vi; memset(&vi, 0, sizeof(vi));
   vi.dwOSVersionInfoSize = sizeof(vi);
-  if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
-    memset(&vi, 0, sizeof(vi));
-    vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
-    if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+  if (!RtlGetVersion_p || RtlGetVersion_p(&vi)) {
+    if (!GetVersionExW((OSVERSIONINFOW *)&vi))
       return vstr;
   }
 
-  if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
-    return vstr;
-
-  const char * w;
-  switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
-    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
-      w = (vi.szCSDVersion[1] == 'B' ||
-           vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
-    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
-      w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
-    case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
-  //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
-      w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
-                                                   :   "xp-mc"); break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
-      w = (!GetSystemMetrics(89/*SM_SERVERR2*/)    ?   "2003"
-                                                   :   "2003r2"); break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0:
-      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "vista"
-                                                   :   "2008" );  break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 1:
-      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win7"
-                                                   :   "2008r2"); break;
-    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 2:
-      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win8"
-                                                   :   "2012"); break;
-    default: w = 0; break;
+  const char * w = 0;
+  if (   vi.dwPlatformId == VER_PLATFORM_WIN32_NT
+      && 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;
+      case 0x64: w = (ws ? "w10tp" : "w10tps"); break; //  6.4 = Win 10 Technical Preview
+      case 0xa0: w = (ws ? "win10" : "w10srv"); break; // 10.0 = Win 10 Final
+    }
   }
 
   const char * w64 = "";
@@ -644,7 +685,7 @@ std::string win_smart_interface::get_os_version_str()
 
   if (!w)
     snprintf(vptr, vlen, "-%s%u.%u%s",
-      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
+      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
       (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
   else if (vi.wServicePackMinor)
     snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
@@ -681,12 +722,13 @@ int64_t win_smart_interface::get_timer_usec()
 
 
 // Return value for device detection functions
-enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
+enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB };
 
 static win_dev_type get_phy_drive_type(int drive);
 static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex);
 static win_dev_type get_log_drive_type(int drive);
-static bool get_usb_id(int drive, unsigned short & vendor_id,
+static bool get_usb_id(int phydrive, int logdrive,
+                       unsigned short & vendor_id,
                        unsigned short & product_id);
 
 static const char * ata_get_def_options(void);
@@ -743,9 +785,10 @@ static int sdxy_to_phydrive(const char (& xy)[2+1])
   return phydrive;
 }
 
-static win_dev_type get_dev_type(const char * name, int & phydrive)
+static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdrive)
 {
-  phydrive = -1;
+  phydrive = logdrive = -1;
+
   name = skipdev(name);
   if (!strncmp(name, "st", 2))
     return DEV_SCSI;
@@ -754,7 +797,7 @@ static win_dev_type get_dev_type(const char * name, int & phydrive)
   if (!strncmp(name, "tape", 4))
     return DEV_SCSI;
 
-  int logdrive = drive_letter(name);
+  logdrive = drive_letter(name);
   if (logdrive >= 0) {
     win_dev_type type = get_log_drive_type(logdrive);
     return (type != DEV_UNKNOWN ? type : DEV_SCSI);
@@ -766,9 +809,9 @@ static win_dev_type get_dev_type(const char * name, int & phydrive)
     return get_phy_drive_type(phydrive);
   }
 
-  phydrive = -1;
   if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
     return get_phy_drive_type(phydrive);
+
   return DEV_UNKNOWN;
 }
 
@@ -813,6 +856,55 @@ smart_device * win_smart_interface::get_custom_smart_device(const char * name, c
     }
     else
       set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
+    return 0;
+  }
+
+  // aacraid?
+  unsigned ctrnum, lun, target;
+  n1 = -1;
+
+  if (   sscanf(type, "aacraid,%u,%u,%u%n", &ctrnum, &lun, &target, &n1) >= 3
+      && n1 == (int)strlen(type)) {
+#define aacraid_MAX_CTLR_NUM  16
+    if (ctrnum >= aacraid_MAX_CTLR_NUM) {
+      set_err(EINVAL, "aacraid: invalid host number %u", ctrnum);
+      return 0;
+    }
+
+    /*
+    1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and
+    2. map ARCX into "\\\\.\\scsiX"
+    */
+    memset(devpath, 0, sizeof(devpath));
+    unsigned ctlrindex = 0;
+    for (int portNum = 0; portNum < aacraid_MAX_CTLR_NUM; portNum++){
+      char subKey[63];
+      snprintf(subKey, sizeof(subKey), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum);
+      HKEY hScsiKey = 0;
+      long regStatus = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hScsiKey);
+      if (regStatus == ERROR_SUCCESS){
+        char driverName[20];
+        DWORD driverNameSize = sizeof(driverName);
+        DWORD regType = 0;
+        regStatus = RegQueryValueExA(hScsiKey, "Driver", NULL, &regType, (LPBYTE) driverName, &driverNameSize);
+        if (regStatus == ERROR_SUCCESS){
+          if (regType == REG_SZ){
+            if (stricmp(driverName, "arcsas") == 0){
+              if(ctrnum == ctlrindex){
+                snprintf(devpath, sizeof(devpath), "\\\\.\\Scsi%d:", portNum);
+                return get_sat_device("sat,auto",
+                  new win_aacraid_device(this, devpath, ctrnum, target, lun));
+              }
+              ctlrindex++;
+            }
+          }
+        }
+        RegCloseKey(hScsiKey);
+      }
+    }
+
+    set_err(EINVAL, "aacraid: host %u not found", ctrnum);
+    return 0;
   }
 
   return 0;
@@ -820,7 +912,7 @@ smart_device * win_smart_interface::get_custom_smart_device(const char * name, c
 
 std::string win_smart_interface::get_valid_custom_dev_types_str()
 {
-  return "areca,N[/E]";
+  return "aacraid,H,L,ID, areca,N[/E]";
 }
 
 
@@ -836,18 +928,22 @@ smart_device * win_smart_interface::autodetect_smart_device(const char * name)
   if (str_starts_with(testname, "csmi"))
     return new win_csmi_device(this, name, "");
 
-  int phydrive = -1;
-  win_dev_type type = get_dev_type(name, phydrive);
+  int phydrive = -1, logdrive = -1;
+  win_dev_type type = get_dev_type(name, phydrive, logdrive);
 
   if (type == DEV_ATA)
     return new win_ata_device(this, name, "");
+
   if (type == DEV_SCSI)
     return new win_scsi_device(this, name, "");
 
+  if (type == DEV_SAT)
+    return get_sat_device("sat", new win_scsi_device(this, name, ""));
+
   if (type == DEV_USB) {
     // Get USB bridge ID
     unsigned short vendor_id = 0, product_id = 0;
-    if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
+    if (!get_usb_id(phydrive, logdrive, vendor_id, product_id)) {
       set_err(EINVAL, "Unable to read USB device ID");
       return 0;
     }
@@ -890,29 +986,33 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
   }
 
   // Set valid types
-  bool ata, scsi, usb, csmi;
+  bool ata, scsi, sat, usb, csmi;
   if (!type) {
-    ata = scsi = usb = csmi = true;
+    ata = scsi = usb = sat = csmi = true;
   }
   else {
-    ata = scsi = usb = csmi = false;
+    ata = scsi = usb = sat = csmi = false;
     if (!strcmp(type, "ata"))
       ata = true;
     else if (!strcmp(type, "scsi"))
       scsi = true;
+    else if (!strcmp(type, "sat"))
+      sat = true;
     else if (!strcmp(type, "usb"))
       usb = true;
     else if (!strcmp(type, "csmi"))
       csmi = true;
     else {
-      set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], usb[,pd], csmi, pd", type);
+      set_err(EINVAL,
+              "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], sat[,pd], usb[,pd], csmi, pd",
+              type);
       return false;
     }
   }
 
   char name[20];
 
-  if (ata || scsi || usb) {
+  if (ata || scsi || sat || usb) {
     // Scan up to 128 drives and 2 3ware controllers
     const int max_raid = 2;
     bool raid_seen[max_raid] = {false, false};
@@ -943,7 +1043,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
             raid_seen[vers_ex.wControllerId] = true;
             // Add physical drives
             int len = strlen(name);
-            for (int pi = 0; pi < 32; pi++) {
+            for (unsigned 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") );
@@ -962,6 +1062,13 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
           devlist.push_back( new win_scsi_device(this, name, "scsi") );
           break;
 
+        case DEV_SAT:
+          // STORAGE_QUERY_PROPERTY returned VendorId "ATA     "
+          if (!sat)
+            continue;
+          devlist.push_back( get_sat_device("sat", new win_scsi_device(this, name, "")) );
+          break;
+
         case DEV_USB:
           // STORAGE_QUERY_PROPERTY returned USB
           if (!usb)
@@ -970,7 +1077,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
             // 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))
+            if (!get_usb_id(i, -1, vendor_id, product_id))
               continue;
             // Get type name for this ID
             const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
@@ -998,12 +1105,13 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
       win_csmi_device test_dev(this, name, "");
       if (!test_dev.open_scsi())
         continue;
-      CSMI_SAS_PHY_INFO phy_info;
-      if (!test_dev.get_phy_info(phy_info))
+
+      unsigned ports_used = test_dev.get_ports_used();
+      if (!ports_used)
         continue;
 
-      for (int pi = 0; pi < phy_info.bNumberOfPhys; pi++) {
-        if (!test_dev.check_phy(phy_info, pi))
+      for (int pi = 0; pi < 32; pi++) {
+        if (!(ports_used & (1 << pi)))
           continue;
         snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
         devlist.push_back( new win_csmi_device(this, name, "ata") );
@@ -1999,6 +2107,16 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
 
 /////////////////////////////////////////////////////////////////////////////
 
+// Return true if ATA drive behind a SAT layer
+static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+{
+  if (!data->desc.VendorIdOffset)
+    return false;
+  if (strcmp(data->raw + data->desc.VendorIdOffset, "ATA     "))
+    return false;
+  return true;
+}
+
 // Return true if Intel ICHxR RAID volume
 static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 {
@@ -2024,22 +2142,34 @@ static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONIN
   switch ((int)data.desc.BusType) {
     case BusTypeAta:
     case 0x0b: // BusTypeSata
+      // Certain Intel AHCI drivers (C600+/C220+) have broken
+      // IOCTL_ATA_PASS_THROUGH support and a working SAT layer
+      if (is_sat(&data))
+        return DEV_SAT;
+
       if (ata_version_ex)
         memset(ata_version_ex, 0, sizeof(*ata_version_ex));
       return DEV_ATA;
 
     case BusTypeScsi:
     case BusTypeRAID:
+      if (is_sat(&data))
+        return DEV_SAT;
+
       // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
       if (is_intel_raid_volume(&data))
         return DEV_SCSI;
       // LSI/3ware RAID volume: supports SMART_*
       if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
         return DEV_ATA;
+
       return DEV_SCSI;
 
     case 0x09: // BusTypeiScsi
     case 0x0a: // BusTypeSas
+      if (is_sat(&data))
+        return DEV_SAT;
+
       return DEV_SCSI;
 
     case BusTypeUsb:
@@ -2163,8 +2293,10 @@ static bool get_serial_from_wmi(int drive, ata_identify_device * id)
 /////////////////////////////////////////////////////////////////////////////
 // USB ID detection using WMI
 
-// Get USB ID for a physical drive number
-static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
+// Get USB ID for a physical or logical drive number
+static bool get_usb_id(int phydrive, int logdrive,
+                       unsigned short & vendor_id,
+                       unsigned short & product_id)
 {
   bool debug = (scsi_debugmode > 1);
 
@@ -2176,13 +2308,41 @@ static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & p
   }
 
   // Get device name
+  std::string name;
+
   wbem_object wo;
-  if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
+  if (0 <= logdrive && logdrive <= 'Z'-'A') {
+    // Drive letter -> Partition info
+    if (!ws.query1(wo, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition",
+                   'A'+logdrive))
+      return false;
+
+    std::string partid = wo.get_str("DeviceID");
+    if (debug)
+      pout("%c: --> \"%s\" -->\n", 'A'+logdrive, partid.c_str());
+
+    // Partition ID -> Physical drive info
+    if (!ws.query1(wo, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
+                   partid.c_str()))
+      return false;
+
+    name = wo.get_str("Model");
+    if (debug)
+      pout("%s --> \"%s\":\n", wo.get_str("DeviceID").c_str(), name.c_str());
+  }
+
+  else if (phydrive >= 0) {
+    // Physical drive number -> Physical drive info
+    if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive))
+      return false;
+
+    name = wo.get_str("Model");
+    if (debug)
+      pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive, name.c_str());
+  }
+  else
     return false;
 
-  std::string name = wo.get_str("Model");
-  if (debug)
-    pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str());
 
   // Get USB_CONTROLLER -> DEVICE associations
   wbem_enumerator we;
@@ -2225,10 +2385,9 @@ static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & p
       prev_usb_ant = ant;
       if (debug)
         pout("  +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
-      continue;
     }
-    else if (str_starts_with(devid, "USBSTOR\\\\")) {
-      // USBSTOR device found
+    else if (str_starts_with(devid, "USBSTOR\\\\") || str_starts_with(devid, "SCSI\\\\")) {
+      // USBSTORage or SCSI device found
       if (debug)
         pout("  +--> \"%s\"\n", devid.c_str());
 
@@ -2492,7 +2651,6 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int
       close();
       return set_err(ENOSYS);
     }
-    devmap = 0x0f;
   }
   m_smartver_state = 1;
 
@@ -2745,9 +2903,10 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
         break;
       case 'f':
         if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
-            rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
+            ata_identify_device * id = reinterpret_cast<ata_identify_device *>(data);
+            rc = get_identify_from_device_property(get_fh(), id);
             if (rc == 0 && m_phydrive >= 0)
-              get_serial_from_wmi(m_phydrive, (ata_identify_device *)data);
+              get_serial_from_wmi(m_phydrive, id);
             id_is_cached = true;
         }
         else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
@@ -2873,14 +3032,19 @@ bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
     return false;
 
   phy_info = phy_info_buf.Information;
-  if (phy_info.bNumberOfPhys > sizeof(phy_info.Phy)/sizeof(phy_info.Phy[0]))
+
+  const int max_number_of_phys = sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]);
+  if (phy_info.bNumberOfPhys > max_number_of_phys)
     return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
 
   if (scsi_debugmode > 1) {
     pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
-    for (int i = 0; i < phy_info.bNumberOfPhys; i++) {
+    for (int i = 0; i < max_number_of_phys; i++) {
       const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
       const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
+      if (id.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+        continue;
+
       pout("Phy[%d] Port:   0x%02x\n", i, pe.bPortIdentifier);
       pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
       pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
@@ -2898,40 +3062,92 @@ bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
   return true;
 }
 
-bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no)
+unsigned csmi_device::get_ports_used()
 {
-  // Check Phy presence
-  if (phy_no >= phy_info.bNumberOfPhys)
-    return set_err(ENOENT, "Port %u does not exist (#ports: %d)", phy_no,
-      phy_info.bNumberOfPhys);
+  CSMI_SAS_PHY_INFO phy_info;
+  if (!get_phy_info(phy_info))
+    return 0;
 
-  const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[phy_no];
-  if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
-    return set_err(ENOENT, "No device on port %u", phy_no);
+  unsigned ports_used = 0;
+  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
+    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
+    if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
+    switch (pe.Attached.bTargetPortProtocol) {
+      case CSMI_SAS_PROTOCOL_SATA:
+      case CSMI_SAS_PROTOCOL_STP:
+        break;
+      default:
+        continue;
+    }
 
-  switch (phy_ent.Attached.bTargetPortProtocol) {
-    case CSMI_SAS_PROTOCOL_SATA:
-    case CSMI_SAS_PROTOCOL_STP:
-      break;
-    default:
-      return set_err(ENOENT, "No SATA device on port %u (protocol: %u)",
-        phy_no, phy_ent.Attached.bTargetPortProtocol);
+    if (pe.bPortIdentifier == 0xff)
+      // Older (<= 9.*) Intel RST driver
+      ports_used |= (1 << i);
+    else
+      ports_used |= (1 << pe.bPortIdentifier);
   }
 
-  return true;
+  return ports_used;
 }
 
-bool csmi_device::select_phy(unsigned phy_no)
+
+bool csmi_device::select_port(int port)
 {
   CSMI_SAS_PHY_INFO phy_info;
   if (!get_phy_info(phy_info))
     return false;
 
+  // Find port
+  int max_port = -1, port_index = -1;
+  for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) {
+    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+    if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+      continue;
 
-  if (!check_phy(phy_info, phy_no))
-    return false;
+    if (pe.bPortIdentifier == 0xff) {
+      // Older (<= 9.*) Intel RST driver
+      max_port = phy_info.bNumberOfPhys - 1;
+      if (i >= phy_info.bNumberOfPhys)
+        break;
+      if ((int)i != port)
+        continue;
+    }
+    else {
+      if (pe.bPortIdentifier > max_port)
+        max_port = pe.bPortIdentifier;
+      if (pe.bPortIdentifier != port)
+        continue;
+    }
+
+    port_index = i;
+    break;
+  }
 
-  m_phy_ent = phy_info.Phy[phy_no];
+  if (port_index < 0) {
+    if (port <= max_port)
+      return set_err(ENOENT, "Port %d is disabled", port);
+    else
+      return set_err(ENOENT, "Port %d does not exist (#ports: %d)", port,
+        max_port + 1);
+  }
+
+  const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[port_index];
+  if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+    return set_err(ENOENT, "No device on port %d", port);
+
+  switch (phy_ent.Attached.bTargetPortProtocol) {
+    case CSMI_SAS_PROTOCOL_SATA:
+    case CSMI_SAS_PROTOCOL_STP:
+      break;
+    default:
+      return set_err(ENOENT, "No SATA device on port %d (protocol: %d)",
+        port, phy_ent.Attached.bTargetPortProtocol);
+  }
+
+  m_phy_ent = phy_ent;
   return true;
 }
 
@@ -3040,7 +3256,7 @@ bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name,
   const char * req_type)
 : smart_device(intf, dev_name, "ata", req_type),
-  m_fh(INVALID_HANDLE_VALUE), m_phy_no(0)
+  m_fh(INVALID_HANDLE_VALUE), m_port(-1)
 {
 }
 
@@ -3068,10 +3284,10 @@ bool win_csmi_device::close()
 bool win_csmi_device::open_scsi()
 {
   // Parse name
-  unsigned contr_no = ~0, phy_no = ~0; int nc = -1;
+  unsigned contr_no = ~0, port = ~0; int nc = -1;
   const char * name = skipdev(get_dev_name());
-  if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &phy_no, &nc) >= 0
-        && nc == (int)strlen(name) && contr_no <= 9 && phy_no < 32)  )
+  if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &port, &nc) >= 0
+        && nc == (int)strlen(name) && contr_no <= 9 && port < 32)  )
     return set_err(EINVAL);
 
   // Open controller handle
@@ -3097,7 +3313,7 @@ bool win_csmi_device::open_scsi()
     pout(" %s: successfully opened\n", devpath);
 
   m_fh = h;
-  m_phy_no = phy_no;
+  m_port = port;
   return true;
 }
 
@@ -3108,7 +3324,7 @@ bool win_csmi_device::open()
     return false;
 
   // Get Phy info for this drive
-  if (!select_phy(m_phy_no)) {
+  if (!select_port(m_port)) {
     close();
     return false;
   }
@@ -3412,14 +3628,14 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
   } else
     iop->resp_sense_len = 0;
 
-  if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
+  if (iop->dxfer_len > sb.spt.DataTransferLength)
     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
   else
     iop->resid = 0;
 
   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,
+     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
         (trunc ? " [only first 256 bytes shown]" : ""));
         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
   }
@@ -3538,14 +3754,14 @@ static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd
   } else
     iop->resp_sense_len = 0;
 
-  if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
+  if (iop->dxfer_len > sb.spt.DataTransferLength)
     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
   else
     iop->resid = 0;
 
   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,
+     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
         (trunc ? " [only first 256 bytes shown]" : ""));
         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
   }
@@ -3681,10 +3897,8 @@ bool win_areca_ata_device::open()
 
 smart_device * win_areca_ata_device::autodetect_open()
 {
-  int is_ata = 1;
-
   // autodetect device type
-  is_ata = arcmsr_get_dev_type();
+  int is_ata = arcmsr_get_dev_type();
   if(is_ata < 0)
   {
     set_err(EIO);
@@ -3758,6 +3972,170 @@ bool win_areca_ata_device::arcmsr_unlock()
   return true;
 }
 
+// AACRAID
+win_aacraid_device::win_aacraid_device(smart_interface * intf,
+  const char *dev_name, unsigned ctrnum, unsigned target, unsigned lun)
+: smart_device(intf, dev_name, "aacraid", "aacraid"),
+  m_ctrnum(ctrnum), m_lun(lun), m_target(target)
+{
+  set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name, m_ctrnum, m_lun, m_target);
+  set_info().dev_type  = strprintf("aacraid,%d,%d,%d", m_ctrnum, m_lun, m_target);
+}
+
+win_aacraid_device::~win_aacraid_device() throw()
+{
+}
+
+bool win_aacraid_device::open()
+{
+  if (is_open())
+    return true;
+
+  HANDLE hFh = CreateFile( get_dev_name(),
+          GENERIC_READ|GENERIC_WRITE,
+          FILE_SHARE_READ|FILE_SHARE_WRITE,
+          NULL,
+          OPEN_EXISTING,
+          0,
+          0);
+  if (hFh == INVALID_HANDLE_VALUE)
+    return set_err(ENODEV, "Open failed, Error=%u", (unsigned)GetLastError());
+
+  set_fh(hFh);
+  return true;
+}
+
+bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
+{
+  int report = scsi_debugmode;
+  if (report > 0)
+  {
+    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((const char *)iop->dxferp,
+        (trunc ? 256 : (int)iop->dxfer_len) , 1);
+      }
+    else
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+      pout("buff %s\n",buff);
+  }
+
+  char ioBuffer[1000];
+  SRB_IO_CONTROL * pSrbIO = (SRB_IO_CONTROL *) ioBuffer;
+  SCSI_REQUEST_BLOCK * pScsiIO = (SCSI_REQUEST_BLOCK *) (ioBuffer + sizeof(SRB_IO_CONTROL));
+  DWORD scsiRequestBlockSize = sizeof(SCSI_REQUEST_BLOCK);
+  char *pRequestSenseIO = (char *) (ioBuffer + sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize);
+  DWORD dataOffset = (sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize  + 7) & 0xfffffff8;
+  char *pDataIO = (char *) (ioBuffer + dataOffset);
+  memset(pScsiIO, 0, scsiRequestBlockSize);
+  pScsiIO->Length    = (USHORT) scsiRequestBlockSize;
+  pScsiIO->Function  = SRB_FUNCTION_EXECUTE_SCSI;
+  pScsiIO->PathId    = 0;
+  pScsiIO->TargetId  = m_target;
+  pScsiIO->Lun       = m_lun;
+  pScsiIO->CdbLength = (int)iop->cmnd_len;
+  switch(iop->dxfer_dir){
+    case DXFER_NONE:
+      pScsiIO->SrbFlags = SRB_NoDataXfer;
+      break;
+    case DXFER_FROM_DEVICE:
+      pScsiIO->SrbFlags |= SRB_DataIn;
+      break;
+    case DXFER_TO_DEVICE:
+      pScsiIO->SrbFlags |= SRB_DataOut;
+      break;
+    default:
+      pout("aacraid: bad dxfer_dir\n");
+      return set_err(EINVAL, "aacraid: bad dxfer_dir\n");
+  }
+  pScsiIO->DataTransferLength = (ULONG)iop->dxfer_len;
+  pScsiIO->TimeOutValue = iop->timeout;
+  UCHAR *pCdb = (UCHAR *) pScsiIO->Cdb;
+  memcpy(pCdb, iop->cmnd, 16);
+  if (iop->max_sense_len){
+    memset(pRequestSenseIO, 0, iop->max_sense_len);
+  }
+  if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_OUT){
+    memcpy(pDataIO, iop->dxferp, iop->dxfer_len);
+  }
+  else if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_IN){
+    memset(pDataIO, 0, iop->dxfer_len);
+  }
+
+  DWORD bytesReturned = 0;
+  memset(pSrbIO, 0, sizeof(SRB_IO_CONTROL));
+  pSrbIO->HeaderLength = sizeof(SRB_IO_CONTROL);
+  memcpy(pSrbIO->Signature, "AACAPI", 7);
+  pSrbIO->ControlCode = ARCIOCTL_SEND_RAW_SRB;
+  pSrbIO->Length = (dataOffset + iop->dxfer_len - sizeof(SRB_IO_CONTROL) + 7) & 0xfffffff8;
+  pSrbIO->Timeout = 3*60;
+
+  if (!DeviceIoControl(
+                   get_fh(),
+                   IOCTL_SCSI_MINIPORT,
+                   ioBuffer,
+                   sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+                   ioBuffer,
+                   sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+                   &bytesReturned,
+                   NULL)
+     ) {
+    return set_err(EIO, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
+  }
+
+  iop->scsi_status = pScsiIO->ScsiStatus;
+  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+    int slen = sizeof(pRequestSenseIO) + 8;
+    if (slen > (int)sizeof(pRequestSenseIO))
+      slen = sizeof(pRequestSenseIO);
+    if (slen > (int)iop->max_sense_len)
+      slen = (int)iop->max_sense_len;
+    memcpy(iop->sensep, pRequestSenseIO, slen);
+    iop->resp_sense_len = slen;
+    if (report) {
+      if (report > 1) {
+        pout("  >>> Sense buffer, len=%d:\n", slen);
+        dStrHex(iop->sensep, slen , 1);
+      }
+      if ((iop->sensep[0] & 0x7f) > 0x71)
+        pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+          iop->scsi_status, iop->sensep[1] & 0xf,
+          iop->sensep[2], iop->sensep[3]);
+      else
+        pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+          iop->scsi_status, iop->sensep[2] & 0xf,
+          iop->sensep[12], iop->sensep[13]);
+    }
+  }
+  else {
+    iop->resp_sense_len = 0;
+  }
+
+  if (iop->dxfer_dir == DXFER_FROM_DEVICE){
+     memcpy(iop->dxferp,pDataIO, iop->dxfer_len);
+  }
+  if((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)){
+    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex((CHAR*)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
+  }
+  return true;
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////////////