]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - os_win32.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / os_win32.cpp
index 1348d7d1a8efa1723787aa2095ca57a998759365..acb20e773dd0d26709998cc04fa466c9f67a8216 100644 (file)
@@ -3,11 +3,7 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
-<<<<<<< HEAD
- * Copyright (C) 2004-16 Christian Franke
-=======
- * Copyright (C) 2004-15 Christian Franke
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
+ * Copyright (C) 2004-18 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
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 #define WINVER 0x0502
 #define _WIN32_WINNT WINVER
 
-#include "int64.h"
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "nvmecmds.h"
 #include "utility.h"
-#include "smartctl.h" // TODO: Do not use smartctl only variables here
 
 #include "dev_interface.h"
 #include "dev_ata_cmd_set.h"
 #include "dev_areca.h"
 
 #include "os_win32/wmiquery.h"
+#include "os_win32/popen.h"
+
+// TODO: Move from smartctl.h to other include file
+extern unsigned char failuretest_permissive;
 
 #include <errno.h>
 
 #define strnicmp strncasecmp
 #endif
 
-<<<<<<< HEAD
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4293 2016-04-14 19:33:05Z chrfranke $";
-=======
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4098 2015-05-30 16:37:37Z chrfranke $";
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
+const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4848 2018-12-05 18:30:46Z chrfranke $";
 
 /////////////////////////////////////////////////////////////////////////////
 // Windows I/O-controls, some declarations are missing in the include files
@@ -284,6 +271,45 @@ ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3);
 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3);
 
 
+// IOCTL_STORAGE_QUERY_PROPERTY: Windows 10 enhancements
+
+namespace win10 {
+
+  // enum STORAGE_PROPERTY_ID: new values
+  const STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty = (STORAGE_PROPERTY_ID)49;
+  const STORAGE_PROPERTY_ID StorageDeviceProtocolSpecificProperty = (STORAGE_PROPERTY_ID)50;
+
+  typedef enum _STORAGE_PROTOCOL_TYPE {
+    ProtocolTypeUnknown = 0,
+    ProtocolTypeScsi,
+    ProtocolTypeAta,
+    ProtocolTypeNvme,
+    ProtocolTypeSd
+  } STORAGE_PROTOCOL_TYPE;
+
+  typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE {
+    NVMeDataTypeUnknown = 0,
+    NVMeDataTypeIdentify,
+    NVMeDataTypeLogPage,
+    NVMeDataTypeFeature
+  } STORAGE_PROTOCOL_NVME_DATA_TYPE;
+
+  typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
+    STORAGE_PROTOCOL_TYPE ProtocolType;
+    ULONG DataType;
+    ULONG ProtocolDataRequestValue;
+    ULONG ProtocolDataRequestSubValue;
+    ULONG ProtocolDataOffset;
+    ULONG ProtocolDataLength;
+    ULONG FixedProtocolReturnData;
+    ULONG Reserved[3];
+  } STORAGE_PROTOCOL_SPECIFIC_DATA;
+
+  ASSERT_SIZEOF(STORAGE_PROTOCOL_SPECIFIC_DATA, 40);
+
+} // namespace win10
+
+
 // IOCTL_STORAGE_PREDICT_FAILURE
 
 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100);
@@ -522,7 +548,7 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version
   assert(num_out == sizeof(GETVERSIONINPARAMS));
 
   if (ata_debugmode > 1) {
-    pout("  SMART_GET_VERSION suceeded, bytes returned: %u\n"
+    pout("  SMART_GET_VERSION succeeded, bytes returned: %u\n"
          "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
       (unsigned)num_out, vers.bVersion, vers.bRevision,
       (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
@@ -616,7 +642,7 @@ static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned dat
   }
 
   if (ata_debugmode > 1) {
-    pout("  %s suceeded, bytes returned: %u (buffer %u)\n", name,
+    pout("  %s succeeded, bytes returned: %u (buffer %u)\n", name,
       (unsigned)num_out, (unsigned)outpar->cBufferSize);
     print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
       (const IDEREGS *)(outpar->bBuffer) : NULL));
@@ -637,34 +663,6 @@ static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned dat
   return 0;
 }
 
-/////////////////////////////////////////////////////////////////////////////
-//// 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;
-};
-
-
 
 /////////////////////////////////////////////////////////////////////////////
 // IDE PASS THROUGH (2000, XP, undocumented)
@@ -733,7 +731,7 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
+    pout("  IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %u (buffer %u)\n",
       (unsigned)num_out, (unsigned)buf->DataBufferSize);
     print_ide_regs_io(regs, &buf->IdeReg);
   }
@@ -774,7 +772,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
   //ab.apt.PathId = 0;
   //ab.apt.TargetId = 0;
   //ab.apt.Lun = 0;
-  ab.apt.TimeOutValue = 10;
+  ab.apt.TimeOutValue = 60; // seconds
   unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
   ab.apt.DataBufferOffset = size;
 
@@ -835,7 +833,6 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
     return -1;
   }
 
-<<<<<<< HEAD
   // Check and copy data
   if (datasize > 0) {
     if (   num_out != size
@@ -843,20 +840,6 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
       if (ata_debugmode) {
         pout("  IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
         print_ide_regs_io(regs, ctfregs);
-=======
-    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;
-        case 0x64: w = (ws ? "win10" : "w10srv"); break;
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
       }
       errno = EIO;
       return -1;
@@ -865,7 +848,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out);
+    pout("  IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, ctfregs);
   }
   *regs = *ctfregs;
@@ -932,7 +915,6 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha
   ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
   memset(&sb, 0, sizeof(sb));
 
-<<<<<<< HEAD
   unsigned size;
   if (datasize > 0) {
     if (datasize > (int)sizeof(sb.space)+1) {
@@ -961,10 +943,6 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha
   sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
   sb.params.in.irDriveRegs = *regs;
   sb.params.in.cBufferSize = size;
-=======
-// Return value for device detection functions
-enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB };
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
 
   // Call miniport ioctl
   size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
@@ -1001,7 +979,7 @@ enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB };
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name,
+    pout("  IOCTL_SCSI_MINIPORT_%s succeeded, bytes returned: %u (buffer %u)\n", name,
       (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
     print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
                              (const IDEREGS *)(sb.params.out.bBuffer) : 0));
@@ -1063,102 +1041,19 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d
     return -1;
   }
 
-<<<<<<< HEAD
   // Copy data
   if (datasize > 0)
     memcpy(data, sb.buffer, datasize);
 
   if (ata_debugmode > 1) {
-    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out);
+    pout("  ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, &sb.regs);
-=======
-    name = skipdev(name);
-#define ARECA_MAX_CTLR_NUM  16
-    n1 = -1;
-    int ctlrindex = 0;
-    if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) {
-      /*
-       1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
-       2. map arcmsrX into "\\\\.\\scsiX"
-      */
-     for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
-        memset(devpath, 0, sizeof(devpath));
-        snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx);
-        win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum);
-        if(arcdev->arcmsr_probe()) {
-          if(ctlrindex-- == 0) {
-            return arcdev;
-          }
-        }
-        delete arcdev;
-      }
-      set_err(ENOENT, "No Areca controller found");
-    }
-    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;
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
   }
   *regs = sb.regs;
 
   return 0;
 }
 
-<<<<<<< HEAD
-=======
-std::string win_smart_interface::get_valid_custom_dev_types_str()
-{
-  return "aacraid,H,L,ID, areca,N[/E]";
-}
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -1192,7 +1087,7 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice)
     return -1;
   }
   if (ata_debugmode > 1)
-    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
+    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n");
   return 0;
 }
 
@@ -1200,7 +1095,6 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 /////////////////////////////////////////////////////////////////////////////
 // IOCTL_STORAGE_QUERY_PROPERTY
 
-<<<<<<< HEAD
 union STORAGE_DEVICE_DESCRIPTOR_DATA {
   STORAGE_DEVICE_DESCRIPTOR desc;
   char raw[256];
@@ -1221,30 +1115,6 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO
       pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
     errno = ENOSYS;
     return -1;
-=======
-  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))) {
-      set_err(EINVAL, "Unable to read USB device ID");
-      return 0;
-    }
-    // Get type name for this ID
-    const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
-    if (!usbtype)
-      return 0;
-    // Return SAT/USB device for this type
-    return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
   }
 
   if (ata_debugmode > 1 || scsi_debugmode > 1) {
@@ -1268,7 +1138,7 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO
 // IOCTL_STORAGE_PREDICT_FAILURE
 
 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
-// or -1 on error, opionally return VendorSpecific data.
+// or -1 on error, optionally return VendorSpecific data.
 // (This works without admin rights)
 
 static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
@@ -1285,7 +1155,6 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
     return -1;
   }
 
-<<<<<<< HEAD
   if (ata_debugmode > 1) {
     pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
          "    PredictFailure: 0x%08x\n"
@@ -1299,49 +1168,6 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
     memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
   return (!pred.PredictFailure ? 0 : 1);
 }
-=======
-  // Set valid types
-  bool ata, scsi, sat, usb, csmi;
-  if (!type) {
-    ata = scsi = usb = sat = csmi = true;
-  }
-  else {
-    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], sat[,pd], usb[,pd], csmi, pd",
-              type);
-      return false;
-    }
-  }
-
-  char name[20];
-
-  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};
-
-    for (int i = 0; i < 128; i++) {
-      if (pd)
-        snprintf(name, sizeof(name), "/dev/pd%d", i);
-      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');
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
 
 
 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
@@ -1357,42 +1183,11 @@ static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device
   // others return it as ProductId only.
   char model[sizeof(id->model) + 1] = "";
 
-<<<<<<< HEAD
   unsigned i = 0;
   if (data.desc.VendorIdOffset) {
     for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++)
       model[i] = data.raw[data.desc.VendorIdOffset+i];
   }
-=======
-        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)
-            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;
-            // Get type name for this ID
-            const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
-            if (!usbtype)
-              continue;
-            // 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;
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
 
   if (data.desc.ProductIdOffset) {
     while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
@@ -1507,7 +1302,7 @@ static bool get_usb_id(int phydrive, int logdrive,
   std::string prev_usb_ant;
   std::string prev_ant, ant, dep;
 
-  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
+  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"");
 
   while (we.next(wo)) {
     prev_ant = ant;
@@ -1519,7 +1314,7 @@ static bool get_usb_id(int phydrive, int logdrive,
       pout(" %s:\n", ant.c_str());
 
     // Extract DeviceID
-    regmatch_t match[2];
+    regular_expression::match_range match[2];
     if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
       if (debug)
         pout("  | (\"%s\")\n", dep.c_str());
@@ -1800,7 +1595,7 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int
   // 3ware RAID if vendor id present
   m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 
-  unsigned long portmap = 0;
+  unsigned portmap = 0;
   if (port >= 0 && devmap >= 0) {
     // 3ware RAID: check vendor id
     if (!m_is_3ware) {
@@ -1823,14 +1618,13 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int
 
   {
     // 3ware RAID: update devicemap first
-
     if (!update_3ware_devicemap_ioctl(h)) {
       if (   smart_get_version(h, &vers_ex) >= 0
           && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
         portmap = vers_ex.dwDeviceMapEx;
     }
     // Check port existence
-    if (!(portmap & (1L << port))) {
+    if (!(portmap & (1U << port))) {
       if (!is_permissive()) {
         close();
         return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
@@ -2197,6 +1991,8 @@ class csmi_device
 : virtual public /*extends*/ smart_device
 {
 public:
+  enum { max_number_of_ports = 32 };
+
   /// Get bitmask of used ports
   unsigned get_ports_used();
 
@@ -2205,8 +2001,10 @@ protected:
     : 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);
+  typedef signed char port_2_index_map[max_number_of_ports];
+
+  /// Get phy info and port mapping, return #ports or -1 on error
+  int get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p2i);
 
   /// Select physical drive
   bool select_port(int port);
@@ -2226,13 +2024,18 @@ private:
 
 /////////////////////////////////////////////////////////////////////////////
 
-bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
+int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p2i)
 {
+  // max_number_of_ports must match CSMI_SAS_PHY_INFO.Phy[] array size
+  typedef char ASSERT_phy_info_size[
+    (int)(sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0])) == max_number_of_ports ? 1 : -1]
+    ATTR_UNUSED;
+
   // Get driver info to check CSMI support
   CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf;
   memset(&driver_info_buf, 0, sizeof(driver_info_buf));
   if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf)))
-    return false;
+    return -1;
 
   if (scsi_debugmode > 1) {
     const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
@@ -2246,26 +2049,82 @@ bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
   CSMI_SAS_PHY_INFO_BUFFER phy_info_buf;
   memset(&phy_info_buf, 0, sizeof(phy_info_buf));
   if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf)))
-    return false;
+    return -1;
 
   phy_info = phy_info_buf.Information;
 
-  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 (phy_info.bNumberOfPhys > max_number_of_ports) {
+    set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
+    return -1;
+  }
+
+  // Create port -> index map
+  //                                     IRST Release
+  // Phy[i].Value               9.x   10.x   14.8   15.2   16.0
+  // ----------------------------------------------------------
+  // bPortIdentifier           0xff   0xff   port   0x00   port
+  // Identify.bPhyIdentifier   index? index? index  index  port
+  // Attached.bPhyIdentifier   0x00   0x00   0x00   index  0x00
+  //
+  // Empty ports with hotplug support may appear in Phy[].
+
+  int number_of_ports;
+  for (int mode = 0; ; mode++) {
+     for (int i = 0; i < max_number_of_ports; i++)
+       p2i[i] = -1;
+
+     number_of_ports = 0;
+     bool found = false;
+     for (int i = 0; i < max_number_of_ports; i++) {
+        const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
+        if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
+          continue;
+
+        // Try to detect which field contains the actual port number.
+        // Use a bPhyIdentifier or the bPortIdentifier if unique
+        // and not always identical to table index, otherwise use index.
+        int port;
+        switch (mode) {
+          case 0:  port = pe.Attached.bPhyIdentifier; break;
+          case 1:  port = pe.Identify.bPhyIdentifier; break;
+          case 2:  port = pe.bPortIdentifier; break;
+          default: port = i; break;
+        }
+        if (!(port < max_number_of_ports && p2i[port] == -1)) {
+          found = false;
+          break;
+        }
+
+        p2i[port] = i;
+        if (number_of_ports <= port)
+          number_of_ports = port + 1;
+        if (port != i)
+          found = true;
+     }
+
+     if (found || mode > 2)
+       break;
+  }
 
   if (scsi_debugmode > 1) {
     pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
-    for (int i = 0; i < max_number_of_phys; i++) {
+    for (int i = 0; i < max_number_of_ports; 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);
+      int port = -1;
+      for (int p = 0; p < max_number_of_ports && port < 0; p++) {
+        if (p2i[p] == i)
+          port = p;
+      }
+
+      pout("Phy[%d] Port:   %d\n", i, port);
       pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
       pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
       pout("  TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
+      pout("  PortIdent:   0x%02x\n", pe.bPortIdentifier);
       pout("  PhyIdent:    0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier);
       const unsigned char * b = id.bSASAddress;
       pout("  SASAddress:  %02x %02x %02x %02x %02x %02x %02x %02x, ",
@@ -2276,20 +2135,23 @@ bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
     }
   }
 
-  return true;
+  return number_of_ports;
 }
 
 unsigned csmi_device::get_ports_used()
 {
   CSMI_SAS_PHY_INFO phy_info;
-  if (!get_phy_info(phy_info))
+  port_2_index_map p2i;
+  int number_of_ports = get_phy_info(phy_info, p2i);
+  if (number_of_ports < 0)
     return 0;
 
   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)
+  for (int p = 0; p < max_number_of_ports; p++) {
+    int i = p2i[p];
+    if (i < 0)
       continue;
+    const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
     if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
       continue;
     switch (pe.Attached.bTargetPortProtocol) {
@@ -2300,55 +2162,30 @@ unsigned csmi_device::get_ports_used()
         continue;
     }
 
-    if (pe.bPortIdentifier == 0xff)
-      // Older (<= 9.*) Intel RST driver
-      ports_used |= (1 << i);
-    else
-      ports_used |= (1 << pe.bPortIdentifier);
+    ports_used |= (1U << p);
   }
 
   return ports_used;
 }
 
-
 bool csmi_device::select_port(int port)
 {
+  if (!(0 <= port && port < max_number_of_ports))
+    return set_err(EINVAL, "Invalid port number %d", port);
+
   CSMI_SAS_PHY_INFO phy_info;
-  if (!get_phy_info(phy_info))
+  port_2_index_map p2i;
+  int number_of_ports = get_phy_info(phy_info, p2i);
+  if (number_of_ports < 0)
     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 (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;
-  }
-
+  int port_index = p2i[port];
   if (port_index < 0) {
-    if (port <= max_port)
+    if (port < number_of_ports)
       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);
+        number_of_ports);
   }
 
   const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[port_index];
@@ -2394,7 +2231,7 @@ bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
     ata_device::supports_output_regs |
     ata_device::supports_multi_sector |
     ata_device::supports_48bit,
-    "CMSI")
+    "CSMI")
   )
     return false;
 
@@ -2481,29 +2318,6 @@ bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
     // TODO: Check ptru_buf->Status.uDataBytes
     memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
 
-<<<<<<< HEAD
-=======
-// 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)
-{
-  if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset))
-    return false;
-  const char * vendor = data->raw + data->desc.VendorIdOffset;
-  if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5)))
-    return false;
-  if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5))
-    return false;
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
   return true;
 }
 
@@ -2518,51 +2332,15 @@ public:
   win_csmi_device(smart_interface * intf, const char * dev_name,
     const char * req_type);
 
-<<<<<<< HEAD
   virtual ~win_csmi_device() throw();
 
   virtual bool open();
 
   virtual bool close();
-=======
-  // Newer BusType* values are missing in older includes
-  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;
+  virtual bool is_open() const;
 
-    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;
->>>>>>> 3d8ad6fa4529eb02ae1391a1e937bf57aad3fb74
-
-  virtual bool is_open() const;
-
-  bool open_scsi();
+  bool open_scsi();
 
 protected:
   virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
@@ -2778,63 +2556,6 @@ static int get_clipboard(char * data, int datasize)
 }
 
 
-// Run a command, write stdout to dataout
-// TODO: Combine with daemon_win32.cpp:daemon_spawn()
-
-static int run_cmd(const char * cmd, char * dataout, int outsize)
-{
-  // Create stdout pipe
-  SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
-  HANDLE pipe_out_w, h;
-  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
-    return -1;
-  HANDLE self = GetCurrentProcess();
-  HANDLE pipe_out_r;
-  if (!DuplicateHandle(self, h, self, &pipe_out_r,
-    GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
-    CloseHandle(pipe_out_w);
-    return -1;
-  }
-  HANDLE pipe_err_w;
-  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
-  }
-
-  // Create process
-  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-  si.hStdInput  = INVALID_HANDLE_VALUE;
-  si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
-  si.dwFlags = STARTF_USESTDHANDLES;
-  PROCESS_INFORMATION pi;
-  if (!CreateProcess(
-    NULL, const_cast<char *>(cmd),
-    NULL, NULL, TRUE/*inherit*/,
-    CREATE_NO_WINDOW/*do not create a new console window*/,
-    NULL, NULL, &si, &pi)) {
-    CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
-  }
-  CloseHandle(pi.hThread);
-  CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
-
-  // Copy stdout to output buffer
-  int i = 0;
-  while (i < outsize) {
-    DWORD num_read;
-    if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
-      break;
-    i += num_read;
-  }
-  CloseHandle(pipe_out_r);
-  // Wait for process
-  WaitForSingleObject(pi.hProcess, INFINITE);
-  CloseHandle(pi.hProcess);
-  return i;
-}
-
-
 static const char * findstr(const char * str, const char * sub)
 {
   const char * s = strstr(str, sub);
@@ -2861,7 +2582,11 @@ bool win_tw_cli_device::open()
     snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
     if (ata_debugmode > 1)
       pout("%s: Run: \"%s\"\n", name, cmd);
-    size = run_cmd(cmd, buffer, sizeof(buffer));
+    FILE * f = popen(cmd, "rb");
+    if (f) {
+      size = fread(buffer, 1, sizeof(buffer), f);
+      pclose(f);
+    }
   }
   else {
     return set_err(EINVAL);
@@ -3715,12 +3440,11 @@ bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
       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);
+        dStrHex(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);
+    pout("buff %s\n",buff);
   }
 
   char ioBuffer[1000];
@@ -3821,7 +3545,7 @@ bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
     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);
+    dStrHex((const uint8_t *)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
   }
   return true;
 }
@@ -3891,7 +3615,12 @@ bool win_nvme_device::open_scsi(int n)
   return true;
 }
 
-// Check if NVMe pass-through works
+// Check if NVMe DeviceIoControl(IOCTL_SCSI_MINIPORT) pass-through works.
+// On Win10 and later that returns false with an errorNumber of 1
+// ("Incorrect function"). Win10 has new pass-through:
+// DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND). However for commonly
+// requested NVMe commands like Identify and Get Features Microsoft want
+// "Protocol specific queries" sent.
 bool win_nvme_device::probe()
 {
   smartmontools::nvme_id_ctrl id_ctrl;
@@ -3972,7 +3701,7 @@ bool win_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & o
 
   // Set NVMe command
   pthru->SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL);
-  memcpy(pthru->SrbIoCtrl.Signature, NVME_SIG_STR, sizeof(NVME_SIG_STR));
+  memcpy(pthru->SrbIoCtrl.Signature, NVME_SIG_STR, sizeof(NVME_SIG_STR)-1);
   pthru->SrbIoCtrl.Timeout = 60;
   pthru->SrbIoCtrl.ControlCode = NVME_PASS_THROUGH_SRB_IO_CODE;
   pthru->SrbIoCtrl.ReturnCode = 0;
@@ -4019,6 +3748,169 @@ bool win_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & o
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+// win10_nvme_device
+
+class win10_nvme_device
+: public /*implements*/ nvme_device,
+  public /*extends*/ win_smart_device
+{
+public:
+  win10_nvme_device(smart_interface * intf, const char * dev_name,
+    const char * req_type, unsigned nsid);
+
+  virtual bool open();
+
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+
+private:
+  bool open(int phydrive);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+win10_nvme_device::win10_nvme_device(smart_interface * intf, const char * dev_name,
+  const char * req_type, unsigned nsid)
+: smart_device(intf, dev_name, "nvme", req_type),
+  nvme_device(nsid)
+{
+}
+
+bool win10_nvme_device::open()
+{
+  // TODO: Use common /dev/ parsing functions
+  const char * name = skipdev(get_dev_name()); int len = strlen(name);
+  // sd[a-z]([a-z])? => Physical drive 0-701
+  char drive[2 + 1] = ""; int n = -1;
+  if (sscanf(name, "sd%2[a-z]%n", drive, &n) == 1 && n == len)
+    return open(sdxy_to_phydrive(drive));
+
+  // pdN => Physical drive N
+  int phydrive = -1; n = -1;
+  if (sscanf(name, "pd%d%n", &phydrive, &n) == 1 && phydrive >= 0 && n == len)
+    return open(phydrive);
+
+  return set_err(EINVAL);
+}
+
+bool win10_nvme_device::open(int phydrive)
+{
+  // TODO: Use common open function for all devices using "\\.\PhysicalDriveN"
+  char devpath[64];
+  snprintf(devpath, sizeof(devpath) - 1, "\\\\.\\PhysicalDrive%d", phydrive);
+
+  // No GENERIC_READ/WRITE access required, this works without admin rights
+  HANDLE h = CreateFileA(devpath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
+    (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, (HANDLE)0);
+
+  if (h == INVALID_HANDLE_VALUE) {
+    long err = GetLastError();
+    if (nvme_debugmode > 1)
+      pout("  %s: Open failed, Error=%ld\n", devpath, err);
+    if (err == ERROR_FILE_NOT_FOUND)
+      set_err(ENOENT, "%s: not found", devpath);
+    else if (err == ERROR_ACCESS_DENIED)
+      set_err(EACCES, "%s: access denied", devpath);
+    else
+      set_err(EIO, "%s: Error=%ld", devpath, err);
+    return false;
+  }
+
+  if (nvme_debugmode > 1)
+    pout("  %s: successfully opened\n", devpath);
+
+  set_fh(h);
+
+  // Use broadcast namespace if no NSID specified
+  // TODO: Get NSID of current device
+  if (!get_nsid())
+    set_nsid(0xffffffff);
+  return true;
+}
+
+struct STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
+{
+  struct { // STORAGE_PROPERTY_QUERY without AdditionalsParameters[1]
+    STORAGE_PROPERTY_ID PropertyId;
+    STORAGE_QUERY_TYPE QueryType;
+  } PropertyQuery;
+  win10::STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecific;
+  BYTE DataBuffer[1];
+};
+
+bool win10_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
+{
+  // Create buffer with appropriate size
+  raw_buffer spsq_raw_buf(offsetof(STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER, DataBuffer) + in.size);
+  STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER * spsq =
+    reinterpret_cast<STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER *>(spsq_raw_buf.data());
+
+  // Set NVMe specific STORAGE_PROPERTY_QUERY
+  spsq->PropertyQuery.QueryType = PropertyStandardQuery;
+  spsq->ProtocolSpecific.ProtocolType = win10::ProtocolTypeNvme;
+
+  switch (in.opcode) {
+    case smartmontools::nvme_admin_identify:
+      if (!in.nsid) // Identify controller
+        spsq->PropertyQuery.PropertyId = win10::StorageAdapterProtocolSpecificProperty;
+      else
+        spsq->PropertyQuery.PropertyId = win10::StorageDeviceProtocolSpecificProperty;
+      spsq->ProtocolSpecific.DataType = win10::NVMeDataTypeIdentify;
+      spsq->ProtocolSpecific.ProtocolDataRequestValue = in.cdw10;
+      break;
+    case smartmontools::nvme_admin_get_log_page:
+      spsq->PropertyQuery.PropertyId = win10::StorageDeviceProtocolSpecificProperty;
+      spsq->ProtocolSpecific.DataType = win10::NVMeDataTypeLogPage;
+      spsq->ProtocolSpecific.ProtocolDataRequestValue = in.cdw10 & 0xff; // LID only ?
+      break;
+    // TODO: nvme_admin_get_features
+    default:
+      return set_err(ENOSYS, "NVMe admin command 0x%02x not supported", in.opcode);
+  }
+
+  spsq->ProtocolSpecific.ProtocolDataRequestSubValue = in.nsid; // ?
+  spsq->ProtocolSpecific.ProtocolDataOffset = sizeof(spsq->ProtocolSpecific);
+  spsq->ProtocolSpecific.ProtocolDataLength = in.size;
+
+  if (in.direction() & nvme_cmd_in::data_out)
+    memcpy(spsq->DataBuffer, in.buffer, in.size);
+
+  if (nvme_debugmode > 1)
+    pout("  [STORAGE_QUERY_PROPERTY: Id=%u, Type=%u, Value=0x%08x, SubVal=0x%08x]\n",
+         (unsigned)spsq->PropertyQuery.PropertyId,
+         (unsigned)spsq->ProtocolSpecific.DataType,
+         (unsigned)spsq->ProtocolSpecific.ProtocolDataRequestValue,
+         (unsigned)spsq->ProtocolSpecific.ProtocolDataRequestSubValue);
+
+  // Call IOCTL_STORAGE_QUERY_PROPERTY
+  DWORD num_out = 0;
+  long err = 0;
+  if (!DeviceIoControl(get_fh(), IOCTL_STORAGE_QUERY_PROPERTY,
+        spsq, spsq_raw_buf.size(), spsq, spsq_raw_buf.size(),
+        &num_out, (OVERLAPPED*)0)) {
+    err = GetLastError();
+  }
+
+  if (nvme_debugmode > 1)
+    pout("  [STORAGE_QUERY_PROPERTY: ReturnData=0x%08x, Reserved[3]={0x%x, 0x%x, 0x%x}]\n",
+    (unsigned)spsq->ProtocolSpecific.FixedProtocolReturnData,
+      (unsigned)spsq->ProtocolSpecific.Reserved[0],
+      (unsigned)spsq->ProtocolSpecific.Reserved[1],
+      (unsigned)spsq->ProtocolSpecific.Reserved[2]);
+
+  // NVMe status is checked by IOCTL
+  if (err)
+    return set_err(EIO, "IOCTL_STORAGE_QUERY_PROPERTY(NVMe) failed, Error=%ld", err);
+
+  if (in.direction() & nvme_cmd_in::data_in)
+    memcpy(in.buffer, spsq->DataBuffer, in.size);
+
+  out.result = spsq->ProtocolSpecific.FixedProtocolReturnData; // Completion DW0 ?
+  return true;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 // win_smart_interface
 // Platform specific interface
@@ -4054,7 +3946,7 @@ protected:
   virtual std::string get_valid_custom_dev_types_str();
 
 private:
-  ata_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
+  smart_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
 };
 
 
@@ -4088,10 +3980,7 @@ std::string win_smart_interface::get_os_version_str()
   assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
 
   // 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
+  // actual OS version.  RtlGetVersion() is not affected.
   LONG /*NTSTATUS*/ (WINAPI /*NTAPI*/ * RtlGetVersion_p)(LPOSVERSIONINFOEXW) =
     (LONG (WINAPI *)(LPOSVERSIONINFOEXW))
     GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
@@ -4104,20 +3993,49 @@ std::string win_smart_interface::get_os_version_str()
   }
 
   const char * w = 0;
+  unsigned build = 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" : "2016"  ); break; // 10.0 = Win 10 : Win Server 2016 (TP only?)
+    switch (  (vi.dwMajorVersion << 4 | vi.dwMinorVersion) << 1
+            | (vi.wProductType > VER_NT_WORKSTATION ? 1 : 0)   ) {
+      case 0x50<<1    :
+      case 0x50<<1 | 1: w = "2000";     break;
+      case 0x51<<1    : w = "xp";       break;
+      case 0x52<<1    : w = "xp64";     break;
+      case 0x52<<1 | 1: w = (!GetSystemMetrics(89/*SM_SERVERR2*/)
+                          ? "2003"
+                          : "2003r2");  break;
+      case 0x60<<1    : w = "vista";    break;
+      case 0x60<<1 | 1: w = "2008";     break;
+      case 0x61<<1    : w = "win7";     break;
+      case 0x61<<1 | 1: w = "2008r2";   break;
+      case 0x62<<1    : w = "win8";     break;
+      case 0x62<<1 | 1: w = "2012";     break;
+      case 0x63<<1    : w = "win8.1";   break;
+      case 0x63<<1 | 1: w = "2012r2";   break;
+      case 0xa0<<1    :
+        switch (vi.dwBuildNumber) {
+          case 10240:   w = "w10-1507"; break;
+          case 10586:   w = "w10-1511"; break;
+          case 14393:   w = "w10-1607"; break;
+          case 15063:   w = "w10-1703"; break;
+          case 16299:   w = "w10-1709"; break;
+          case 17134:   w = "w10-1803"; break;
+          case 17763:   w = "w10-1809"; break;
+          default:      w = "w10";
+                        build = vi.dwBuildNumber; break;
+        } break;
+      case 0xa0<<1 | 1:
+        switch (vi.dwBuildNumber) {
+          case 14393:   w = "2016";      break;
+          case 16299:   w = "2016-1709"; break;
+          case 17134:   w = "2016-1803"; break;
+          case 17763:   w = "2019";      break;
+          default:      w = (vi.dwBuildNumber < 17763
+                          ? "2016"
+                          : "2019");
+                        build = vi.dwBuildNumber; break;
+        } break;
     }
   }
 
@@ -4131,10 +4049,12 @@ std::string win_smart_interface::get_os_version_str()
     snprintf(vptr, vlen, "-%s%u.%u%s",
       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
       (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
+  else if (build)
+    snprintf(vptr, vlen, "-%s-b%u%s", w, build, w64);
   else if (vi.wServicePackMinor)
-    snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
+    snprintf(vptr, vlen, "-%s-sp%u.%u%s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
   else if (vi.wServicePackMajor)
-    snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
+    snprintf(vptr, vlen, "-%s-sp%u%s", w, vi.wServicePackMajor, w64);
   else
     snprintf(vptr, vlen, "-%s%s", w, w64);
   return vstr;
@@ -4183,7 +4103,9 @@ scsi_device * win_smart_interface::get_scsi_device(const char * name, const char
 nvme_device * win_smart_interface::get_nvme_device(const char * name, const char * type,
   unsigned nsid)
 {
-  return new win_nvme_device(this, name, type, nsid);
+  if (str_starts_with(skipdev(name), "nvme"))
+    return new win_nvme_device(this, name, type, nsid);
+  return new win10_nvme_device(this, name, type, nsid);
 }
 
 
@@ -4289,7 +4211,7 @@ std::string win_smart_interface::get_valid_custom_dev_types_str()
 
 
 // Return value for device detection functions
-enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB };
+enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_SAT, DEV_USB, DEV_NVME };
 
 // Return true if ATA drive behind a SAT layer
 static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
@@ -4359,6 +4281,12 @@ static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONIN
     case BusTypeUsb:
       return DEV_USB;
 
+    case 0x11: // BusTypeNvme
+      return DEV_NVME;
+
+    case 0x12: //BusTypeSCM 
+    case 0x13: //BusTypeUfs
+    case 0x14: //BusTypeMax,
     default:
       return DEV_UNKNOWN;
   }
@@ -4437,7 +4365,7 @@ static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdri
 }
 
 
-ata_device * win_smart_interface::get_usb_device(const char * name,
+smart_device * win_smart_interface::get_usb_device(const char * name,
   int phydrive, int logdrive /* = -1 */)
 {
   // Get USB bridge ID
@@ -4453,7 +4381,7 @@ ata_device * win_smart_interface::get_usb_device(const char * name,
     return 0;
 
   // Return SAT/USB device for this type
-  return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
+  return get_scsi_passthrough_device(usbtype, new win_scsi_device(this, name, ""));
 }
 
 smart_device * win_smart_interface::autodetect_smart_device(const char * name)
@@ -4486,6 +4414,9 @@ smart_device * win_smart_interface::autodetect_smart_device(const char * name)
   if (type == DEV_USB)
     return get_usb_device(name, phydrive, logdrive);
 
+  if (type == DEV_NVME)
+    return new win10_nvme_device(this, name, "", 0 /* use default nsid */);
+
   return 0;
 }
 
@@ -4547,9 +4478,9 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
     }
   }
 
-  char name[20];
+  char name[32];
 
-  if (ata || scsi || sat || usb) {
+  if (ata || scsi || sat || usb || nvme) {
     // Scan up to 128 drives and 2 3ware controllers
     const int max_raid = 2;
     bool raid_seen[max_raid] = {false, false};
@@ -4582,7 +4513,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
             // Add physical drives
             int len = strlen(name);
             for (unsigned int pi = 0; pi < 32; pi++) {
-              if (vers_ex.dwDeviceMapEx & (1L << pi)) {
+              if (vers_ex.dwDeviceMapEx & (1U << pi)) {
                 snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
                 devlist.push_back( new win_ata_device(this, name, "ata") );
               }
@@ -4617,6 +4548,13 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
             dev = new win_scsi_device(this, name, "");
           break;
 
+        case DEV_NVME:
+          // STORAGE_QUERY_PROPERTY returned NVMe
+          if (!nvme)
+            continue;
+          dev = new win10_nvme_device(this, name, "", 0 /* use default nsid */);
+          break;
+
         default:
           // Unknown type
           continue;
@@ -4639,7 +4577,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
         continue;
 
       for (int pi = 0; pi < 32; pi++) {
-        if (!(ports_used & (1 << pi)))
+        if (!(ports_used & (1U << pi)))
           continue;
         snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
         devlist.push_back( new win_csmi_device(this, name, "ata") );
@@ -4732,170 +4670,6 @@ bool win_smart_interface::disable_system_auto_standby(bool disable)
   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;
-}
 
 } // namespace