/*
* os_win32.cpp
*
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
*
- * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2004-18 Christian Franke
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
+ * Original AACRaid code:
+ * Copyright (C) 2015 Nidhi Malhotra <nidhi.malhotra@pmcs.com>
*
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ * Original Areca code:
+ * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
*
+ * 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>
#include <windows.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)
#include <devioctl.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <ntddstor.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)
#include <ddk/ntdddisk.h>
#include <ddk/ntddscsi.h>
#include <winioctl.h>
#endif
+#ifndef _WIN32
+// 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"
-#ifdef __CYGWIN__
-#include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
+// aacraid support
+#include "aacraid.h"
+
+// Silence -Wunused-local-typedefs warning from g++ >= 4.8
+#if __GNUC__ >= 4
+#define ATTR_UNUSED __attribute__((unused))
+#else
+#define ATTR_UNUSED /**/
#endif
// Macro to check constants at compile time using a dummy typedef
#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) \
- typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
+ typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED
#ifndef _WIN64
#define SELECT_WIN_32_64(x32, x64) (x32)
#define SELECT_WIN_32_64(x32, x64) (x64)
#endif
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3558 2012-06-05 16:42:05Z chrfranke $";
-
-// 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
+// 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 4848 2018-12-05 18:30:46Z chrfranke $";
+
/////////////////////////////////////////////////////////////////////////////
// Windows I/O-controls, some declarations are missing in the include files
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);
ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
+// NVME_PASS_THROUGH
+
+#ifndef NVME_PASS_THROUGH_SRB_IO_CODE
+
+#define NVME_SIG_STR "NvmeMini"
+#define NVME_STORPORT_DRIVER 0xe000
+
+#define NVME_PASS_THROUGH_SRB_IO_CODE \
+ CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#pragma pack(1)
+typedef struct _NVME_PASS_THROUGH_IOCTL
+{
+ SRB_IO_CONTROL SrbIoCtrl;
+ ULONG VendorSpecific[6];
+ ULONG NVMeCmd[16]; // Command DW[0...15]
+ ULONG CplEntry[4]; // Completion DW[0...3]
+ ULONG Direction; // 0=No, 1=Out, 2=In, 3=I/O
+ ULONG QueueId; // 0=AdminQ
+ ULONG DataBufferLen; // sizeof(DataBuffer) if Data In
+ ULONG MetaDataLen;
+ ULONG ReturnBufferLen; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
+ UCHAR DataBuffer[1];
+} NVME_PASS_THROUGH_IOCTL;
+#pragma pack()
+
+#endif // NVME_PASS_THROUGH_SRB_IO_CODE
+
+ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE, (int)0xe0002000);
+ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, 152+1);
+ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL, offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer)+1);
+
+
// CSMI structs
ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL));
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"
/////////////////////////////////////////////////////////////////////////////
#pragma warning(disable:4250)
#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
+static int is_permissive()
+{
+ if (!failuretest_permissive) {
+ pout("To continue, add one or more '-T permissive' options.\n");
+ return 0;
+ }
+ failuretest_permissive--;
+ return 1;
+}
+
+// return number for drive letter, -1 on error
+// "[A-Za-z]:([/\\][.]?)?" => 0-25
+// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
+static int drive_letter(const char * s)
+{
+ return ( (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
+ && s[1] == ':'
+ && (!s[2] || ( strchr("/\\\"", s[2])
+ && (!s[3] || (s[3] == '.' && !s[4]))) ) ?
+ (s[0] & 0x1f) - 1 : -1);
+}
+
+// Skip trailing "/dev/", do not allow "/dev/X:"
+static const char * skipdev(const char * s)
+{
+ return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
+}
+
+// "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
+static int sdxy_to_phydrive(const char (& xy)[2+1])
+{
+ int phydrive = xy[0] - 'a';
+ if (xy[1])
+ phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
+ return phydrive;
+}
+
+static void copy_swapped(unsigned char * dest, const char * src, int destsize)
+{
+ int srclen = strcspn(src, "\r\n");
+ int i;
+ for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
+ dest[i] = src[i+1]; dest[i+1] = src[i];
+ }
+ if (i < destsize-1 && i < srclen)
+ dest[i+1] = src[i];
+}
+/////////////////////////////////////////////////////////////////////////////
+// win_smart_device
+
class win_smart_device
: virtual public /*implements*/ smart_device
{
};
-/////////////////////////////////////////////////////////////////////////////
+// Common routines for devices with HANDLEs
-class win_ata_device
-: public /*implements*/ ata_device,
- public /*extends*/ win_smart_device
+win_smart_device::~win_smart_device() throw()
{
-public:
- win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
+ if (m_fh != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_fh);
+}
- virtual ~win_ata_device() throw();
+bool win_smart_device::is_open() const
+{
+ return (m_fh != INVALID_HANDLE_VALUE);
+}
- virtual bool open();
+bool win_smart_device::close()
+{
+ if (m_fh == INVALID_HANDLE_VALUE)
+ return true;
+ BOOL rc = ::CloseHandle(m_fh);
+ m_fh = INVALID_HANDLE_VALUE;
+ return !!rc;
+}
- virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
- virtual bool ata_identify_is_cached() const;
+/////////////////////////////////////////////////////////////////////////////
-private:
- bool open(int phydrive, int logdrive, const char * options, int port);
+#define SMART_CYL_LOW 0x4F
+#define SMART_CYL_HI 0xC2
- std::string m_options;
- bool m_usr_options; // options set by user?
- bool m_admin; // open with admin access?
- bool m_id_is_cached; // ata_identify_is_cached() return value.
- bool m_is_3ware; // AMCC/3ware controller detected?
- int m_drive, m_port;
- int m_smartver_state;
-};
+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",
+ (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
+ r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
+}
+static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
+{
+ pout(" Input : "); print_ide_regs(ri, 0);
+ if (ro) {
+ pout(" Output: "); print_ide_regs(ro, 1);
+ }
+}
/////////////////////////////////////////////////////////////////////////////
-class win_scsi_device
-: public /*implements*/ scsi_device,
- virtual public /*extends*/ win_smart_device
+// call SMART_GET_VERSION, return device map or -1 on error
+
+static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
{
-public:
- win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+ GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
+ const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
+ DWORD num_out;
- virtual bool open();
+ if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
+ NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+ if (ata_debugmode)
+ pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
+ errno = ENOSYS;
+ return -1;
+ }
+ assert(num_out == sizeof(GETVERSIONINPARAMS));
- virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+ if (ata_debugmode > 1) {
+ 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);
+ if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
+ pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
+ vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx);
+ }
-private:
- bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
-};
+ if (ata_version_ex)
+ *ata_version_ex = vers_ex;
+ // TODO: Check vers.fCapabilities here?
+ return vers.bIDEDeviceMap;
+}
-/////////////////////////////////////////////////////////////////////////////
-#if WIN9X_SUPPORT
+// call SMART_* ioctl
-class win_aspi_device
-: public /*implements*/ scsi_device
+static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
{
-public:
- win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type);
-
- virtual bool is_open() const;
-
- virtual bool open();
+ SENDCMDINPARAMS inpar;
+ SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
- virtual bool close();
+ unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
+ const SENDCMDOUTPARAMS * outpar;
+ DWORD code, num_out;
+ unsigned int size_out;
+ const char * name;
- virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+ memset(&inpar, 0, sizeof(inpar));
+ inpar.irDriveRegs = *regs;
-private:
- int m_adapter;
- unsigned char m_id;
-};
+ // 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;
-#endif // WIN9X_SUPPORT
+ // Drive number 0-3 was required on Win9x/ME only
+ //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
+ //inpar.bDriveNumber = drive;
+ if (port >= 0) {
+ // Set RAID port
+ inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
+ inpar_ex.bPortNumber = port;
+ }
-//////////////////////////////////////////////////////////////////////
+ if (datasize == 512) {
+ code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
+ inpar.cBufferSize = size_out = 512;
+ }
+ else if (datasize == 0) {
+ code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
+ if (regs->bFeaturesReg == ATA_SMART_STATUS)
+ size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
+ // Note: cBufferSize must be 0 on Win9x
+ else
+ size_out = 0;
+ }
+ else {
+ errno = EINVAL;
+ return -1;
+ }
-class csmi_device
-: virtual public /*extends*/ smart_device
-{
-public:
- /// Get phy info
- bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
+ memset(&outbuf, 0, sizeof(outbuf));
- /// Check physical drive existence
- bool check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no);
+ if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
+ outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
+ // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
+ long err = GetLastError();
+ if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
+ pout(" %s failed, Error=%ld\n", name, err);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = ( err == ERROR_INVALID_FUNCTION/*9x*/
+ || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
+ || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+ return -1;
+ }
+ // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
-protected:
- csmi_device()
- : smart_device(never_called)
- { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
+ outpar = (const SENDCMDOUTPARAMS *)outbuf;
- /// Select physical drive
- bool select_phy(unsigned phy_no);
+ if (outpar->DriverStatus.bDriverError) {
+ if (ata_debugmode) {
+ pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+ outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
+ return -1;
+ }
- /// Get info for selected physical drive
- const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
- { return m_phy_ent; }
+ if (ata_debugmode > 1) {
+ 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));
+ }
- /// Call platform-specific CSMI ioctl
- virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
- unsigned csmi_bufsiz) = 0;
+ if (datasize)
+ memcpy(data, outpar->bBuffer, 512);
+ else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
+ if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
+ memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
+ else { // Workaround for driver not returning regs
+ if (ata_debugmode)
+ pout(" WARNING: driver does not return ATA registers in output buffer!\n");
+ *regs = inpar.irDriveRegs;
+ }
+ }
-private:
- CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
-};
+ return 0;
+}
-class csmi_ata_device
-: virtual public /*extends*/ csmi_device,
- virtual public /*implements*/ ata_device
-{
-public:
- virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
-
-protected:
- csmi_ata_device()
- : smart_device(never_called) { }
-};
-
-
-//////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// IDE PASS THROUGH (2000, XP, undocumented)
+//
+// Based on WinATA.cpp, 2002 c't/Matthias Withopf
+// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
-class win_csmi_device
-: public /*implements*/ csmi_ata_device
+static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
{
-public:
- win_csmi_device(smart_interface * intf, const char * dev_name,
- const char * req_type);
-
- virtual ~win_csmi_device() throw();
-
- virtual bool open();
-
- virtual bool close();
-
- virtual bool is_open() const;
-
- bool open_scsi();
-
-protected:
- virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
- unsigned csmi_bufsiz);
-
-private:
- HANDLE m_fh; ///< Controller device handle
- unsigned m_phy_no; ///< Physical drive number
-};
-
+ if (datasize > 512) {
+ errno = EINVAL;
+ return -1;
+ }
+ unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
+ ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+ DWORD num_out;
+ const unsigned char magic = 0xcf;
-//////////////////////////////////////////////////////////////////////
+ if (!buf) {
+ errno = ENOMEM;
+ return -1;
+ }
-class win_tw_cli_device
-: public /*implements*/ ata_device_with_command_set
-{
-public:
- win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
+ buf->IdeReg = *regs;
+ buf->DataBufferSize = datasize;
+ if (datasize)
+ buf->DataBuffer[0] = magic;
- virtual bool is_open() const;
+ if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
+ buf, size, buf, size, &num_out, NULL)) {
+ long err = GetLastError();
+ if (ata_debugmode) {
+ pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
+ print_ide_regs_io(regs, NULL);
+ }
+ VirtualFree(buf, 0, MEM_RELEASE);
+ errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+ return -1;
+ }
- virtual bool open();
+ // Check ATA status
+ if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
+ if (ata_debugmode) {
+ pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
+ print_ide_regs_io(regs, &buf->IdeReg);
+ }
+ VirtualFree(buf, 0, MEM_RELEASE);
+ errno = EIO;
+ return -1;
+ }
- virtual bool close();
+ // Check and copy data
+ if (datasize) {
+ if ( num_out != size
+ || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
+ if (ata_debugmode) {
+ pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
+ (unsigned)num_out, (unsigned)buf->DataBufferSize);
+ print_ide_regs_io(regs, &buf->IdeReg);
+ }
+ VirtualFree(buf, 0, MEM_RELEASE);
+ errno = EIO;
+ return -1;
+ }
+ memcpy(data, buf->DataBuffer, datasize);
+ }
-protected:
- virtual int ata_command_interface(smart_command_set command, int select, char * data);
+ if (ata_debugmode > 1) {
+ 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);
+ }
+ *regs = buf->IdeReg;
-private:
- bool m_ident_valid, m_smart_valid;
- ata_identify_device m_ident_buf;
- ata_smart_values m_smart_buf;
-};
+ // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
+ VirtualFree(buf, 0, MEM_RELEASE);
+ return 0;
+}
/////////////////////////////////////////////////////////////////////////////
-/// Areca RAID support
-
-/* ARECA IO CONTROL CODE*/
-#define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004
-#define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008
-#define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C
-#define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010
-#define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018
-#define ARECA_SIG_STR "ARCMSR"
-
+// ATA PASS THROUGH (Win2003, XP SP2)
-// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver
-typedef struct _SRB_IO_CONTROL
-{
- unsigned int HeaderLength;
- unsigned char Signature[8];
- unsigned int Timeout;
- unsigned int ControlCode;
- unsigned int ReturnCode;
- unsigned int Length;
-} sSRB_IO_CONTROL;
-
-typedef struct _SRB_BUFFER
-{
- sSRB_IO_CONTROL srbioctl;
- unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware
-} sSRB_BUFFER;
+// Warning:
+// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
+// transfer per command. Therefore, multi-sector transfers are only supported
+// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
+// or READ/WRITE LOG EXT work only with single sector transfers.
+// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
+// See:
+// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
-class win_areca_device
-: public /*implements*/ ata_device,
- public /*extends*/ win_smart_device
+static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
{
-public:
- win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum = 1);
-
- static int arcmsr_command_handler(HANDLE fh, unsigned long arcmsr_cmd, unsigned char *data, int data_len);
+ const int max_sectors = 32; // TODO: Allocate dynamic buffer
-protected:
- virtual bool open();
+ typedef struct {
+ ATA_PASS_THROUGH_EX apt;
+ ULONG Filler;
+ UCHAR ucDataBuf[max_sectors * 512];
+ } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
- virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+ const unsigned char magic = 0xcf;
- bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+ ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
+ ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
+ //ab.apt.PathId = 0;
+ //ab.apt.TargetId = 0;
+ //ab.apt.Lun = 0;
+ ab.apt.TimeOutValue = 60; // seconds
+ unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
+ ab.apt.DataBufferOffset = size;
-private:
- int m_disknum; ///< Disk number.
- int m_encnum; ///< Enclosure number.
-};
+ if (datasize > 0) {
+ if (datasize > (int)sizeof(ab.ucDataBuf)) {
+ errno = EINVAL;
+ return -1;
+ }
+ ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
+ ab.apt.DataTransferLength = datasize;
+ size += datasize;
+ ab.ucDataBuf[0] = magic;
+ }
+ else if (datasize < 0) {
+ if (-datasize > (int)sizeof(ab.ucDataBuf)) {
+ errno = EINVAL;
+ return -1;
+ }
+ ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
+ ab.apt.DataTransferLength = -datasize;
+ size += -datasize;
+ memcpy(ab.ucDataBuf, data, -datasize);
+ }
+ else {
+ assert(ab.apt.AtaFlags == 0);
+ assert(ab.apt.DataTransferLength == 0);
+ }
+ assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
+ IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
+ IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
+ *ctfregs = *regs;
-//////////////////////////////////////////////////////////////////////
-// Platform specific interfaces
+ if (prev_regs) {
+ *ptfregs = *prev_regs;
+ ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
+ }
-// Common to all windows flavors
-class win_smart_interface
-: public /*implements part of*/ smart_interface
-{
-public:
- virtual std::string get_os_version_str();
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
+ &ab, size, &ab, size, &num_out, NULL)) {
+ long err = GetLastError();
+ if (ata_debugmode) {
+ pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+ return -1;
+ }
- virtual std::string get_app_examples(const char * appname);
+ // Check ATA status
+ if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
+ if (ata_debugmode) {
+ pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
+ print_ide_regs_io(regs, ctfregs);
+ }
+ errno = EIO;
+ return -1;
+ }
-#ifndef __CYGWIN__
- virtual int64_t get_timer_usec();
-#endif
+ // Check and copy data
+ if (datasize > 0) {
+ if ( num_out != size
+ || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
+ if (ata_debugmode) {
+ pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
+ print_ide_regs_io(regs, ctfregs);
+ }
+ errno = EIO;
+ return -1;
+ }
+ memcpy(data, ab.ucDataBuf, datasize);
+ }
-//virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
-// const char * pattern = 0);
+ if (ata_debugmode > 1) {
+ pout(" IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out);
+ print_ide_regs_io(regs, ctfregs);
+ }
+ *regs = *ctfregs;
+ if (prev_regs)
+ *prev_regs = *ptfregs;
-protected:
- virtual ata_device * get_ata_device(const char * name, const char * type);
+ return 0;
+}
-//virtual scsi_device * get_scsi_device(const char * name, const char * type);
- virtual smart_device * autodetect_smart_device(const char * name);
-};
+/////////////////////////////////////////////////////////////////////////////
+// SMART IOCTL via SCSI MINIPORT ioctl
-#if WIN9X_SUPPORT
+// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
+// miniport driver (via SCSI port driver scsiport.sys).
+// It can be used to skip the missing or broken handling of some SMART
+// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
-// Win9x/ME reduced functionality
-class win9x_smart_interface
-: public /*extends*/ win_smart_interface
+static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
{
-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);
-};
+ // Select code
+ DWORD code = 0; const char * name = 0;
+ if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
+ code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
+ }
+ else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
+ case ATA_SMART_READ_VALUES:
+ code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
+ case ATA_SMART_READ_THRESHOLDS:
+ code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
+ case ATA_SMART_ENABLE:
+ code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
+ case ATA_SMART_DISABLE:
+ code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
+ case ATA_SMART_STATUS:
+ code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
+ case ATA_SMART_AUTOSAVE:
+ code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
+ //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
+ // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
+ case ATA_SMART_IMMEDIATE_OFFLINE:
+ code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
+ case ATA_SMART_AUTO_OFFLINE:
+ code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
+ case ATA_SMART_READ_LOG_SECTOR:
+ code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
+ case ATA_SMART_WRITE_LOG_SECTOR:
+ code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
+ }
+ if (!code) {
+ errno = ENOSYS;
+ return -1;
+ }
-#endif // WIN9X_SUPPORT
+ // Set SRB
+ struct {
+ SRB_IO_CONTROL srbc;
+ union {
+ SENDCMDINPARAMS in;
+ SENDCMDOUTPARAMS out;
+ } params;
+ char space[512-1];
+ } sb;
+ ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
+ memset(&sb, 0, sizeof(sb));
-// WinNT,2000,XP,...
-class winnt_smart_interface
-: public /*extends*/ win_smart_interface
-{
-public:
- virtual bool disable_system_auto_standby(bool disable);
+ unsigned size;
+ if (datasize > 0) {
+ if (datasize > (int)sizeof(sb.space)+1) {
+ errno = EINVAL;
+ return -1;
+ }
+ size = datasize;
+ }
+ else if (datasize < 0) {
+ if (-datasize > (int)sizeof(sb.space)+1) {
+ errno = EINVAL;
+ return -1;
+ }
+ size = -datasize;
+ memcpy(sb.params.in.bBuffer, data, size);
+ }
+ else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+ size = sizeof(IDEREGS);
+ else
+ size = 0;
+ sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+ memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
+ sb.srbc.Timeout = 60; // seconds
+ sb.srbc.ControlCode = code;
+ //sb.srbc.ReturnCode = 0;
+ sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
+ sb.params.in.irDriveRegs = *regs;
+ sb.params.in.cBufferSize = size;
- virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
- const char * pattern = 0);
+ // Call miniport ioctl
+ size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+ &sb, size, &sb, size, &num_out, NULL)) {
+ long err = GetLastError();
+ if (ata_debugmode) {
+ pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+ return -1;
+ }
-protected:
- virtual scsi_device * get_scsi_device(const char * name, const char * type);
+ // Check result
+ if (sb.srbc.ReturnCode) {
+ if (ata_debugmode) {
+ pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = EIO;
+ return -1;
+ }
- virtual smart_device * autodetect_smart_device(const char * name);
+ if (sb.params.out.DriverStatus.bDriverError) {
+ if (ata_debugmode) {
+ pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+ sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
+ return -1;
+ }
- virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+ if (ata_debugmode > 1) {
+ 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));
+ }
- virtual std::string get_valid_custom_dev_types_str();
-};
+ if (datasize > 0)
+ memcpy(data, sb.params.out.bBuffer, datasize);
+ else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+ memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
+ return 0;
+}
-//////////////////////////////////////////////////////////////////////
-#ifndef _WIN64
-// Running on 64-bit Windows as 32-bit app ?
-static bool is_wow64()
-{
- BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
- (BOOL (WINAPI *)(HANDLE, PBOOL))
- GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
- if (!IsWow64Process_p)
- return false;
- BOOL w64 = FALSE;
- if (!IsWow64Process_p(GetCurrentProcess(), &w64))
- return false;
- return !!w64;
-}
-#endif // _WIN64
+/////////////////////////////////////////////////////////////////////////////
+// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
-// Return info string about build host and OS version
-std::string win_smart_interface::get_os_version_str()
+static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
{
- char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
- = SMARTMONTOOLS_BUILD_HOST;
- if (vstr[1] < '6')
- vstr[1] = '6';
- char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
- const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
- assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
+ struct {
+ SRB_IO_CONTROL srbc;
+ IDEREGS regs;
+ UCHAR buffer[512];
+ } sb;
+ ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
- OSVERSIONINFOEXA 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))
- return vstr;
+ if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
+ errno = EINVAL;
+ return -1;
}
+ memset(&sb, 0, sizeof(sb));
+ strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
+ sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+ sb.srbc.Timeout = 60; // seconds
+ sb.srbc.ControlCode = 0xA0000000;
+ sb.srbc.ReturnCode = 0;
+ sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
+ sb.regs = *regs;
+ sb.regs.bReserved = port;
- 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"
- : "win8s"); break;
- default: w = 0; break;
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+ &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
+ long err = GetLastError();
+ if (ata_debugmode) {
+ pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+ return -1;
}
- const char * w64 = "";
-#ifndef _WIN64
- if (is_wow64())
- w64 = "(64)";
-#endif
-
- if (!w)
- snprintf(vptr, vlen, "-%s%lu.%lu%s",
- (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
- vi.dwMajorVersion, vi.dwMinorVersion, w64);
- else if (vi.wServicePackMinor)
- snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
- else if (vi.wServicePackMajor)
- snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
- else
- snprintf(vptr, vlen, "-%s%s", w, w64);
- return vstr;
-}
-
-#ifndef __CYGWIN__
-// MSVCRT only provides ftime() which uses GetSystemTime()
-// This provides only ~15ms resolution by default.
-// Use QueryPerformanceCounter instead (~300ns).
-// (Cygwin provides CLOCK_MONOTONIC which has the same effect)
-int64_t win_smart_interface::get_timer_usec()
-{
- static int64_t freq = 0;
-
- LARGE_INTEGER t;
- if (freq == 0)
- freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
- if (freq <= 0)
- return smart_interface::get_timer_usec();
-
- if (!QueryPerformanceCounter(&t))
- return -1;
- if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
+ if (sb.srbc.ReturnCode) {
+ if (ata_debugmode) {
+ pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode);
+ print_ide_regs_io(regs, NULL);
+ }
+ errno = EIO;
return -1;
+ }
- return (t.QuadPart * 1000000LL) / freq;
-}
-#endif // __CYGWIN__
+ // Copy data
+ if (datasize > 0)
+ memcpy(data, sb.buffer, datasize);
+ if (ata_debugmode > 1) {
+ pout(" ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out);
+ print_ide_regs_io(regs, &sb.regs);
+ }
+ *regs = sb.regs;
-// Return value for device detection functions
-enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
+ return 0;
+}
-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,
- unsigned short & product_id);
-static const char * ata_get_def_options(void);
+/////////////////////////////////////////////////////////////////////////////
+// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
+// 3DM/CLI "Rescan Controller" function does not to always update it.
-static int is_permissive()
+static int update_3ware_devicemap_ioctl(HANDLE hdevice)
{
- if (!failuretest_permissive) {
- pout("To continue, add one or more '-T permissive' options.\n");
- return 0;
- }
- failuretest_permissive--;
- return 1;
-}
+ SRB_IO_CONTROL srbc;
+ memset(&srbc, 0, sizeof(srbc));
+ strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
+ srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+ srbc.Timeout = 60; // seconds
+ srbc.ControlCode = 0xCC010014;
+ srbc.ReturnCode = 0;
+ srbc.Length = 0;
-// return number for drive letter, -1 on error
-// "[A-Za-z]:([/\\][.]?)?" => 0-25
-// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
-static int drive_letter(const char * s)
-{
- return ( (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
- && s[1] == ':'
- && (!s[2] || ( strchr("/\\\"", s[2])
- && (!s[3] || (s[3] == '.' && !s[4]))) ) ?
- (s[0] & 0x1f) - 1 : -1);
-}
-
-// Skip trailing "/dev/", do not allow "/dev/X:"
-static const char * skipdev(const char * s)
-{
- return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
-}
-
-ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
-{
- const char * testname = skipdev(name);
- if (!strncmp(testname, "csmi", 4))
- return new win_csmi_device(this, name, type);
- if (!strncmp(testname, "tw_cli", 6))
- return new win_tw_cli_device(this, name, type);
- return new win_ata_device(this, name, type);
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+ &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
+ long err = GetLastError();
+ if (ata_debugmode)
+ pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+ errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+ return -1;
+ }
+ if (srbc.ReturnCode) {
+ if (ata_debugmode)
+ pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode);
+ errno = EIO;
+ return -1;
+ }
+ if (ata_debugmode > 1)
+ pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n");
+ return 0;
}
-#ifdef WIN9X_SUPPORT
-scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type)
-{
- return new win_aspi_device(this, name, type);
-}
+/////////////////////////////////////////////////////////////////////////////
+// IOCTL_STORAGE_QUERY_PROPERTY
-#endif
+union STORAGE_DEVICE_DESCRIPTOR_DATA {
+ STORAGE_DEVICE_DESCRIPTOR desc;
+ char raw[256];
+};
-scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type)
-{
- const char * testname = skipdev(name);
- if (!strncmp(testname, "scsi", 4))
-#if WIN9X_SUPPORT
- return new win_aspi_device(this, name, type);
-#else
- return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0);
-#endif
- return new win_scsi_device(this, name, type);
-}
+// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
+// (This works without admin rights)
-static win_dev_type get_dev_type(const char * name, int & phydrive)
+static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
{
- phydrive = -1;
- name = skipdev(name);
- if (!strncmp(name, "st", 2))
- return DEV_SCSI;
- if (!strncmp(name, "nst", 3))
- return DEV_SCSI;
- if (!strncmp(name, "tape", 4))
- return DEV_SCSI;
+ STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
+ memset(data, 0, sizeof(*data));
- int logdrive = drive_letter(name);
- if (logdrive >= 0) {
- win_dev_type type = get_log_drive_type(logdrive);
- return (type != DEV_UNKNOWN ? type : DEV_SCSI);
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+ &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
+ if (ata_debugmode > 1 || scsi_debugmode > 1)
+ pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
+ errno = ENOSYS;
+ return -1;
}
- char drive[1+1] = "";
- if (sscanf(name, "sd%1[a-z]", drive) == 1) {
- phydrive = drive[0] - 'a';
- return get_phy_drive_type(phydrive);
+ if (ata_debugmode > 1 || scsi_debugmode > 1) {
+ pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
+ " Vendor: \"%s\"\n"
+ " Product: \"%s\"\n"
+ " Revision: \"%s\"\n"
+ " Removable: %s\n"
+ " BusType: 0x%02x\n",
+ (data->desc.VendorIdOffset ? data->raw+data->desc.VendorIdOffset : "(null)"),
+ (data->desc.ProductIdOffset ? data->raw+data->desc.ProductIdOffset : "(null)"),
+ (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
+ (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
+ );
}
-
- phydrive = -1;
- if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
- return get_phy_drive_type(phydrive);
- return DEV_UNKNOWN;
-}
-
-smart_device * win_smart_interface::autodetect_smart_device(const char * name)
-{
- 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?
- int disknum = -1, n1 = -1, n2 = -1;
- int encnum = 1;
- HANDLE fh = INVALID_HANDLE_VALUE;
- char devpath[32];
+/////////////////////////////////////////////////////////////////////////////
+// IOCTL_STORAGE_PREDICT_FAILURE
- if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
- if (!(1 <= disknum && disknum <= 128)) {
- set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
- return 0;
- }
- if (!(1 <= encnum && encnum <= 8)) {
- set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
- return 0;
- }
+// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
+// or -1 on error, optionally return VendorSpecific data.
+// (This works without admin rights)
- 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));
- sprintf(devpath, "\\\\.\\scsi%d:", idx);
- if ( (fh = CreateFile( devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE ) {
- if (win_areca_device::arcmsr_command_handler(fh, ARCMSR_IOCTL_RETURN_CODE_3F, NULL, 0) == 0) {
- if (ctlrindex-- == 0) {
- return new win_areca_device(this, devpath, fh, disknum, encnum);
- }
- }
- CloseHandle(fh);
- }
- }
- set_err(ENOENT, "No Areca controller found");
- }
- else
- set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
- }
+static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
+{
+ STORAGE_PREDICT_FAILURE pred;
+ memset(&pred, 0, sizeof(pred));
- return 0;
-}
+ DWORD num_out;
+ if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
+ 0, 0, &pred, sizeof(pred), &num_out, NULL)) {
+ if (ata_debugmode > 1)
+ pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
+ errno = ENOSYS;
+ return -1;
+ }
-std::string winnt_smart_interface::get_valid_custom_dev_types_str()
-{
- return "areca,N[/E]";
+ if (ata_debugmode > 1) {
+ pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
+ " PredictFailure: 0x%08x\n"
+ " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
+ (unsigned)pred.PredictFailure,
+ pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
+ pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
+ );
+ }
+ if (data)
+ memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
+ return (!pred.PredictFailure ? 0 : 1);
}
-smart_device * winnt_smart_interface::autodetect_smart_device(const char * name)
+// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
+static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
{
- smart_device * dev = win_smart_interface::autodetect_smart_device(name);
- if (dev)
- return dev;
-
- if (!strncmp(skipdev(name), "csmi", 4))
- return new win_csmi_device(this, name, "");
+ STORAGE_DEVICE_DESCRIPTOR_DATA data;
+ if (storage_query_property_ioctl(hdevice, &data))
+ return -1;
- int phydrive = -1;
- win_dev_type type = get_dev_type(name, phydrive);
+ memset(id, 0, sizeof(*id));
- if (type == DEV_ATA)
- return new win_ata_device(this, name, "");
- if (type == DEV_SCSI)
- return new win_scsi_device(this, name, "");
+ // Some drivers split ATA model string into VendorId and ProductId,
+ // others return it as ProductId only.
+ char model[sizeof(id->model) + 1] = "";
- 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, ""));
+ 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];
}
- return 0;
-}
+ if (data.desc.ProductIdOffset) {
+ while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
+ i--;
+ // Ignore VendorId "ATA"
+ if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
+ i = 0;
+ for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
+ model[i] = data.raw[data.desc.ProductIdOffset+j];
+ }
+ while (i > 0 && model[i-1] == ' ')
+ i--;
+ model[i] = 0;
+ copy_swapped(id->model, model, sizeof(id->model));
-#if WIN9X_SUPPORT
+ if (data.desc.ProductRevisionOffset)
+ copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
-// Scan for devices on Win9x/ME
+ id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+ id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
+ return 0;
+}
-bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist,
- const char * type, const char * pattern /* = 0*/)
+// Get Serial Number in IDENTIFY from WMI
+static bool get_serial_from_wmi(int drive, ata_identify_device * id)
{
- if (pattern) {
- set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+ bool debug = (ata_debugmode > 1);
+
+ wbem_services ws;
+ if (!ws.connect()) {
+ if (debug)
+ pout("WMI connect failed\n");
return false;
}
- if (!type || !strcmp(type, "ata")) {
- if (!ata_scan(devlist))
- return false;
- }
+ wbem_object wo;
+ if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
+ "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
+ return false;
- if (!type || !strcmp(type, "scsi")) {
- if (!scsi_scan(devlist))
- 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;
}
-#endif // WIN9X_SUPPORT
+/////////////////////////////////////////////////////////////////////////////
+// USB ID detection using WMI
-// Scan for devices
-
-bool winnt_smart_interface::scan_smart_devices(smart_device_list & devlist,
- const char * type, const char * pattern /* = 0*/)
+// 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)
{
- if (pattern) {
- set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+ bool debug = (scsi_debugmode > 1);
+
+ wbem_services ws;
+ if (!ws.connect()) {
+ if (debug)
+ pout("WMI connect failed\n");
return false;
}
- // Set valid types
- bool ata, scsi, usb, csmi;
- if (!type) {
- ata = scsi = usb = csmi = true;
- }
- else {
- ata = scsi = usb = csmi = false;
- if (!strcmp(type, "ata"))
- ata = true;
- else if (!strcmp(type, "scsi"))
- scsi = 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, scsi, usb, csmi", type);
+ // Get device name
+ std::string name;
+
+ wbem_object wo;
+ 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;
- }
- }
- // Scan up to 10 drives and 2 3ware controllers
- const int max_raid = 2;
- bool raid_seen[max_raid] = {false, false};
+ std::string partid = wo.get_str("DeviceID");
+ if (debug)
+ pout("%c: --> \"%s\" -->\n", 'A'+logdrive, partid.c_str());
- char name[20];
- for (int i = 0; i <= 9; i++) {
- sprintf(name, "/dev/sd%c", 'a'+i);
- GETVERSIONINPARAMS_EX vers_ex;
+ // Partition ID -> Physical drive info
+ if (!ws.query1(wo, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
+ partid.c_str()))
+ return false;
- 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;
+ name = wo.get_str("Model");
+ if (debug)
+ pout("%s --> \"%s\":\n", wo.get_str("DeviceID").c_str(), name.c_str());
+ }
- // Interpret RAID drive map if present
- if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
- // Skip if too many controllers or logical drive from this controller already seen
- if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
- continue;
- 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)) {
- sprintf(name+len, ",%u", pi);
- devlist.push_back( new win_ata_device(this, name, "ata") );
- }
- }
- }
- else {
- devlist.push_back( new win_ata_device(this, name, "ata") );
- }
- break;
+ 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;
- case DEV_SCSI:
- // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
- if (!scsi)
- continue;
- devlist.push_back( new win_scsi_device(this, name, "scsi") );
- break;
+ name = wo.get_str("Model");
+ if (debug)
+ pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive, name.c_str());
+ }
+ else
+ return false;
- 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;
- default:
- // Unknown type
- break;
+ // Get USB_CONTROLLER -> DEVICE associations
+ wbem_enumerator we;
+ if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
+ return false;
+
+ unsigned short usb_venid = 0, prev_usb_venid = 0;
+ unsigned short usb_proid = 0, prev_usb_proid = 0;
+ std::string prev_usb_ant;
+ std::string prev_ant, ant, dep;
+
+ const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"");
+
+ while (we.next(wo)) {
+ prev_ant = ant;
+ // Find next 'USB_CONTROLLER, DEVICE' pair
+ ant = wo.get_str("Antecedent");
+ dep = wo.get_str("Dependent");
+
+ if (debug && ant != prev_ant)
+ pout(" %s:\n", ant.c_str());
+
+ // Extract DeviceID
+ 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());
+ continue;
}
- }
- if (csmi) {
- // Scan CSMI devices
- for (int i = 0; i <= 9; i++) {
- snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
- win_csmi_device test_dev(this, name, "");
- if (!test_dev.open_scsi())
+ std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
+
+ if (str_starts_with(devid, "USB\\\\VID_")) {
+ // USB bridge entry, save CONTROLLER, ID
+ int nc = -1;
+ if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
+ &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
+ prev_usb_venid = prev_usb_proid = 0;
+ }
+ prev_usb_ant = ant;
+ if (debug)
+ pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
+ }
+ 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());
+
+ // Retrieve name
+ wbem_object wo2;
+ if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
continue;
- CSMI_SAS_PHY_INFO phy_info;
- if (!test_dev.get_phy_info(phy_info))
+ std::string name2 = wo2.get_str("Name");
+
+ // Continue if not name of physical disk drive
+ if (name2 != name) {
+ if (debug)
+ pout(" +---> (\"%s\")\n", name2.c_str());
continue;
+ }
- for (int pi = 0; pi < phy_info.bNumberOfPhys; pi++) {
- if (!test_dev.check_phy(phy_info, pi))
- continue;
- snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
- devlist.push_back( new win_csmi_device(this, name, "ata") );
+ // Fail if previous USB bridge is associated to other controller or ID is unknown
+ if (!(ant == prev_usb_ant && prev_usb_venid)) {
+ if (debug)
+ pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
+ return false;
+ }
+
+ // Handle multiple devices with same name
+ if (usb_venid) {
+ // Fail if multiple devices with same name have different USB bridge types
+ if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
+ if (debug)
+ pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
+ return false;
+ }
}
+
+ // Found
+ usb_venid = prev_usb_venid;
+ usb_proid = prev_usb_proid;
+ if (debug)
+ pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
+
+ // Continue to check for duplicate names ...
+ }
+ else {
+ if (debug)
+ pout(" | \"%s\"\n", devid.c_str());
}
}
+
+ if (!usb_venid)
+ return false;
+
+ vendor_id = usb_venid;
+ product_id = usb_proid;
+
return true;
}
-// get examples for smartctl
-std::string win_smart_interface::get_app_examples(const char * appname)
-{
- if (strcmp(appname, "smartctl"))
- return "";
- return "=================================================== SMARTCTL EXAMPLES =====\n\n"
- " smartctl -a /dev/hda (Prints all SMART information)\n\n"
- " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
- " (Enables SMART on first disk)\n\n"
- " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
- " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\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"
- " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
- " smartctl -a /dev/pd3\n"
- " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
- " smartctl -a /dev/tape1\n"
- " (Prints all information for SCSI tape on Tape 1)\n"
- " smartctl -A /dev/hdb,3\n"
- " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
- " smartctl -A /dev/tw_cli/c0/p1\n"
- " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
- " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
- " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
- " on 1st Areca RAID controller)\n"
- "\n"
- " ATA SMART access methods and ordering may be specified by modifiers\n"
- " following the device name: /dev/hdX:[saicm], where\n"
- " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
- " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
- " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
- + strprintf(
- " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
- );
-}
+/////////////////////////////////////////////////////////////////////////////
+// Call GetDevicePowerState()
+// returns: 1=active, 0=standby, -1=error
+// (This would also work for SCSI drives)
-bool winnt_smart_interface::disable_system_auto_standby(bool disable)
+static int get_device_power_state(HANDLE hdevice)
{
- if (disable) {
- SYSTEM_POWER_STATUS ps;
- if (!GetSystemPowerStatus(&ps))
- return set_err(ENOSYS, "Unknown power status");
- if (ps.ACLineStatus != 1) {
- SetThreadExecutionState(ES_CONTINUOUS);
- if (ps.ACLineStatus == 0)
- set_err(EIO, "AC offline");
- else
- set_err(EIO, "Unknown AC line status");
- return false;
- }
+ BOOL state = TRUE;
+ if (!GetDevicePowerState(hdevice, &state)) {
+ long err = GetLastError();
+ if (ata_debugmode)
+ pout(" GetDevicePowerState() failed, Error=%ld\n", err);
+ errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+ // TODO: This may not work as expected on transient errors,
+ // because smartd interprets -1 as SLEEP mode regardless of errno.
+ return -1;
}
- if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
- return set_err(ENOSYS);
- return true;
+ if (ata_debugmode > 1)
+ pout(" GetDevicePowerState() succeeded, state=%d\n", state);
+ return state;
}
/////////////////////////////////////////////////////////////////////////////
-// ATA Interface
-/////////////////////////////////////////////////////////////////////////////
+// win_ata_device
-#define SMART_CYL_LOW 0x4F
-#define SMART_CYL_HI 0xC2
-
-static void print_ide_regs(const IDEREGS * r, int out)
+class win_ata_device
+: public /*implements*/ ata_device,
+ public /*extends*/ win_smart_device
{
- 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,
- r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
-}
+public:
+ win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
-static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
-{
- pout(" Input : "); print_ide_regs(ri, 0);
- if (ro) {
- pout(" Output: "); print_ide_regs(ro, 1);
- }
-}
+ virtual ~win_ata_device() throw();
-/////////////////////////////////////////////////////////////////////////////
+ virtual bool open();
-// call SMART_GET_VERSION, return device map or -1 on error
+ virtual bool is_powered_down();
-static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
-{
- GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
- const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
- DWORD num_out;
+ virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
- if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
- NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
- if (ata_debugmode)
- pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
- errno = ENOSYS;
- return -1;
- }
- assert(num_out == sizeof(GETVERSIONINPARAMS));
+ virtual bool ata_identify_is_cached() const;
- if (ata_debugmode > 1) {
- pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
- " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
- num_out, vers.bVersion, vers.bRevision,
- vers.fCapabilities, vers.bIDEDeviceMap);
- if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
- pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
- vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
- }
+private:
+ bool open(bool query_device);
- if (ata_version_ex)
- *ata_version_ex = vers_ex;
+ bool open(int phydrive, int logdrive, const char * options, int port, bool query_device);
- // TODO: Check vers.fCapabilities here?
- return vers.bIDEDeviceMap;
-}
+ std::string m_options;
+ bool m_usr_options; // options set by user?
+ 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_is_3ware; // LSI/3ware controller detected?
+ int m_port; // LSI/3ware port
+ int m_smartver_state;
+};
-// call SMART_* ioctl
+win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+ m_usr_options(false),
+ m_admin(false),
+ m_phydrive(-1),
+ m_id_is_cached(false),
+ m_is_3ware(false),
+ m_port(-1),
+ m_smartver_state(0)
+{
+}
-static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
+win_ata_device::~win_ata_device() throw()
{
- SENDCMDINPARAMS inpar;
- SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
+}
- unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
- const SENDCMDOUTPARAMS * outpar;
- DWORD code, num_out;
- unsigned int size_out;
- const char * name;
+// Get default ATA device options
- memset(&inpar, 0, sizeof(inpar));
- inpar.irDriveRegs = *regs;
- // drive is set to 0-3 on Win9x only
- inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
- inpar.bDriveNumber = drive;
+static const char * ata_get_def_options()
+{
+ return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
+ // STORAGE_*, SCSI_MINIPORT_*
+}
- if (port >= 0) {
- // Set RAID port
- inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
- inpar_ex.bPortNumber = port;
- }
+// Open ATA device
- if (datasize == 512) {
- code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
- inpar.cBufferSize = size_out = 512;
+bool win_ata_device::open()
+{
+ // Open device for r/w operations
+ return open(false);
+}
+
+bool win_ata_device::open(bool query_device)
+{
+ const char * name = skipdev(get_dev_name()); int len = strlen(name);
+ // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
+ char drive[2+1] = "", options[8+1] = ""; int n1 = -1, 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) ) {
+ return open(sdxy_to_phydrive(drive), -1, options, -1, query_device);
}
- else if (datasize == 0) {
- code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
- if (regs->bFeaturesReg == ATA_SMART_STATUS)
- size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
- // Note: cBufferSize must be 0 on Win9x
- else
- size_out = 0;
+ // [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;
+ unsigned port = ~0;
+ 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) ) {
+ return open(sdxy_to_phydrive(drive), -1, options, port, query_device);
}
- else {
- errno = EINVAL;
- return -1;
+ // pd<m>,N => Physical drive <m>, RAID port N
+ int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
+ if ( sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
+ && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
+ return open(phydrive, -1, "", (int)port, query_device);
+ }
+ // [a-zA-Z]: => Physical drive behind logical drive 0-25
+ int logdrive = drive_letter(name);
+ if (logdrive >= 0) {
+ return open(-1, logdrive, "", -1, query_device);
}
- memset(&outbuf, 0, sizeof(outbuf));
+ return set_err(EINVAL);
+}
- if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
- outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
- // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
- long err = GetLastError();
- if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
- pout(" %s failed, Error=%ld\n", name, err);
- print_ide_regs_io(regs, NULL);
- }
- errno = ( err == ERROR_INVALID_FUNCTION/*9x*/
- || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
- || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
- return -1;
- }
- // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
- outpar = (const SENDCMDOUTPARAMS *)outbuf;
+bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port, bool query_device)
+{
+ m_phydrive = -1;
+ char devpath[30];
+ if (0 <= phydrive && phydrive <= 255)
+ snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive));
+ else if (0 <= logdrive && logdrive <= 'Z'-'A')
+ snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
+ else
+ return set_err(ENOENT);
- if (outpar->DriverStatus.bDriverError) {
- if (ata_debugmode) {
- pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
- outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
- print_ide_regs_io(regs, NULL);
- }
- errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
- return -1;
+ // Open device
+ HANDLE h = INVALID_HANDLE_VALUE;
+ if (!(*options && !options[strspn(options, "fp")]) && !query_device) {
+ // Open with admin rights
+ m_admin = true;
+ h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0);
}
-
- if (ata_debugmode > 1) {
- pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
- num_out, outpar->cBufferSize);
- print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
- (const IDEREGS *)(outpar->bBuffer) : NULL));
+ if (h == INVALID_HANDLE_VALUE) {
+ // Open without admin rights
+ m_admin = false;
+ h = CreateFileA(devpath, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0);
+ }
+ if (h == INVALID_HANDLE_VALUE) {
+ long err = GetLastError();
+ 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;
}
+ set_fh(h);
- if (datasize)
- memcpy(data, outpar->bBuffer, 512);
- else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
- if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
- memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
- else { // Workaround for driver not returning regs
- if (ata_debugmode)
- pout(" WARNING: driver does not return ATA registers in output buffer!\n");
- *regs = inpar.irDriveRegs;
+ // Warn once if admin rights are missing
+ if (!m_admin && !query_device) {
+ static bool noadmin_warning = false;
+ if (!noadmin_warning) {
+ pout("Warning: Limited functionality due to missing admin rights\n");
+ noadmin_warning = true;
}
}
- return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// IDE PASS THROUGH (2000, XP, undocumented)
-//
-// Based on WinATA.cpp, 2002 c't/Matthias Withopf
-// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
+ if (ata_debugmode > 1)
+ pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
-static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
-{
- if (datasize > 512) {
- errno = EINVAL;
- return -1;
+ m_usr_options = false;
+ if (*options) {
+ // Save user options
+ m_options = options; m_usr_options = true;
}
- unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
- ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
- DWORD num_out;
- const unsigned char magic = 0xcf;
-
- if (!buf) {
- errno = ENOMEM;
- return -1;
+ else if (port >= 0)
+ // RAID: SMART_* and SCSI_MINIPORT
+ m_options = "s3";
+ else {
+ // Set default options according to Windows version
+ static const char * def_options = ata_get_def_options();
+ m_options = def_options;
}
- buf->IdeReg = *regs;
- buf->DataBufferSize = datasize;
- if (datasize)
- buf->DataBuffer[0] = magic;
+ // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
+ m_port = port;
+ if (port < 0)
+ return true;
- if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
- buf, size, buf, size, &num_out, NULL)) {
- long err = GetLastError();
- if (ata_debugmode) {
- pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
- print_ide_regs_io(regs, NULL);
+ // 3ware RAID: Get port map
+ GETVERSIONINPARAMS_EX vers_ex;
+ int devmap = smart_get_version(h, &vers_ex);
+
+ // 3ware RAID if vendor id present
+ m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+
+ unsigned portmap = 0;
+ if (port >= 0 && devmap >= 0) {
+ // 3ware RAID: check vendor id
+ if (!m_is_3ware) {
+ pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
+ "This is no 3ware 9000 controller or driver has no SMART support.\n",
+ vers_ex.wIdentifier);
+ devmap = -1;
}
- VirtualFree(buf, 0, MEM_RELEASE);
- errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
- return -1;
+ else
+ portmap = vers_ex.dwDeviceMapEx;
}
-
- // Check ATA status
- if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
- if (ata_debugmode) {
- pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
- print_ide_regs_io(regs, &buf->IdeReg);
+ if (devmap < 0) {
+ pout("%s: ATA driver has no SMART support\n", devpath);
+ if (!is_permissive()) {
+ close();
+ return set_err(ENOSYS);
}
- VirtualFree(buf, 0, MEM_RELEASE);
- errno = EIO;
- return -1;
}
+ m_smartver_state = 1;
- // Check and copy data
- if (datasize) {
- if ( num_out != size
- || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
- if (ata_debugmode) {
- pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
- num_out, buf->DataBufferSize);
- print_ide_regs_io(regs, &buf->IdeReg);
+ {
+ // 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 & (1U << port))) {
+ if (!is_permissive()) {
+ close();
+ return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
}
- VirtualFree(buf, 0, MEM_RELEASE);
- errno = EIO;
- return -1;
}
- memcpy(data, buf->DataBuffer, datasize);
- }
-
- if (ata_debugmode > 1) {
- pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
- num_out, buf->DataBufferSize);
- print_ide_regs_io(regs, &buf->IdeReg);
}
- *regs = buf->IdeReg;
- // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
- VirtualFree(buf, 0, MEM_RELEASE);
- return 0;
+ return true;
}
/////////////////////////////////////////////////////////////////////////////
-// ATA PASS THROUGH (Win2003, XP SP2)
-
-// Warning:
-// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
-// transfer per command. Therefore, multi-sector transfers are only supported
-// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
-// or READ/WRITE LOG EXT work only with single sector transfers.
-// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
-// See:
-// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
-static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
+// Query OS if device is powered up or down.
+bool win_ata_device::is_powered_down()
{
- const int max_sectors = 32; // TODO: Allocate dynamic buffer
+ // To check power mode, we open device for query operations only.
+ // Opening for SMART r/w operations can already spin up the disk.
+ bool self_open = !is_open();
+ if (self_open)
+ if (!open(true))
+ return false;
+ int rc = get_device_power_state(get_fh());
+ if (self_open)
+ close();
+ return !rc;
+}
- typedef struct {
- ATA_PASS_THROUGH_EX apt;
- ULONG Filler;
- UCHAR ucDataBuf[max_sectors * 512];
- } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
+/////////////////////////////////////////////////////////////////////////////
- const unsigned char magic = 0xcf;
+// Interface to ATA devices
+bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+ // No multi-sector support for now, see above
+ // warning about IOCTL_ATA_PASS_THROUGH
+ if (!ata_cmd_is_supported(in,
+ ata_device::supports_data_out |
+ ata_device::supports_output_regs |
+ ata_device::supports_48bit)
+ )
+ return false;
- ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
- ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
- //ab.apt.PathId = 0;
- //ab.apt.TargetId = 0;
- //ab.apt.Lun = 0;
- ab.apt.TimeOutValue = 10;
- unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
- ab.apt.DataBufferOffset = size;
+ // 3ware RAID: SMART DISABLE without port number disables SMART functions
+ if ( m_is_3ware && m_port < 0
+ && in.in_regs.command == ATA_SMART_CMD
+ && in.in_regs.features == ATA_SMART_DISABLE)
+ return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
- if (datasize > 0) {
- if (datasize > (int)sizeof(ab.ucDataBuf)) {
- errno = EINVAL;
- return -1;
- }
- ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
- ab.apt.DataTransferLength = datasize;
- size += datasize;
- ab.ucDataBuf[0] = magic;
- }
- else if (datasize < 0) {
- if (-datasize > (int)sizeof(ab.ucDataBuf)) {
- errno = EINVAL;
- return -1;
- }
- ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
- ab.apt.DataTransferLength = -datasize;
- size += -datasize;
- memcpy(ab.ucDataBuf, data, -datasize);
- }
- else {
- assert(ab.apt.AtaFlags == 0);
- assert(ab.apt.DataTransferLength == 0);
- }
-
- assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
- IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
- IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
- *ctfregs = *regs;
-
- if (prev_regs) {
- *ptfregs = *prev_regs;
- ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
- }
-
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
- &ab, size, &ab, size, &num_out, NULL)) {
- long err = GetLastError();
- if (ata_debugmode) {
- pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
- print_ide_regs_io(regs, NULL);
- }
- errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
- return -1;
- }
-
- // Check ATA status
- if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
- if (ata_debugmode) {
- pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
- print_ide_regs_io(regs, ctfregs);
- }
- errno = EIO;
- return -1;
- }
-
- // Check and copy data
- if (datasize > 0) {
- if ( num_out != size
- || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
- if (ata_debugmode) {
- pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);
- print_ide_regs_io(regs, ctfregs);
- }
- errno = EIO;
- return -1;
- }
- memcpy(data, ab.ucDataBuf, datasize);
- }
+ // Determine ioctl functions valid for this ATA cmd
+ const char * valid_options = 0;
- if (ata_debugmode > 1) {
- pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
- print_ide_regs_io(regs, ctfregs);
- }
- *regs = *ctfregs;
- if (prev_regs)
- *prev_regs = *ptfregs;
+ switch (in.in_regs.command) {
+ case ATA_IDENTIFY_DEVICE:
+ case ATA_IDENTIFY_PACKET_DEVICE:
+ // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+ // and SCSI_MINIPORT_* if requested by user
+ valid_options = (m_usr_options ? "saimf" : "saif");
+ break;
- return 0;
-}
+ case ATA_CHECK_POWER_MODE:
+ // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
+ valid_options = "pai3";
+ break;
+ case ATA_SMART_CMD:
+ switch (in.in_regs.features) {
+ case ATA_SMART_READ_VALUES:
+ case ATA_SMART_READ_THRESHOLDS:
+ case ATA_SMART_AUTOSAVE:
+ case ATA_SMART_ENABLE:
+ case ATA_SMART_DISABLE:
+ case ATA_SMART_AUTO_OFFLINE:
+ // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+ // and SCSI_MINIPORT_* if requested by user
+ valid_options = (m_usr_options ? "saimf" : "saif");
+ break;
-/////////////////////////////////////////////////////////////////////////////
-// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
+ case ATA_SMART_IMMEDIATE_OFFLINE:
+ // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
+ valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ?
+ "saim3" : "aim3");
+ break;
-// undocumented SCSI opcode to for ATA passthrough
-#define SCSIOP_ATA_PASSTHROUGH 0xCC
+ case ATA_SMART_READ_LOG_SECTOR:
+ // SMART_RCV_DRIVE_DATA does not support READ_LOG
+ // Try SCSI_MINIPORT also to skip buggy class driver
+ // SMART functions do not support multi sector I/O.
+ if (in.size == 512)
+ valid_options = (m_usr_options ? "saim3" : "aim3");
+ else
+ valid_options = "a";
+ break;
-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;
+ case ATA_SMART_WRITE_LOG_SECTOR:
+ // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
+ // but SCSI_MINIPORT_* only if requested by user and single sector.
+ valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
+ break;
- SCSI_PASS_THROUGH_WITH_BUFFERS sb;
- IDEREGS * cdbregs;
- unsigned int size;
- DWORD num_out;
- const unsigned char magic = 0xcf;
+ case ATA_SMART_STATUS:
+ valid_options = (m_usr_options ? "saimf" : "saif");
+ break;
- 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;
+ default:
+ // Unknown SMART command, handle below
+ break;
+ }
+ break;
- 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;
+ default:
+ // Other ATA command, handle below
+ break;
}
- // 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;
+ if (!valid_options) {
+ // No special ATA command found above, select a generic pass through ioctl.
+ if (!( in.direction == ata_cmd_in::no_data
+ || (in.direction == ata_cmd_in::data_in && in.size == 512))
+ || in.in_regs.is_48bit_cmd() )
+ // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
+ valid_options = "a";
+ else
+ // ATA/IDE_PASS_THROUGH
+ valid_options = "ai";
}
- // Cannot check ATA status, because command does not return IDEREGS
+ if (!m_admin) {
+ // Restrict to IOCTL_STORAGE_*
+ if (strchr(valid_options, 'f'))
+ valid_options = "f";
+ else if (strchr(valid_options, 'p'))
+ valid_options = "p";
+ else
+ return set_err(ENOSYS, "Function requires admin rights");
+ }
- // 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);
+ // Set IDEREGS
+ IDEREGS regs, prev_regs;
+ {
+ const ata_in_regs & lo = in.in_regs;
+ regs.bFeaturesReg = lo.features;
+ regs.bSectorCountReg = lo.sector_count;
+ regs.bSectorNumberReg = lo.lba_low;
+ regs.bCylLowReg = lo.lba_mid;
+ regs.bCylHighReg = lo.lba_high;
+ regs.bDriveHeadReg = lo.device;
+ regs.bCommandReg = lo.command;
+ regs.bReserved = 0;
+ }
+ if (in.in_regs.is_48bit_cmd()) {
+ const ata_in_regs & hi = in.in_regs.prev;
+ prev_regs.bFeaturesReg = hi.features;
+ prev_regs.bSectorCountReg = hi.sector_count;
+ prev_regs.bSectorNumberReg = hi.lba_low;
+ prev_regs.bCylLowReg = hi.lba_mid;
+ prev_regs.bCylHighReg = hi.lba_high;
+ prev_regs.bDriveHeadReg = hi.device;
+ prev_regs.bCommandReg = hi.command;
+ prev_regs.bReserved = 0;
}
- if (ata_debugmode > 1) {
- pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
- print_ide_regs_io(regs, NULL);
+ // Set data direction
+ int datasize = 0;
+ char * data = 0;
+ switch (in.direction) {
+ case ata_cmd_in::no_data:
+ break;
+ case ata_cmd_in::data_in:
+ datasize = (int)in.size;
+ data = (char *)in.buffer;
+ break;
+ case ata_cmd_in::data_out:
+ datasize = -(int)in.size;
+ data = (char *)in.buffer;
+ break;
+ default:
+ return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
+ (int)in.direction);
}
- return 0;
-}
-/////////////////////////////////////////////////////////////////////////////
-// SMART IOCTL via SCSI MINIPORT ioctl
+ // Try all valid ioctls in the order specified in m_options
+ bool powered_up = false;
+ bool out_regs_set = false;
+ bool id_is_cached = false;
+ const char * options = m_options.c_str();
-// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
-// miniport driver (via SCSI port driver scsiport.sys).
-// It can be used to skip the missing or broken handling of some SMART
-// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
+ for (int i = 0; ; i++) {
+ char opt = options[i];
-static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
-{
- // Select code
- DWORD code = 0; const char * name = 0;
- if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
- code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
- }
- else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
- case ATA_SMART_READ_VALUES:
- code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
- case ATA_SMART_READ_THRESHOLDS:
- code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
- case ATA_SMART_ENABLE:
- code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
- case ATA_SMART_DISABLE:
- code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
- case ATA_SMART_STATUS:
- code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
- case ATA_SMART_AUTOSAVE:
- code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
- //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
- // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
- case ATA_SMART_IMMEDIATE_OFFLINE:
- code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
- case ATA_SMART_AUTO_OFFLINE:
- code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
- case ATA_SMART_READ_LOG_SECTOR:
- code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
- case ATA_SMART_WRITE_LOG_SECTOR:
- code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
- }
- if (!code) {
- errno = ENOSYS;
- return -1;
- }
-
- // Set SRB
- struct {
- SRB_IO_CONTROL srbc;
- union {
- SENDCMDINPARAMS in;
- SENDCMDOUTPARAMS out;
- } params;
- char space[512-1];
- } sb;
- ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
- memset(&sb, 0, sizeof(sb));
-
- unsigned size;
- if (datasize > 0) {
- if (datasize > (int)sizeof(sb.space)+1) {
- errno = EINVAL;
- return -1;
- }
- size = datasize;
- }
- else if (datasize < 0) {
- if (-datasize > (int)sizeof(sb.space)+1) {
- errno = EINVAL;
- return -1;
- }
- size = -datasize;
- memcpy(sb.params.in.bBuffer, data, size);
- }
- else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
- size = sizeof(IDEREGS);
- else
- size = 0;
- sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
- memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
- sb.srbc.Timeout = 60; // seconds
- sb.srbc.ControlCode = code;
- //sb.srbc.ReturnCode = 0;
- sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
- sb.params.in.irDriveRegs = *regs;
- sb.params.in.cBufferSize = size;
-
- // Call miniport ioctl
- size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
- &sb, size, &sb, size, &num_out, NULL)) {
- long err = GetLastError();
- if (ata_debugmode) {
- pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
- print_ide_regs_io(regs, NULL);
+ if (!opt) {
+ if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
+ // Power up reported by GetDevicePowerState() and no ioctl available
+ // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
+ regs.bSectorCountReg = 0xff;
+ out_regs_set = true;
+ break;
+ }
+ // No IOCTL found
+ return set_err(ENOSYS);
}
- errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
- return -1;
- }
+ if (!strchr(valid_options, opt))
+ // Invalid for this command
+ continue;
- // Check result
- if (sb.srbc.ReturnCode) {
- if (ata_debugmode) {
- pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
- print_ide_regs_io(regs, NULL);
- }
- errno = EIO;
- return -1;
- }
+ errno = 0;
+ assert( datasize == 0 || datasize == 512
+ || (datasize == -512 && strchr("am", opt))
+ || (datasize > 512 && opt == 'a'));
+ int rc;
+ switch (opt) {
+ default: assert(0);
+ case 's':
+ // call SMART_GET_VERSION once for each drive
+ if (m_smartver_state > 1) {
+ rc = -1; errno = ENOSYS;
+ break;
+ }
+ if (!m_smartver_state) {
+ assert(m_port == -1);
+ GETVERSIONINPARAMS_EX vers_ex;
+ if (smart_get_version(get_fh(), &vers_ex) < 0) {
+ if (!failuretest_permissive) {
+ m_smartver_state = 2;
+ rc = -1; errno = ENOSYS;
+ break;
+ }
+ failuretest_permissive--;
+ }
+ else {
+ // 3ware RAID if vendor id present
+ m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+ }
- if (sb.params.out.DriverStatus.bDriverError) {
- if (ata_debugmode) {
- pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
- sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
- print_ide_regs_io(regs, NULL);
+ m_smartver_state = 1;
+ }
+ rc = smart_ioctl(get_fh(), ®s, data, datasize, m_port);
+ out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
+ id_is_cached = (m_port < 0); // Not cached by 3ware driver
+ break;
+ case 'm':
+ rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s, data, datasize);
+ id_is_cached = (m_port < 0);
+ break;
+ case 'a':
+ rc = ata_pass_through_ioctl(get_fh(), ®s,
+ (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
+ data, datasize);
+ out_regs_set = true;
+ break;
+ case 'i':
+ rc = ide_pass_through_ioctl(get_fh(), ®s, data, datasize);
+ out_regs_set = true;
+ break;
+ case 'f':
+ if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
+ 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, id);
+ id_is_cached = true;
+ }
+ else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
+ case ATA_SMART_READ_VALUES:
+ rc = storage_predict_failure_ioctl(get_fh(), data);
+ if (rc > 0)
+ rc = 0;
+ break;
+ case ATA_SMART_ENABLE:
+ rc = 0;
+ break;
+ case ATA_SMART_STATUS:
+ rc = storage_predict_failure_ioctl(get_fh());
+ if (rc == 0) {
+ // Good SMART status
+ out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
+ }
+ else if (rc > 0) {
+ // Bad SMART status
+ out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
+ rc = 0;
+ }
+ break;
+ default:
+ errno = ENOSYS; rc = -1;
+ }
+ else {
+ errno = ENOSYS; rc = -1;
+ }
+ break;
+ case '3':
+ rc = ata_via_3ware_miniport_ioctl(get_fh(), ®s, data, datasize, m_port);
+ out_regs_set = true;
+ break;
+ case 'p':
+ assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
+ rc = get_device_power_state(get_fh());
+ if (rc == 0) {
+ // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
+ // spin up the drive => simulate ATA result STANDBY.
+ regs.bSectorCountReg = 0x00;
+ out_regs_set = true;
+ }
+ else if (rc > 0) {
+ // Power up reported by GetDevicePowerState(), but this reflects the actual mode
+ // only if it is selected by the device driver => try a passthrough ioctl to get the
+ // actual mode, if none available simulate ACTIVE/IDLE.
+ powered_up = true;
+ rc = -1; errno = ENOSYS;
+ }
+ break;
}
- errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
- return -1;
- }
-
- if (ata_debugmode > 1) {
- pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
- num_out, sb.params.out.cBufferSize);
- print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
- (const IDEREGS *)(sb.params.out.bBuffer) : 0));
- }
-
- if (datasize > 0)
- memcpy(data, sb.params.out.bBuffer, datasize);
- else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
- memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
-
-static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
-{
- struct {
- SRB_IO_CONTROL srbc;
- IDEREGS regs;
- UCHAR buffer[512];
- } sb;
- ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
+ if (!rc)
+ // Working ioctl found
+ break;
- if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
- errno = EINVAL;
- return -1;
- }
- memset(&sb, 0, sizeof(sb));
- strcpy((char *)sb.srbc.Signature, "<3ware>");
- sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
- sb.srbc.Timeout = 60; // seconds
- sb.srbc.ControlCode = 0xA0000000;
- sb.srbc.ReturnCode = 0;
- sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
- sb.regs = *regs;
- sb.regs.bReserved = port;
+ if (errno != ENOSYS)
+ // Abort on I/O error
+ return set_err(errno);
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
- &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
- long err = GetLastError();
- if (ata_debugmode) {
- pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
- print_ide_regs_io(regs, NULL);
- }
- errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
- return -1;
+ out_regs_set = false;
+ // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
}
- if (sb.srbc.ReturnCode) {
- if (ata_debugmode) {
- pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
- print_ide_regs_io(regs, NULL);
+ // Return IDEREGS if set
+ if (out_regs_set) {
+ ata_out_regs & lo = out.out_regs;
+ lo.error = regs.bFeaturesReg;
+ lo.sector_count = regs.bSectorCountReg;
+ lo.lba_low = regs.bSectorNumberReg;
+ lo.lba_mid = regs.bCylLowReg;
+ lo.lba_high = regs.bCylHighReg;
+ lo.device = regs.bDriveHeadReg;
+ lo.status = regs.bCommandReg;
+ if (in.in_regs.is_48bit_cmd()) {
+ ata_out_regs & hi = out.out_regs.prev;
+ hi.sector_count = prev_regs.bSectorCountReg;
+ hi.lba_low = prev_regs.bSectorNumberReg;
+ hi.lba_mid = prev_regs.bCylLowReg;
+ hi.lba_high = prev_regs.bCylHighReg;
}
- errno = EIO;
- return -1;
- }
-
- // Copy data
- if (datasize > 0)
- memcpy(data, sb.buffer, datasize);
-
- if (ata_debugmode > 1) {
- pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
- print_ide_regs_io(regs, &sb.regs);
- }
- *regs = sb.regs;
-
- return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
-// 3DM/CLI "Rescan Controller" function does not to always update it.
-
-static int update_3ware_devicemap_ioctl(HANDLE hdevice)
-{
- SRB_IO_CONTROL srbc;
- memset(&srbc, 0, sizeof(srbc));
- strcpy((char *)srbc.Signature, "<3ware>");
- srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
- srbc.Timeout = 60; // seconds
- srbc.ControlCode = 0xCC010014;
- srbc.ReturnCode = 0;
- srbc.Length = 0;
-
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
- &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
- long err = GetLastError();
- if (ata_debugmode)
- pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
- errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
- return -1;
- }
- if (srbc.ReturnCode) {
- if (ata_debugmode)
- pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
- errno = EIO;
- return -1;
}
- if (ata_debugmode > 1)
- pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
- return 0;
-}
-
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// Routines for pseudo device /dev/tw_cli/*
-// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
+ if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
+ || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
+ // Update ata_identify_is_cached() result according to ioctl used.
+ m_id_is_cached = id_is_cached;
-// Get clipboard data
+ return true;
+}
-static int get_clipboard(char * data, int datasize)
+// Return true if OS caches the ATA identify sector
+bool win_ata_device::ata_identify_is_cached() const
{
- if (!OpenClipboard(NULL))
- return -1;
- HANDLE h = GetClipboardData(CF_TEXT);
- if (!h) {
- CloseClipboard();
- return 0;
- }
- const void * p = GlobalLock(h);
- int n = GlobalSize(h);
- if (n > datasize)
- n = datasize;
- memcpy(data, p, n);
- GlobalFree(h);
- CloseClipboard();
- return n;
+ return m_id_is_cached;
}
-// Run a command, write stdout to dataout
-// TODO: Combine with daemon_win32.cpp:daemon_spawn()
+//////////////////////////////////////////////////////////////////////
+// csmi_device
-static int run_cmd(const char * cmd, char * dataout, int outsize)
+class csmi_device
+: virtual public /*extends*/ smart_device
{
- // 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;
- }
+public:
+ enum { max_number_of_ports = 32 };
- // 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);
+ /// Get bitmask of used ports
+ unsigned get_ports_used();
- // 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;
-}
+protected:
+ csmi_device()
+ : smart_device(never_called)
+ { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
+ typedef signed char port_2_index_map[max_number_of_ports];
-static const char * findstr(const char * str, const char * sub)
-{
- const char * s = strstr(str, sub);
- return (s ? s+strlen(sub) : "");
-}
+ /// 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);
-static void copy_swapped(unsigned char * dest, const char * src, int destsize)
-{
- int srclen = strcspn(src, "\r\n");
- int i;
- for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
- dest[i] = src[i+1]; dest[i+1] = src[i];
- }
- if (i < destsize-1 && i < srclen)
- dest[i+1] = src[i];
-}
+ /// Get info for selected physical drive
+ const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
+ { return m_phy_ent; }
+ /// Call platform-specific CSMI ioctl
+ virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+ unsigned csmi_bufsiz) = 0;
-// TODO: This is OS independent
+private:
+ CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
+};
-win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "tw_cli", req_type),
- m_ident_valid(false), m_smart_valid(false)
-{
- memset(&m_ident_buf, 0, sizeof(m_ident_buf));
- memset(&m_smart_buf, 0, sizeof(m_smart_buf));
-}
+/////////////////////////////////////////////////////////////////////////////
-bool win_tw_cli_device::is_open() const
+int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p2i)
{
- return (m_ident_valid || m_smart_valid);
-}
+ // 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 -1;
-bool win_tw_cli_device::open()
-{
- m_ident_valid = m_smart_valid = false;
- const char * name = skipdev(get_dev_name());
- // Read tw_cli or 3DM browser output into buffer
- char buffer[4096];
- int size = -1, n1 = -1, n2 = -1;
- if (!strcmp(name, "tw_cli/clip")) { // read clipboard
- size = get_clipboard(buffer, sizeof(buffer));
- }
- else if (!strcmp(name, "tw_cli/stdin")) { // read stdin
- size = fread(buffer, 1, sizeof(buffer), stdin);
- }
- else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
- // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
- char cmd[100];
- 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));
- }
- else {
- return set_err(EINVAL);
+ if (scsi_debugmode > 1) {
+ const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
+ pout("CSMI_SAS_DRIVER_INFO:\n");
+ pout(" Name: \"%.81s\"\n", driver_info.szName);
+ pout(" Description: \"%.81s\"\n", driver_info.szDescription);
+ pout(" Revision: %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
}
- if (ata_debugmode > 1)
- pout("%s: Read %d bytes\n", name, size);
- if (size <= 0)
- return set_err(ENOENT);
- if (size >= (int)sizeof(buffer))
- return set_err(EIO);
+ // Get 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 -1;
- buffer[size] = 0;
- if (ata_debugmode > 1)
- pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
+ phy_info = phy_info_buf.Information;
- // Fake identify sector
- ASSERT_SIZEOF(ata_identify_device, 512);
- ata_identify_device * id = &m_ident_buf;
- memset(id, 0, sizeof(*id));
- copy_swapped(id->model , findstr(buffer, " Model = " ), sizeof(id->model));
- copy_swapped(id->fw_rev , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
- copy_swapped(id->serial_no, findstr(buffer, " Serial = " ), sizeof(id->serial_no));
- unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
- sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
- if (nblocks) {
- id->words047_079[49-47] = 0x0200; // size valid
- id->words047_079[60-47] = (unsigned short)(nblocks ); // secs_16
- id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
+ if (phy_info.bNumberOfPhys > max_number_of_ports) {
+ set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
+ return -1;
}
- id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
- id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
- // Parse smart data hex dump
- const char * s = findstr(buffer, "Drive Smart Data:");
- if (!*s)
- s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
- if (!*s) {
- s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
- if (*s) {
- const char * s1 = findstr(s, "<td class"); // html version
- if (*s1)
- s = s1;
- s += strcspn(s, "\r\n");
- }
- else
- s = buffer; // try raw hex dump without header
- }
- unsigned char * sd = (unsigned char *)&m_smart_buf;
- int i = 0;
- for (;;) {
- unsigned x = ~0; int n = -1;
- if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
- break;
- sd[i] = (unsigned char)x;
- if (!(++i < 512 && n > 0))
- break;
- s += n;
- if (*s == '<') // "<br>"
- s += strcspn(s, "\r\n");
- }
- if (i < 512) {
- if (!id->model[1]) {
- // No useful data found
- char * err = strstr(buffer, "Error:");
- if (!err)
- err = strstr(buffer, "error :");
- if (err && (err = strchr(err, ':'))) {
- // Show tw_cli error message
- err++;
- err[strcspn(err, "\r\n")] = 0;
- return set_err(EIO, "%s", err);
- }
- return set_err(EIO);
- }
- sd = 0;
- }
+ // 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;
- m_ident_valid = true;
- m_smart_valid = !!sd;
- return true;
-}
+ // 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;
+ }
-bool win_tw_cli_device::close()
-{
- m_ident_valid = m_smart_valid = false;
- return 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_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;
-int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
-{
- switch (command) {
- case IDENTIFY:
- if (!m_ident_valid)
- break;
- memcpy(data, &m_ident_buf, 512);
- return 0;
- case READ_VALUES:
- if (!m_smart_valid)
- break;
- memcpy(data, &m_smart_buf, 512);
- return 0;
- case ENABLE:
- case STATUS:
- case STATUS_CHECK: // Fake "good" SMART status
- return 0;
- default:
- break;
- }
- // Arrive here for all unsupported commands
- set_err(ENOSYS);
- return -1;
+ 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, ",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+ b = at.bSASAddress;
+ pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+ }
+ }
+
+ return number_of_ports;
}
+unsigned csmi_device::get_ports_used()
+{
+ CSMI_SAS_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;
-/////////////////////////////////////////////////////////////////////////////
-// IOCTL_STORAGE_QUERY_PROPERTY
+ unsigned ports_used = 0;
+ 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) {
+ case CSMI_SAS_PROTOCOL_SATA:
+ case CSMI_SAS_PROTOCOL_STP:
+ break;
+ default:
+ continue;
+ }
-union STORAGE_DEVICE_DESCRIPTOR_DATA {
- STORAGE_DEVICE_DESCRIPTOR desc;
- char raw[256];
-};
+ ports_used |= (1U << p);
+ }
-// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
-// (This works without admin rights)
+ return ports_used;
+}
-static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+bool csmi_device::select_port(int port)
{
- STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
- memset(data, 0, sizeof(*data));
+ if (!(0 <= port && port < max_number_of_ports))
+ return set_err(EINVAL, "Invalid port number %d", port);
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
- &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
- if (ata_debugmode > 1 || scsi_debugmode > 1)
- pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
- errno = ENOSYS;
- return -1;
+ CSMI_SAS_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;
+
+ int port_index = p2i[port];
+ if (port_index < 0) {
+ 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,
+ number_of_ports);
}
- if (ata_debugmode > 1 || scsi_debugmode > 1) {
- pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
- " Vendor: \"%s\"\n"
- " Product: \"%s\"\n"
- " Revision: \"%s\"\n"
- " Removable: %s\n"
- " BusType: 0x%02x\n",
- (data->desc.VendorIdOffset ? data->raw+data->desc.VendorIdOffset : "(null)"),
- (data->desc.ProductIdOffset ? data->raw+data->desc.ProductIdOffset : "(null)"),
- (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
- (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
- );
+ 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);
}
- return 0;
+
+ m_phy_ent = phy_ent;
+ return true;
}
-/////////////////////////////////////////////////////////////////////////////
-// IOCTL_STORAGE_PREDICT_FAILURE
+//////////////////////////////////////////////////////////////////////
+// csmi_ata_device
-// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
-// or -1 on error, opionally return VendorSpecific data.
-// (This works without admin rights)
+class csmi_ata_device
+: virtual public /*extends*/ csmi_device,
+ virtual public /*implements*/ ata_device
+{
+public:
+ virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
-static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
+protected:
+ csmi_ata_device()
+ : smart_device(never_called) { }
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
{
- STORAGE_PREDICT_FAILURE pred;
- memset(&pred, 0, sizeof(pred));
+ if (!ata_cmd_is_supported(in,
+ ata_device::supports_data_out |
+ ata_device::supports_output_regs |
+ ata_device::supports_multi_sector |
+ ata_device::supports_48bit,
+ "CSMI")
+ )
+ return false;
- DWORD num_out;
- if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
- 0, 0, &pred, sizeof(pred), &num_out, NULL)) {
- if (ata_debugmode > 1)
- pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
- errno = ENOSYS;
- return -1;
+ // Create buffer with appropriate size
+ raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
+ CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
+
+ // Set addresses from Phy info
+ CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
+ const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
+ pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
+ pthru.bPortIdentifier = phy_ent.bPortIdentifier;
+ memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
+ sizeof(pthru.bDestinationSASAddress));
+ pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
+
+ // Set transfer mode
+ switch (in.direction) {
+ case ata_cmd_in::no_data:
+ pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
+ break;
+ case ata_cmd_in::data_in:
+ pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
+ pthru.uDataLength = in.size;
+ break;
+ case ata_cmd_in::data_out:
+ pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
+ pthru.uDataLength = in.size;
+ memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
+ break;
+ default:
+ return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
+ (int)in.direction);
}
- if (ata_debugmode > 1) {
- pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
- " PredictFailure: 0x%08lx\n"
- " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
- pred.PredictFailure,
- pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
- pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
- );
+ // Set host-to-device FIS
+ {
+ unsigned char * fis = pthru.bCommandFIS;
+ const ata_in_regs & lo = in.in_regs;
+ const ata_in_regs & hi = in.in_regs.prev;
+ fis[ 0] = 0x27; // Type: host-to-device FIS
+ fis[ 1] = 0x80; // Bit7: Update command register
+ fis[ 2] = lo.command;
+ fis[ 3] = lo.features;
+ fis[ 4] = lo.lba_low;
+ fis[ 5] = lo.lba_mid;
+ fis[ 6] = lo.lba_high;
+ fis[ 7] = lo.device;
+ fis[ 8] = hi.lba_low;
+ fis[ 9] = hi.lba_mid;
+ fis[10] = hi.lba_high;
+ fis[11] = hi.features;
+ fis[12] = lo.sector_count;
+ fis[13] = hi.sector_count;
}
- if (data)
- memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
- return (!pred.PredictFailure ? 0 : 1);
-}
+ // Call ioctl
+ if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
+ return false;
+ }
-/////////////////////////////////////////////////////////////////////////////
+ // Get device-to-host FIS
+ {
+ const unsigned char * fis = pthru_buf->Status.bStatusFIS;
+ ata_out_regs & lo = out.out_regs;
+ lo.status = fis[ 2];
+ lo.error = fis[ 3];
+ lo.lba_low = fis[ 4];
+ lo.lba_mid = fis[ 5];
+ lo.lba_high = fis[ 6];
+ lo.device = fis[ 7];
+ lo.sector_count = fis[12];
+ if (in.in_regs.is_48bit_cmd()) {
+ ata_out_regs & hi = out.out_regs.prev;
+ hi.lba_low = fis[ 8];
+ hi.lba_mid = fis[ 9];
+ hi.lba_high = fis[10];
+ hi.sector_count = fis[13];
+ }
+ }
+
+ // Get data
+ if (in.direction == ata_cmd_in::data_in)
+ // TODO: Check ptru_buf->Status.uDataBytes
+ memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
-// 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;
return true;
}
-// get DEV_* for open handle
-static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
+
+//////////////////////////////////////////////////////////////////////
+// win_csmi_device
+
+class win_csmi_device
+: public /*implements*/ csmi_ata_device
{
- // Get BusType from device descriptor
- STORAGE_DEVICE_DESCRIPTOR_DATA data;
- if (storage_query_property_ioctl(hdevice, &data))
- return DEV_UNKNOWN;
+public:
+ win_csmi_device(smart_interface * intf, const char * dev_name,
+ const char * req_type);
- // Newer BusType* values are missing in older includes
- switch ((int)data.desc.BusType) {
- case BusTypeAta:
- case 0x0b: // BusTypeSata
- if (ata_version_ex)
- memset(ata_version_ex, 0, sizeof(*ata_version_ex));
- return DEV_ATA;
+ virtual ~win_csmi_device() throw();
- case BusTypeScsi:
- case BusTypeRAID:
- // 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;
+ virtual bool open();
- case 0x09: // BusTypeiScsi
- case 0x0a: // BusTypeSas
- return DEV_SCSI;
+ virtual bool close();
- case BusTypeUsb:
- return DEV_USB;
+ virtual bool is_open() const;
- default:
- return DEV_UNKNOWN;
- }
- /*NOTREACHED*/
-}
+ bool open_scsi();
-// get DEV_* for device path
-static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
-{
- bool admin = true;
- HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
- if (h == INVALID_HANDLE_VALUE) {
- admin = false;
- h = CreateFileA(path, 0,
- FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
- if (h == INVALID_HANDLE_VALUE)
- return DEV_UNKNOWN;
- }
- if (ata_debugmode > 1 || scsi_debugmode > 1)
- pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
- win_dev_type type = get_controller_type(h, admin, ata_version_ex);
- CloseHandle(h);
- return type;
-}
+protected:
+ virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+ unsigned csmi_bufsiz);
+
+private:
+ HANDLE m_fh; ///< Controller device handle
+ int m_port; ///< Port number
+};
-// get DEV_* for physical drive number
-static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
+
+//////////////////////////////////////////////////////////////////////
+
+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_port(-1)
{
- char path[30];
- snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
- return get_controller_type(path, ata_version_ex);
}
-static win_dev_type get_phy_drive_type(int drive)
+win_csmi_device::~win_csmi_device() throw()
{
- return get_phy_drive_type(drive, 0);
+ if (m_fh != INVALID_HANDLE_VALUE)
+ CloseHandle(m_fh);
}
-// get DEV_* for logical drive number
-static win_dev_type get_log_drive_type(int drive)
+bool win_csmi_device::is_open() const
{
- char path[30];
- snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
- return get_controller_type(path);
+ return (m_fh != INVALID_HANDLE_VALUE);
}
-// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
-static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
+bool win_csmi_device::close()
{
- STORAGE_DEVICE_DESCRIPTOR_DATA data;
- if (storage_query_property_ioctl(hdevice, &data))
- return -1;
+ if (m_fh == INVALID_HANDLE_VALUE)
+ return true;
+ BOOL rc = CloseHandle(m_fh);
+ m_fh = INVALID_HANDLE_VALUE;
+ return !!rc;
+}
- memset(id, 0, sizeof(*id));
- // Some drivers split ATA model string into VendorId and ProductId,
- // others return it as ProductId only.
- char model[sizeof(id->model) + 1] = "";
+bool win_csmi_device::open_scsi()
+{
+ // Parse name
+ 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, &port, &nc) >= 0
+ && nc == (int)strlen(name) && contr_no <= 9 && port < 32) )
+ return set_err(EINVAL);
- 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];
- }
+ // Open controller handle
+ char devpath[30];
+ snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
- if (data.desc.ProductIdOffset) {
- while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
- i--;
- // Ignore VendorId "ATA"
- if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
- i = 0;
- for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
- model[i] = data.raw[data.desc.ProductIdOffset+j];
- }
+ HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
- while (i > 0 && model[i-1] == ' ')
- i--;
- model[i] = 0;
- copy_swapped(id->model, model, sizeof(id->model));
+ if (h == INVALID_HANDLE_VALUE) {
+ long err = GetLastError();
+ 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 (data.desc.ProductRevisionOffset)
- copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
+ if (scsi_debugmode > 1)
+ pout(" %s: successfully opened\n", devpath);
- id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
- id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
- return 0;
+ m_fh = h;
+ m_port = port;
+ return true;
}
-/////////////////////////////////////////////////////////////////////////////
-// 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)
+bool win_csmi_device::open()
{
- bool debug = (scsi_debugmode > 1);
+ if (!open_scsi())
+ return false;
- wbem_services ws;
- if (!ws.connect()) {
- if (debug)
- pout("WMI connect failed\n");
+ // Get Phy info for this drive
+ if (!select_port(m_port)) {
+ close();
return false;
}
- // Get device name
- wbem_object wo;
- if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
- return false;
+ return true;
+}
- 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;
- if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
- return false;
+bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
+ unsigned csmi_bufsiz)
+{
+ // Determine signature
+ const char * sig;
+ switch (code) {
+ case CC_CSMI_SAS_GET_DRIVER_INFO:
+ sig = CSMI_ALL_SIGNATURE; break;
+ case CC_CSMI_SAS_GET_PHY_INFO:
+ case CC_CSMI_SAS_STP_PASSTHRU:
+ sig = CSMI_SAS_SIGNATURE; break;
+ default:
+ return set_err(ENOSYS, "Unknown CSMI code=%u", code);
+ }
- unsigned short usb_venid = 0, prev_usb_venid = 0;
- unsigned short usb_proid = 0, prev_usb_proid = 0;
- std::string prev_usb_ant;
- std::string prev_ant, ant, dep;
+ // Set header
+ csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
+ strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
+ csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
+ csmi_buffer->ControlCode = code;
+ csmi_buffer->ReturnCode = 0;
+ csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
- const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
+ // Call function
+ DWORD num_out = 0;
+ if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
+ csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
+ long err = GetLastError();
+ if (scsi_debugmode)
+ pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
+ if ( err == ERROR_INVALID_FUNCTION
+ || err == ERROR_NOT_SUPPORTED
+ || err == ERROR_DEV_NOT_EXIST)
+ return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
+ else
+ return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
+ }
- while (we.next(wo)) {
- prev_ant = ant;
- // Find next 'USB_CONTROLLER, DEVICE' pair
- ant = wo.get_str("Antecedent");
- dep = wo.get_str("Dependent");
+ // Check result
+ if (csmi_buffer->ReturnCode) {
+ if (scsi_debugmode) {
+ pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
+ code, (unsigned)csmi_buffer->ReturnCode);
+ }
+ return set_err(EIO, "CSMI(%u) failed with ReturnCode=%u", code, (unsigned)csmi_buffer->ReturnCode);
+ }
- if (debug && ant != prev_ant)
- pout(" %s:\n", ant.c_str());
+ if (scsi_debugmode > 1)
+ pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out);
- // Extract DeviceID
- regmatch_t match[2];
- if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
- if (debug)
- pout(" | (\"%s\")\n", dep.c_str());
- continue;
- }
+ return true;
+}
- std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
- if (str_starts_with(devid, "USB\\\\VID_")) {
- // USB bridge entry, save CONTROLLER, ID
- int nc = -1;
- if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
- &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
- prev_usb_venid = prev_usb_proid = 0;
- }
- 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
- if (debug)
- pout(" +--> \"%s\"\n", devid.c_str());
+//////////////////////////////////////////////////////////////////////
+// win_tw_cli_device
- // Retrieve name
- wbem_object wo2;
- if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
- continue;
- std::string name2 = wo2.get_str("Name");
+// Routines for pseudo device /dev/tw_cli/*
+// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
+// TODO: This is OS independent
- // Continue if not name of physical disk drive
- if (name2 != name) {
- if (debug)
- pout(" +---> (\"%s\")\n", name2.c_str());
- continue;
- }
+class win_tw_cli_device
+: public /*implements*/ ata_device_with_command_set
+{
+public:
+ win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
- // Fail if previos USB bridge is associated to other controller or ID is unknown
- if (!(ant == prev_usb_ant && prev_usb_venid)) {
- if (debug)
- pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
- return false;
- }
+ virtual bool is_open() const;
- // Handle multiple devices with same name
- if (usb_venid) {
- // Fail if multiple devices with same name have different USB bridge types
- if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
- if (debug)
- pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
- return false;
- }
- }
+ virtual bool open();
- // Found
- usb_venid = prev_usb_venid;
- usb_proid = prev_usb_proid;
- if (debug)
- pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
+ virtual bool close();
- // Continue to check for duplicate names ...
- }
- else {
- if (debug)
- pout(" | \"%s\"\n", devid.c_str());
- }
- }
+protected:
+ virtual int ata_command_interface(smart_command_set command, int select, char * data);
- if (!usb_venid)
- return false;
+private:
+ bool m_ident_valid, m_smart_valid;
+ ata_identify_device m_ident_buf;
+ ata_smart_values m_smart_buf;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "tw_cli", req_type),
+ m_ident_valid(false), m_smart_valid(false)
+{
+ memset(&m_ident_buf, 0, sizeof(m_ident_buf));
+ memset(&m_smart_buf, 0, sizeof(m_smart_buf));
+}
- vendor_id = usb_venid;
- product_id = usb_proid;
- return true;
+bool win_tw_cli_device::is_open() const
+{
+ return (m_ident_valid || m_smart_valid);
}
-/////////////////////////////////////////////////////////////////////////////
-
-// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
-// returns: 1=active, 0=standby, -1=error
-// (This would also work for SCSI drives)
+// Get clipboard data
-static int get_device_power_state(HANDLE hdevice)
+static int get_clipboard(char * data, int datasize)
{
- static bool unsupported = false;
- if (unsupported) {
- errno = ENOSYS;
+ if (!OpenClipboard(NULL))
return -1;
+ HANDLE h = GetClipboardData(CF_TEXT);
+ if (!h) {
+ CloseClipboard();
+ return 0;
}
+ const void * p = GlobalLock(h);
+ int n = GlobalSize(h);
+ if (n > datasize)
+ n = datasize;
+ memcpy(data, p, n);
+ GlobalFree(h);
+ CloseClipboard();
+ return n;
+}
-#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;
+static const char * findstr(const char * str, const char * sub)
+{
+ const char * s = strstr(str, sub);
+ return (s ? s+strlen(sub) : "");
+}
+
+
+bool win_tw_cli_device::open()
+{
+ m_ident_valid = m_smart_valid = false;
+ const char * name = skipdev(get_dev_name());
+ // Read tw_cli or 3DM browser output into buffer
+ char buffer[4096];
+ int size = -1, n1 = -1, n2 = -1;
+ if (!strcmp(name, "tw_cli/clip")) { // read clipboard
+ size = get_clipboard(buffer, sizeof(buffer));
+ }
+ else if (!strcmp(name, "tw_cli/stdin")) { // read stdin
+ size = fread(buffer, 1, sizeof(buffer), stdin);
+ }
+ else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
+ // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
+ char cmd[100];
+ snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
+ if (ata_debugmode > 1)
+ pout("%s: Run: \"%s\"\n", name, cmd);
+ FILE * f = popen(cmd, "rb");
+ if (f) {
+ size = fread(buffer, 1, sizeof(buffer), f);
+ pclose(f);
}
-#ifdef __CYGWIN__
- kernel_dll_pid = GetCurrentProcessId();
-#endif
}
-
- BOOL state = TRUE;
- if (!GetDevicePowerState_p(hdevice, &state)) {
- long err = GetLastError();
- if (ata_debugmode)
- pout(" GetDevicePowerState() failed, Error=%ld\n", err);
- errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
- // TODO: This may not work as expected on transient errors,
- // because smartd interprets -1 as SLEEP mode regardless of errno.
- return -1;
+ else {
+ return set_err(EINVAL);
}
if (ata_debugmode > 1)
- pout(" GetDevicePowerState() succeeded, state=%d\n", state);
- return state;
-}
-
+ pout("%s: Read %d bytes\n", name, size);
+ if (size <= 0)
+ return set_err(ENOENT);
+ if (size >= (int)sizeof(buffer))
+ return set_err(EIO);
-/////////////////////////////////////////////////////////////////////////////
+ buffer[size] = 0;
+ if (ata_debugmode > 1)
+ pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
-#if WIN9X_SUPPORT
-// Print SMARTVSD error message, return errno
+ // Fake identify sector
+ ASSERT_SIZEOF(ata_identify_device, 512);
+ ata_identify_device * id = &m_ident_buf;
+ memset(id, 0, sizeof(*id));
+ copy_swapped(id->model , findstr(buffer, " Model = " ), sizeof(id->model));
+ copy_swapped(id->fw_rev , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
+ copy_swapped(id->serial_no, findstr(buffer, " Serial = " ), sizeof(id->serial_no));
+ unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
+ sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
+ if (nblocks) {
+ id->words047_079[49-47] = 0x0200; // size valid
+ id->words047_079[60-47] = (unsigned short)(nblocks ); // secs_16
+ id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
+ }
+ id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+ id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
-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;
+ // Parse smart data hex dump
+ const char * s = findstr(buffer, "Drive Smart Data:");
+ if (!*s)
+ s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
+ if (!*s) {
+ s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
+ if (*s) {
+ const char * s1 = findstr(s, "<td class"); // html version
+ if (*s1)
+ s = s1;
+ s += strcspn(s, "\r\n");
}
+ else
+ s = buffer; // try raw hex dump without header
}
- 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);
+ unsigned char * sd = (unsigned char *)&m_smart_buf;
+ int i = 0;
+ for (;;) {
+ unsigned x = ~0; int n = -1;
+ if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
+ break;
+ sd[i] = (unsigned char)x;
+ if (!(++i < 512 && n > 0))
+ break;
+ s += n;
+ if (*s == '<') // "<br>"
+ s += strcspn(s, "\r\n");
+ }
+ if (i < 512) {
+ if (!id->model[1]) {
+ // No useful data found
+ char * err = strstr(buffer, "Error:");
+ if (!err)
+ err = strstr(buffer, "error :");
+ if (err && (err = strchr(err, ':'))) {
+ // Show tw_cli error message
+ err++;
+ err[strcspn(err, "\r\n")] = 0;
+ return set_err(EIO, "%s", err);
+ }
+ return set_err(EIO);
}
- return ENOSYS;
+ sd = 0;
}
-}
-#endif // WIN9X_SUPPORT
+ m_ident_valid = true;
+ m_smart_valid = !!sd;
+ return true;
+}
-// Get default ATA device options
-static const char * ata_get_def_options()
+bool win_tw_cli_device::close()
{
- DWORD ver = GetVersion();
- if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME
- 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_*
+ m_ident_valid = m_smart_valid = false;
+ return true;
}
-// Common routines for devices with HANDLEs
-
-win_smart_device::~win_smart_device() throw()
+int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
{
- if (m_fh != INVALID_HANDLE_VALUE)
- ::CloseHandle(m_fh);
+ switch (command) {
+ case IDENTIFY:
+ if (!m_ident_valid)
+ break;
+ memcpy(data, &m_ident_buf, 512);
+ return 0;
+ case READ_VALUES:
+ if (!m_smart_valid)
+ break;
+ memcpy(data, &m_smart_buf, 512);
+ return 0;
+ case ENABLE:
+ case STATUS:
+ case STATUS_CHECK: // Fake "good" SMART status
+ return 0;
+ default:
+ break;
+ }
+ // Arrive here for all unsupported commands
+ set_err(ENOSYS);
+ return -1;
}
-bool win_smart_device::is_open() const
-{
- return (m_fh != INVALID_HANDLE_VALUE);
-}
-bool win_smart_device::close()
+/////////////////////////////////////////////////////////////////////////////
+// win_scsi_device
+// SPT Interface (for SCSI devices and ATA devices behind SATLs)
+
+class win_scsi_device
+: public /*implements*/ scsi_device,
+ virtual public /*extends*/ win_smart_device
{
- if (m_fh == INVALID_HANDLE_VALUE)
- return true;
- BOOL rc = ::CloseHandle(m_fh);
- m_fh = INVALID_HANDLE_VALUE;
- return !!rc;
-}
+public:
+ win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
-// ATA
+ virtual bool open();
-win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "ata", req_type),
- m_usr_options(false),
- m_admin(false),
- m_id_is_cached(false),
- m_is_3ware(false),
- m_drive(0),
- m_port(-1),
- m_smartver_state(0)
-{
-}
+ virtual bool scsi_pass_through(scsi_cmnd_io * iop);
-win_ata_device::~win_ata_device() throw()
-{
-}
+private:
+ bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
+};
-// Open ATA device
+/////////////////////////////////////////////////////////////////////////////
-bool win_ata_device::open()
+win_scsi_device::win_scsi_device(smart_interface * intf,
+ const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type)
+{
+}
+
+bool win_scsi_device::open()
{
const char * name = skipdev(get_dev_name()); int len = strlen(name);
- // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
- char drive[1+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
- && ((n1 == len && !options[0]) || n2 == len) ) {
- return open(drive[0] - 'a', -1, options, -1);
- }
- // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
- drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
- unsigned port = ~0;
- if ( sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
- && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) {
- return open(drive[0] - 'a', -1, options, port);
+ // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
+ char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int 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)) ) {
+ return open(sdxy_to_phydrive(drive), -1, -1, sub_addr);
}
// pd<m>,N => Physical drive <m>, RAID port N
- int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
- if ( sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
- && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
- return open(phydrive, -1, "", (int)port);
+ int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
+ if ( sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
+ && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
+ return open(pd_num, -1, -1, sub_addr);
+ }
+ // [a-zA-Z]: => Physical drive behind logical drive 0-25
+ int logdrive = drive_letter(name);
+ if (logdrive >= 0) {
+ return open(-1, logdrive, -1, -1);
+ }
+ // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
+ int tape_num = -1; n1 = -1;
+ if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+ return open(-1, -1, tape_num, -1);
+ }
+ tape_num = -1; n1 = -1;
+ if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+ return open(-1, -1, tape_num, -1);
}
- // [a-zA-Z]: => Physical drive behind logical drive 0-25
- int logdrive = drive_letter(name);
- if (logdrive >= 0) {
- return open(-1, logdrive, "", -1);
+ // tape<m> => tape drive <m>
+ tape_num = -1; n1 = -1;
+ if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+ return open(-1, -1, tape_num, -1);
}
return set_err(EINVAL);
}
-
-bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
+bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
{
- // path depends on Windows Version
- char devpath[30];
- if (win9x && 0 <= phydrive && phydrive <= 7)
- // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
- strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
- 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);
- else
- return set_err(ENOENT);
+ char b[128];
+ b[sizeof(b) - 1] = '\0';
+ if (pd_num >= 0)
+ snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
+ else if (ld_num >= 0)
+ snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
+ else if (tape_num >= 0)
+ snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
+ else {
+ set_err(EINVAL);
+ return false;
+ }
// Open device
- HANDLE h = INVALID_HANDLE_VALUE;
- if (win9x || !(*options && !options[strspn(options, "fp")])) {
- // Open with admin rights
- m_admin = true;
- h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, 0, 0);
- }
- if (!win9x && h == INVALID_HANDLE_VALUE) {
- // Open without admin rights
- m_admin = false;
- h = CreateFileA(devpath, 0,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, 0, 0);
- }
+ HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
- long err = GetLastError();
-#if WIN9X_SUPPORT
- if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND)
- smartvsd_error();
-#endif
- 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);
+ set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError());
return false;
}
set_fh(h);
+ return true;
+}
- // Warn once if admin rights are missing
- if (!m_admin) {
- static bool noadmin_warning = false;
- if (!noadmin_warning) {
- pout("Warning: Limited functionality due to missing admin rights\n");
- noadmin_warning = true;
- }
- }
- if (ata_debugmode > 1)
- pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
+typedef struct {
+ SCSI_PASS_THROUGH_DIRECT spt;
+ ULONG Filler;
+ UCHAR ucSenseBuf[64];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
- m_usr_options = false;
- if (*options) {
- // Save user options
- m_options = options; m_usr_options = true;
- }
- else if (port >= 0)
- // RAID: SMART_* and SCSI_MINIPORT
- m_options = "s3";
- else {
- // Set default options according to Windows version
- static const char * def_options = ata_get_def_options();
- m_options = def_options;
- }
- // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
- m_drive = 0; m_port = port;
- if (!win9x && port < 0)
- return true;
+// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
+// Used if DataTransferLength not supported by *_DIRECT.
+static long scsi_pass_through_indirect(HANDLE h,
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
+{
+ struct SCSI_PASS_THROUGH_WITH_BUFFERS {
+ SCSI_PASS_THROUGH spt;
+ ULONG Filler;
+ UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
+ UCHAR ucDataBuf[512];
+ };
- // Win9X/ME: Get drive map
- // RAID: Get port map
- GETVERSIONINPARAMS_EX vers_ex;
- int devmap = smart_get_version(h, &vers_ex);
+ SCSI_PASS_THROUGH_WITH_BUFFERS sb;
+ memset(&sb, 0, sizeof(sb));
- // 3ware RAID if vendor id present
- m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
+ // DATA_OUT not implemented yet
+ if (!( sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
+ && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
+ return ERROR_INVALID_PARAMETER;
- unsigned long portmap = 0;
- if (port >= 0 && devmap >= 0) {
- // 3ware RAID: check vendor id
- if (!m_is_3ware) {
- pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
- "This is no 3ware 9000 controller or driver has no SMART support.\n",
- vers_ex.wIdentifier);
- devmap = -1;
+ sb.spt.Length = sizeof(sb.spt);
+ sb.spt.CdbLength = sbd->spt.CdbLength;
+ memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
+ sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+ sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+ sb.spt.DataIn = sbd->spt.DataIn;
+ sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
+ sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+ sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
+
+ DWORD num_out;
+ if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
+ &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+ return GetLastError();
+
+ sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
+ if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
+ memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
+
+ sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
+ if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
+ memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
+ return 0;
+}
+
+
+// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
+bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
+{
+ int report = scsi_debugmode; // TODO
+
+ 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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
}
else
- portmap = vers_ex.dwDeviceMapEx;
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+ pout("%s", buff);
}
- if (devmap < 0) {
- pout("%s: ATA driver has no SMART support\n", devpath);
- if (!is_permissive()) {
- close();
- return set_err(ENOSYS);
- }
- devmap = 0x0f;
+
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+ if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+ set_err(EINVAL, "cmnd_len too large");
+ return false;
}
- m_smartver_state = 1;
- if (port >= 0) {
- // 3ware RAID: update devicemap first
+ memset(&sb, 0, sizeof(sb));
+ sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+ sb.spt.CdbLength = iop->cmnd_len;
+ memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+ sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+ sb.spt.SenseInfoOffset =
+ offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+ sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
- 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 (!is_permissive()) {
- close();
- return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
- }
- }
- return true;
+ bool direct = true;
+ switch (iop->dxfer_dir) {
+ case DXFER_NONE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ break;
+ case DXFER_FROM_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+ // transfers (needed for SMART STATUS check of JMicron USB bridges)
+ if (sb.spt.DataTransferLength == 1)
+ direct = false;
+ break;
+ case DXFER_TO_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ break;
+ default:
+ set_err(EINVAL, "bad dxfer_dir");
+ return false;
}
- // 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);
- }
+ long err = 0;
+ if (direct) {
+ DWORD num_out;
+ if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+ err = GetLastError();
}
- // Drive number must be passed to ioctl
- m_drive = (phydrive & 0x3);
- return true;
-}
+ else
+ err = scsi_pass_through_indirect(get_fh(), &sb);
+ if (err)
+ return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
+ "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
+ (direct ? "_DIRECT" : ""), err);
-#if WIN9X_SUPPORT
+ iop->scsi_status = sb.spt.ScsiStatus;
+ if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+ int slen = sb.ucSenseBuf[7] + 8;
-// Scan for ATA drives on Win9x/ME
+ if (slen > (int)sizeof(sb.ucSenseBuf))
+ slen = sizeof(sb.ucSenseBuf);
+ if (slen > (int)iop->max_sense_len)
+ slen = iop->max_sense_len;
+ memcpy(iop->sensep, sb.ucSenseBuf, 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;
-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
- }
+ if (iop->dxfer_len > sb.spt.DataTransferLength)
+ iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+ else
+ iop->resid = 0;
- // 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") );
+ 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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
}
return true;
}
-#endif // WIN9X_SUPPORT
-
/////////////////////////////////////////////////////////////////////////////
+/// Areca RAID support
-// Interface to ATA devices
-bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+// TODO: combine with above scsi_pass_through_direct()
+static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
{
- // No multi-sector support for now, see above
- // warning about IOCTL_ATA_PASS_THROUGH
- if (!ata_cmd_is_ok(in,
- true, // data_out_support
- false, // !multi_sector_support
- true) // ata_48bit_support
- )
- return false;
+ int report = scsi_debugmode; // TODO
- // 3ware RAID: SMART DISABLE without port number disables SMART functions
- if ( m_is_3ware && m_port < 0
- && in.in_regs.command == ATA_SMART_CMD
- && in.in_regs.features == ATA_SMART_DISABLE)
- return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
+ if (report > 0) {
+ int k, j;
+ const unsigned char * ucp = iop->cmnd;
+ const char * np;
+ char buff[256];
+ const int sz = (int)sizeof(buff);
- // Determine ioctl functions valid for this ATA cmd
- const char * valid_options = 0;
+ 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;
- switch (in.in_regs.command) {
- case ATA_IDENTIFY_DEVICE:
- case ATA_IDENTIFY_PACKET_DEVICE:
- // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
- // and SCSI_MINIPORT_* if requested by user
- valid_options = (m_usr_options ? "saicmf" : "saicf");
- break;
+ 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);
+ }
- case ATA_CHECK_POWER_MODE:
- // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
- valid_options = "pai3";
- break;
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+ if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+ return EINVAL;
+ }
- case ATA_SMART_CMD:
- switch (in.in_regs.features) {
- case ATA_SMART_READ_VALUES:
- case ATA_SMART_READ_THRESHOLDS:
- case ATA_SMART_AUTOSAVE:
- case ATA_SMART_ENABLE:
- case ATA_SMART_DISABLE:
- case ATA_SMART_AUTO_OFFLINE:
- // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
- // and SCSI_MINIPORT_* if requested by user
- valid_options = (m_usr_options ? "saicmf" : "saicf");
- break;
+ memset(&sb, 0, sizeof(sb));
+ sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+ //sb.spt.PathId = 0;
+ sb.spt.TargetId = targetid;
+ //sb.spt.Lun = 0;
+ sb.spt.CdbLength = iop->cmnd_len;
+ memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+ sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+ sb.spt.SenseInfoOffset =
+ offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+ sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
- case ATA_SMART_IMMEDIATE_OFFLINE:
- // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
- valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ?
- "saicm3" : "aicm3");
- break;
+ bool direct = true;
+ switch (iop->dxfer_dir) {
+ case DXFER_NONE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ break;
+ case DXFER_FROM_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+ // transfers (needed for SMART STATUS check of JMicron USB bridges)
+ if (sb.spt.DataTransferLength == 1)
+ direct = false;
+ break;
+ case DXFER_TO_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ break;
+ default:
+ return EINVAL;
+ }
- case ATA_SMART_READ_LOG_SECTOR:
- // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
- // Try SCSI_MINIPORT also to skip buggy class driver
- // SMART functions do not support multi sector I/O.
- if (in.size == 512)
- valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3");
- else
- valid_options = "a";
- break;
+ long err = 0;
+ if (direct) {
+ DWORD num_out;
+ if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+ err = GetLastError();
+ }
+ else
+ err = scsi_pass_through_indirect(fd, &sb);
- case ATA_SMART_WRITE_LOG_SECTOR:
- // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
- // but SCSI_MINIPORT_* only if requested by user and single sector.
- valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
- break;
+ if (err)
+ {
+ return err;
+ }
- case ATA_SMART_STATUS:
- // May require lba_mid,lba_high register return
- if (in.out_needed.is_set())
- valid_options = (m_usr_options ? "saimf" : "saif");
- else
- valid_options = (m_usr_options ? "saicmf" : "saicf");
- break;
+ iop->scsi_status = sb.spt.ScsiStatus;
+ if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+ int slen = sb.ucSenseBuf[7] + 8;
- default:
- // Unknown SMART command, handle below
- break;
+ if (slen > (int)sizeof(sb.ucSenseBuf))
+ slen = sizeof(sb.ucSenseBuf);
+ if (slen > (int)iop->max_sense_len)
+ slen = iop->max_sense_len;
+ memcpy(iop->sensep, sb.ucSenseBuf, slen);
+ iop->resp_sense_len = slen;
+ if (report) {
+ if (report > 1) {
+ pout(" >>> Sense buffer, len=%d:\n", slen);
+ dStrHex(iop->sensep, slen , 1);
}
- break;
+ 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;
- default:
- // Other ATA command, handle below
- break;
- }
+ if (iop->dxfer_len > sb.spt.DataTransferLength)
+ iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+ else
+ iop->resid = 0;
- if (!valid_options) {
- // No special ATA command found above, select a generic pass through ioctl.
- if (!( in.direction == ata_cmd_in::no_data
- || (in.direction == ata_cmd_in::data_in && in.size == 512))
- || in.in_regs.is_48bit_cmd() )
- // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
- valid_options = "a";
- else if (in.out_needed.is_set())
- // Need output registers: ATA/IDE_PASS_THROUGH
- valid_options = "ai";
- else
- valid_options = "aic";
+ 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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
}
- if (!m_admin) {
- // Restrict to IOCTL_STORAGE_*
- if (strchr(valid_options, 'f'))
- valid_options = "f";
- else if (strchr(valid_options, 'p'))
- valid_options = "p";
- else
- return set_err(ENOSYS, "Function requires admin rights");
- }
+ return 0;
+}
- // Set IDEREGS
- IDEREGS regs, prev_regs;
- {
- const ata_in_regs & lo = in.in_regs;
- regs.bFeaturesReg = lo.features;
- regs.bSectorCountReg = lo.sector_count;
- regs.bSectorNumberReg = lo.lba_low;
- regs.bCylLowReg = lo.lba_mid;
- regs.bCylHighReg = lo.lba_high;
- regs.bDriveHeadReg = lo.device;
- regs.bCommandReg = lo.command;
- regs.bReserved = 0;
- }
- if (in.in_regs.is_48bit_cmd()) {
- const ata_in_regs & hi = in.in_regs.prev;
- prev_regs.bFeaturesReg = hi.features;
- prev_regs.bSectorCountReg = hi.sector_count;
- prev_regs.bSectorNumberReg = hi.lba_low;
- prev_regs.bCylLowReg = hi.lba_mid;
- prev_regs.bCylHighReg = hi.lba_high;
- prev_regs.bDriveHeadReg = hi.device;
- prev_regs.bCommandReg = hi.command;
- prev_regs.bReserved = 0;
- }
- // Set data direction
- int datasize = 0;
- char * data = 0;
- switch (in.direction) {
- case ata_cmd_in::no_data:
- break;
- case ata_cmd_in::data_in:
- datasize = (int)in.size;
- data = (char *)in.buffer;
- break;
- case ata_cmd_in::data_out:
- datasize = -(int)in.size;
- data = (char *)in.buffer;
- break;
- default:
- return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
- (int)in.direction);
- }
+/////////////////////////////////////////////////////////////////////////////
+// win_areca_scsi_device
+// SAS(SCSI) device behind Areca RAID Controller
+
+class win_areca_scsi_device
+: public /*implements*/ areca_scsi_device,
+ public /*extends*/ win_smart_device
+{
+public:
+ win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
+ 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);
+
+private:
+ HANDLE m_mutex;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+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);
+}
- // Try all valid ioctls in the order specified in m_options
- bool powered_up = false;
- bool out_regs_set = false;
- bool id_is_cached = false;
- const char * options = m_options.c_str();
+bool win_areca_scsi_device::open()
+{
+ HANDLE hFh;
- for (int i = 0; ; i++) {
- char opt = options[i];
+ if( is_open() )
+ {
+ return true;
+ }
+ hFh = CreateFile( get_dev_name(),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+ if(hFh == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
- if (!opt) {
- if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
- // Power up reported by GetDevicePowerState() and no ioctl available
- // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
- regs.bSectorCountReg = 0xff;
- out_regs_set = true;
- break;
- }
- // No IOCTL found
- return set_err(ENOSYS);
- }
- if (!strchr(valid_options, opt))
- // Invalid for this command
- continue;
+ set_fh(hFh);
+ return true;
+}
- errno = 0;
- assert( datasize == 0 || datasize == 512
- || (datasize == -512 && strchr("am", opt))
- || (datasize > 512 && opt == 'a'));
- int rc;
- switch (opt) {
- default: assert(0);
- case 's':
- // call SMART_GET_VERSION once for each drive
- if (m_smartver_state > 1) {
- rc = -1; errno = ENOSYS;
- break;
- }
- if (!m_smartver_state) {
- assert(m_port == -1);
- GETVERSIONINPARAMS_EX vers_ex;
- if (smart_get_version(get_fh(), &vers_ex) < 0) {
- if (!failuretest_permissive) {
- m_smartver_state = 2;
- rc = -1; errno = ENOSYS;
- break;
- }
- failuretest_permissive--;
- }
- else {
- // 3ware RAID if vendor id present
- m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
- }
+smart_device * win_areca_scsi_device::autodetect_open()
+{
+ return this;
+}
- m_smartver_state = 1;
- }
- rc = smart_ioctl(get_fh(), m_drive, ®s, data, datasize, m_port);
- 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
- break;
- case 'm':
- rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s, data, datasize);
- id_is_cached = (m_port < 0 && !win9x);
- break;
- case 'a':
- rc = ata_pass_through_ioctl(get_fh(), ®s,
- (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
- data, datasize);
- out_regs_set = true;
- break;
- case 'i':
- rc = ide_pass_through_ioctl(get_fh(), ®s, data, datasize);
- out_regs_set = true;
- break;
- case 'c':
- rc = ata_via_scsi_pass_through_ioctl(get_fh(), ®s, data, datasize);
- break;
- case 'f':
- if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
- rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
- id_is_cached = true;
- }
- else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
- case ATA_SMART_READ_VALUES:
- rc = storage_predict_failure_ioctl(get_fh(), data);
- if (rc > 0)
- rc = 0;
- break;
- case ATA_SMART_ENABLE:
- rc = 0;
- break;
- case ATA_SMART_STATUS:
- rc = storage_predict_failure_ioctl(get_fh());
- if (rc == 0) {
- // Good SMART status
- out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
- }
- else if (rc > 0) {
- // Bad SMART status
- out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
- rc = 0;
- }
- break;
- default:
- errno = ENOSYS; rc = -1;
- }
- else {
- errno = ENOSYS; rc = -1;
- }
- break;
- case '3':
- rc = ata_via_3ware_miniport_ioctl(get_fh(), ®s, data, datasize, m_port);
- out_regs_set = true;
- break;
- case 'p':
- assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
- rc = get_device_power_state(get_fh());
- if (rc == 0) {
- // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
- // spin up the drive => simulate ATA result STANDBY.
- regs.bSectorCountReg = 0x00;
- out_regs_set = true;
- }
- else if (rc > 0) {
- // Power up reported by GetDevicePowerState(), but this reflects the actual mode
- // only if it is selected by the device driver => try a passthrough ioctl to get the
- // actual mode, if none available simulate ACTIVE/IDLE.
- powered_up = true;
- rc = -1; errno = ENOSYS;
- }
- break;
- }
+int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+ int ioctlreturn = 0;
- if (!rc)
- // Working ioctl found
- break;
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ // errors found
+ return -1;
+ }
+ }
- if (errno != ENOSYS)
- // Abort on I/O error
- return set_err(errno);
+ return ioctlreturn;
+}
- out_regs_set = false;
- // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
- }
+bool win_areca_scsi_device::arcmsr_lock()
+{
+#define SYNCOBJNAME "Global\\SynIoctlMutex"
+ int ctlrnum = -1;
+ char mutexstr[64];
- // Return IDEREGS if set
- if (out_regs_set) {
- ata_out_regs & lo = out.out_regs;
- lo.error = regs.bFeaturesReg;
- lo.sector_count = regs.bSectorCountReg;
- lo.lba_low = regs.bSectorNumberReg;
- lo.lba_mid = regs.bCylLowReg;
- lo.lba_high = regs.bCylHighReg;
- lo.device = regs.bDriveHeadReg;
- lo.status = regs.bCommandReg;
- if (in.in_regs.is_48bit_cmd()) {
- ata_out_regs & hi = out.out_regs.prev;
- hi.sector_count = prev_regs.bSectorCountReg;
- hi.lba_low = prev_regs.bSectorNumberReg;
- hi.lba_mid = prev_regs.bCylLowReg;
- hi.lba_high = prev_regs.bCylHighReg;
- }
+ if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+ return set_err(EINVAL, "unable to parse device name");
+
+ snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+ m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+ if ( m_mutex == NULL )
+ {
+ return set_err(EIO, "CreateMutex failed");
}
- if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
- || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
- // Update ata_identify_is_cached() result according to ioctl used.
- m_id_is_cached = id_is_cached;
+ // atomic access to driver
+ WaitForSingleObject(m_mutex, INFINITE);
return true;
}
-// Return true if OS caches the ATA identify sector
-bool win_ata_device::ata_identify_is_cached() const
+
+bool win_areca_scsi_device::arcmsr_unlock()
{
- return m_id_is_cached;
+ if( m_mutex != NULL)
+ {
+ ReleaseMutex(m_mutex);
+ CloseHandle(m_mutex);
+ }
+
+ return true;
}
-//////////////////////////////////////////////////////////////////////
-// csmi_ata_device
+/////////////////////////////////////////////////////////////////////////////
+// win_areca_ata_device
+// SATA(ATA) device behind Areca RAID Controller
-bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
+class win_areca_ata_device
+: public /*implements*/ areca_ata_device,
+ public /*extends*/ win_smart_device
{
- // 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;
+public:
+ win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
+ 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);
- if (scsi_debugmode > 1) {
- const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
- pout("CSMI_SAS_DRIVER_INFO:\n");
- pout(" Name: \"%.81s\"\n", driver_info.szName);
- pout(" Description: \"%.81s\"\n", driver_info.szDescription);
- pout(" Revision: %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
- }
+private:
+ HANDLE m_mutex;
+};
- // Get 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;
- phy_info = phy_info_buf.Information;
- if (phy_info.bNumberOfPhys > sizeof(phy_info.Phy)/sizeof(phy_info.Phy[0]))
- 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++) {
- const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
- const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
- 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);
- pout(" TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
- 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, ",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
- b = at.bSASAddress;
- pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
- }
+win_areca_ata_device::win_areca_ata_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);
+}
+
+bool win_areca_ata_device::open()
+{
+ HANDLE hFh;
+
+ if( is_open() )
+ {
+ return true;
+ }
+ hFh = CreateFile( get_dev_name(),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+ if(hFh == INVALID_HANDLE_VALUE)
+ {
+ return false;
}
+ set_fh(hFh);
return true;
}
-bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no)
+smart_device * win_areca_ata_device::autodetect_open()
{
- // 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);
-
- 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);
+ // autodetect device type
+ int is_ata = arcmsr_get_dev_type();
+ if(is_ata < 0)
+ {
+ set_err(EIO);
+ return this;
+ }
- 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(is_ata == 1)
+ {
+ // SATA device
+ return this;
}
- return true;
+ // SAS device
+ smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
+ close();
+ delete this;
+ newdev->open(); // TODO: Can possibly pass open fd
+
+ return newdev.release();
}
-bool csmi_device::select_phy(unsigned phy_no)
+int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
{
- CSMI_SAS_PHY_INFO phy_info;
- if (!get_phy_info(phy_info))
- return false;
+ int ioctlreturn = 0;
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ // errors found
+ return -1;
+ }
+ }
- if (!check_phy(phy_info, phy_no))
- return false;
-
- m_phy_ent = phy_info.Phy[phy_no];
- return true;
+ return ioctlreturn;
}
-
-bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+bool win_areca_ata_device::arcmsr_lock()
{
- if (!ata_cmd_is_ok(in,
- true, // data_out_support
- true, // multi_sector_support
- true) // ata_48bit_support
- )
- return false;
-
- // Create buffer with appropriate size
- raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
- CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
-
- // Set addresses from Phy info
- CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
- const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
- pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
- pthru.bPortIdentifier = phy_ent.bPortIdentifier;
- memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
- sizeof(pthru.bDestinationSASAddress));
- pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
+#define SYNCOBJNAME "Global\\SynIoctlMutex"
+ int ctlrnum = -1;
+ char mutexstr[64];
- // Set transfer mode
- switch (in.direction) {
- case ata_cmd_in::no_data:
- pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
- break;
- case ata_cmd_in::data_in:
- pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
- pthru.uDataLength = in.size;
- break;
- case ata_cmd_in::data_out:
- pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
- pthru.uDataLength = in.size;
- memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
- break;
- default:
- return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
- (int)in.direction);
- }
+ if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+ return set_err(EINVAL, "unable to parse device name");
- // Set host-to-device FIS
+ snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+ m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+ if ( m_mutex == NULL )
{
- unsigned char * fis = pthru.bCommandFIS;
- const ata_in_regs & lo = in.in_regs;
- const ata_in_regs & hi = in.in_regs.prev;
- fis[ 0] = 0x27; // Type: host-to-device FIS
- fis[ 1] = 0x80; // Bit7: Update command register
- fis[ 2] = lo.command;
- fis[ 3] = lo.features;
- fis[ 4] = lo.lba_low;
- fis[ 5] = lo.lba_mid;
- fis[ 6] = lo.lba_high;
- fis[ 7] = lo.device;
- fis[ 8] = hi.lba_low;
- fis[ 9] = hi.lba_mid;
- fis[10] = hi.lba_high;
- fis[11] = hi.features;
- fis[12] = lo.sector_count;
- fis[13] = hi.sector_count;
+ return set_err(EIO, "CreateMutex failed");
}
- // Call ioctl
- if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
- return false;
- }
+ // atomic access to driver
+ WaitForSingleObject(m_mutex, INFINITE);
- // Get device-to-host FIS
+ return true;
+}
+
+
+bool win_areca_ata_device::arcmsr_unlock()
+{
+ if( m_mutex != NULL)
{
- const unsigned char * fis = pthru_buf->Status.bStatusFIS;
- ata_out_regs & lo = out.out_regs;
- lo.status = fis[ 2];
- lo.error = fis[ 3];
- lo.lba_low = fis[ 4];
- lo.lba_mid = fis[ 5];
- lo.lba_high = fis[ 6];
- lo.device = fis[ 7];
- lo.sector_count = fis[12];
- if (in.in_regs.is_48bit_cmd()) {
- ata_out_regs & hi = out.out_regs.prev;
- hi.lba_low = fis[ 8];
- hi.lba_mid = fis[ 9];
- hi.lba_high = fis[10];
- hi.sector_count = fis[13];
- }
+ ReleaseMutex(m_mutex);
+ CloseHandle(m_mutex);
}
- // Get data
- if (in.direction == ata_cmd_in::data_in)
- // TODO: Check ptru_buf->Status.uDataBytes
- memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
-
return true;
}
-//////////////////////////////////////////////////////////////////////
-// win_csmi_device
-
-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)
-{
-}
+/////////////////////////////////////////////////////////////////////////////
+// win_aacraid_device
+// PMC aacraid Support
-win_csmi_device::~win_csmi_device() throw()
+class win_aacraid_device
+:public /*implements*/ scsi_device,
+public /*extends*/ win_smart_device
{
- if (m_fh != INVALID_HANDLE_VALUE)
- CloseHandle(m_fh);
-}
+public:
+ win_aacraid_device(smart_interface *intf, const char *dev_name,unsigned int ctrnum, unsigned int target, unsigned int lun);
-bool win_csmi_device::is_open() const
-{
- return (m_fh != INVALID_HANDLE_VALUE);
-}
+ virtual ~win_aacraid_device() throw();
-bool win_csmi_device::close()
-{
- if (m_fh == INVALID_HANDLE_VALUE)
- return true;
- BOOL rc = CloseHandle(m_fh);
- m_fh = INVALID_HANDLE_VALUE;
- return !!rc;
-}
+ virtual bool open();
+ virtual bool scsi_pass_through(struct scsi_cmnd_io *iop);
-bool win_csmi_device::open_scsi()
-{
- // Parse name
- unsigned contr_no = ~0, phy_no = ~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) )
- return set_err(EINVAL);
+private:
+ //Device Host number
+ int m_ctrnum;
- // Open controller handle
- char devpath[30];
- snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
+ //Channel(Lun) of the device
+ int m_lun;
- HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
+ //Id of the device
+ int m_target;
+};
- if (h == INVALID_HANDLE_VALUE) {
- long err = GetLastError();
- 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 (scsi_debugmode > 1)
- pout(" %s: successfully opened\n", devpath);
+/////////////////////////////////////////////////////////////////////////////
- m_fh = h;
- m_phy_no = phy_no;
- return true;
+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_csmi_device::open()
+bool win_aacraid_device::open()
{
- if (!open_scsi())
- return false;
+ if (is_open())
+ return true;
- // Get Phy info for this drive
- if (!select_phy(m_phy_no)) {
- close();
- return false;
- }
+ 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_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
- unsigned csmi_bufsiz)
+bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
{
- // Determine signature
- const char * sig;
- switch (code) {
- case CC_CSMI_SAS_GET_DRIVER_INFO:
- sig = CSMI_ALL_SIGNATURE; break;
- case CC_CSMI_SAS_GET_PHY_INFO:
- case CC_CSMI_SAS_STP_PASSTHRU:
- sig = CSMI_SAS_SIGNATURE; break;
- default:
- return set_err(ENOSYS, "Unknown CSMI code=%u", code);
- }
-
- // Set header
- csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
- strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
- csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
- csmi_buffer->ControlCode = code;
- csmi_buffer->ReturnCode = 0;
- csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
+ 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;
- // Call function
- DWORD num_out = 0;
- if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
- csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
- long err = GetLastError();
- if (scsi_debugmode)
- pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
- if ( err == ERROR_INVALID_FUNCTION
- || err == ERROR_NOT_SUPPORTED
- || err == ERROR_DEV_NOT_EXIST)
- return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
+ 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 : (int)iop->dxfer_len) , 1);
+ }
else
- return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
+ 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());
}
- // Check result
- if (csmi_buffer->ReturnCode) {
- if (scsi_debugmode) {
- pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",
- code, csmi_buffer->ReturnCode);
+ 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]);
}
- return set_err(EIO, "CSMI(%u) failed with ReturnCode=%lu", code, csmi_buffer->ReturnCode);
+ }
+ else {
+ iop->resp_sense_len = 0;
}
- if (scsi_debugmode > 1)
- pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code, num_out);
-
+ 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((const uint8_t *)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
+ }
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;
+// win_nvme_device
-// 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
+class win_nvme_device
+: public /*implements*/ nvme_device,
+ public /*extends*/ win_smart_device
+{
+public:
+ win_nvme_device(smart_interface * intf, const char * dev_name,
+ const char * req_type, unsigned nsid);
-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;
+ virtual bool open();
-#pragma pack()
+ virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
-// 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
+ bool open_scsi(int n);
+ bool probe();
-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;
-}
+private:
+ int m_scsi_no;
+};
-// Get ASPI entrypoint from wnaspi32.dll
+/////////////////////////////////////////////////////////////////////////////
-static FARPROC aspi_get_address(const char * name, int verbose)
+win_nvme_device::win_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),
+ m_scsi_no(-1)
{
- 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)
+bool win_nvme_device::open_scsi(int n)
{
- 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);
- }
+ // TODO: Use common open function for all devices using "\\.\ScsiN:"
+ char devpath[32];
+ snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%d:", n);
- // 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;
+ HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
- // 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 (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 (scsi_debugmode)
- pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
+ if (nvme_debugmode > 1)
+ pout(" %s: successfully opened\n", devpath);
-#ifdef __CYGWIN__
- // save PID to detect fork() in aspi_entry_valid()
- aspi_dll_pid = GetCurrentProcessId();
-#endif
- assert(aspi_entry_valid());
- return 0;
+ set_fh(h);
+ return true;
}
+// 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;
+ nvme_cmd_in in;
+ in.set_data_in(smartmontools::nvme_admin_identify, &id_ctrl, sizeof(id_ctrl));
+ // in.nsid = 0;
+ in.cdw10 = 0x1;
+ nvme_cmd_out out;
+
+ bool ok = nvme_pass_through(in, out);
+ if (!ok && nvme_debugmode > 1)
+ pout(" nvme probe failed: %s\n", get_errmsg());
+ return ok;
+}
-static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
+bool win_nvme_device::open()
{
- 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());
+ if (m_scsi_no < 0) {
+ // First open -> search of NVMe devices
+ const char * name = skipdev(get_dev_name());
+ char s[2+1] = ""; int n1 = -1, n2 = -1, len = strlen(name);
+ unsigned no = ~0, nsid = 0xffffffff;
+ sscanf(name, "nvm%2[es]%u%nn%u%n", s, &no, &n1, &nsid, &n2);
+
+ if (!( (n1 == len || (n2 == len && nsid > 0))
+ && s[0] == 'e' && (!s[1] || s[1] == 's') ))
+ return set_err(EINVAL);
+
+ if (!s[1]) {
+ // /dev/nvmeN* -> search for nth NVMe device
+ unsigned nvme_cnt = 0;
+ for (int i = 0; i < 32; i++) {
+ if (!open_scsi(i)) {
+ if (get_errno() == EACCES)
+ return false;
+ continue;
+ }
+ // Done if pass-through works and correct number
+ if (probe()) {
+ if (nvme_cnt == no) {
+ m_scsi_no = i;
+ break;
+ }
+ nvme_cnt++;
+ }
+ close();
}
- // TODO: ASPI_ABORT_IO command
- aspi_entry = 0;
- h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
- return -EIO;
- }
- }
- CloseHandle(event);
- return 0;
-}
+ if (!is_open())
+ return set_err(ENOENT);
+ clear_err();
+ }
+ else {
+ // /dev/nvmesN* -> use "\\.\ScsiN:"
+ if (!open_scsi(no))
+ return false;
+ m_scsi_no = no;
+ }
-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)
-{
-}
+ if (!get_nsid())
+ set_nsid(nsid);
+ }
+ else {
+ // Reopen same "\\.\ScsiN:"
+ if (!open_scsi(m_scsi_no))
+ return false;
+ }
-bool win_aspi_device::is_open() const
-{
- return (m_adapter >= 0);
+ return true;
}
-bool win_aspi_device::open()
+bool win_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
{
- // 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);
+ // Create buffer with appropriate size
+ raw_buffer pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer) + in.size);
+ NVME_PASS_THROUGH_IOCTL * pthru =
+ reinterpret_cast<NVME_PASS_THROUGH_IOCTL *>(pthru_raw_buf.data());
+
+ // Set NVMe command
+ pthru->SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL);
+ 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;
+ pthru->SrbIoCtrl.Length = pthru_raw_buf.size() - sizeof(SRB_IO_CONTROL);
+
+ pthru->NVMeCmd[0] = in.opcode;
+ pthru->NVMeCmd[1] = in.nsid;
+ pthru->NVMeCmd[10] = in.cdw10;
+ pthru->NVMeCmd[11] = in.cdw11;
+ pthru->NVMeCmd[12] = in.cdw12;
+ pthru->NVMeCmd[13] = in.cdw13;
+ pthru->NVMeCmd[14] = in.cdw14;
+ pthru->NVMeCmd[15] = in.cdw15;
+
+ pthru->Direction = in.direction();
+ // pthru->QueueId = 0; // AdminQ
+ // pthru->DataBufferLen = 0;
+ if (in.direction() & nvme_cmd_in::data_out) {
+ pthru->DataBufferLen = in.size;
+ memcpy(pthru->DataBuffer, in.buffer, in.size);
+ }
+ // pthru->MetaDataLen = 0;
+ pthru->ReturnBufferLen = pthru_raw_buf.size();
+
+ // Call NVME_PASS_THROUGH
+ DWORD num_out = 0;
+ BOOL ok = DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT,
+ pthru, pthru_raw_buf.size(), pthru, pthru_raw_buf.size(),
+ &num_out, (OVERLAPPED*)0);
- if (!aspi_entry_valid()) {
- if (aspi_open_dll(1/*verbose*/))
- return set_err(ENOENT);
- }
+ // Check status
+ unsigned status = pthru->CplEntry[3] >> 17;
+ if (status)
+ return set_nvme_err(out, status);
- // 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);
- }
+ if (!ok)
+ return set_err(EIO, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError());
- // 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);
+ if (in.direction() & nvme_cmd_in::data_in)
+ memcpy(in.buffer, pthru->DataBuffer, in.size);
- m_adapter = (int)adapter; m_id = (unsigned char)id;
+ out.result = pthru->CplEntry[0];
return true;
}
-bool win_aspi_device::close()
-{
- // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
- return true;
-}
+/////////////////////////////////////////////////////////////////////////////
+// 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);
-// Scan for ASPI drives
+ virtual bool open();
-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;
- }
+ virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
- for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {
- ASPI_SRB srb;
+private:
+ bool open(int phydrive);
+};
- 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;
- }
+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)
+{
+}
- 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 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));
- 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;
- }
+ // 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);
- 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;
+ return set_err(EINVAL);
}
-
-// Interface to ASPI SCSI devices
-bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)
+bool win10_nvme_device::open(int phydrive)
{
- int report = scsi_debugmode; // TODO
+ // TODO: Use common open function for all devices using "\\.\PhysicalDriveN"
+ char devpath[64];
+ snprintf(devpath, sizeof(devpath) - 1, "\\\\.\\PhysicalDrive%d", phydrive);
- if (m_adapter < 0) {
- set_err(EBADF);
- return false;
- }
-
- if (!aspi_entry_valid()) {
- set_err(EBADF);
- return false;
- }
+ // 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 (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
- set_err(EINVAL, "bad CDB length");
+ 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 (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);
+ if (nvme_debugmode > 1)
+ pout(" %s: successfully opened\n", devpath);
- 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;
+ set_fh(h);
- 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);
- }
+ // Use broadcast namespace if no NSID specified
+ // TODO: Get NSID of current device
+ if (!get_nsid())
+ set_nsid(0xffffffff);
+ return true;
+}
- 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);
+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];
+};
- 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;
+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 DXFER_TO_DEVICE:
- srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
- srb.i.data_size = iop->dxfer_len;
- srb.i.data_addr = iop->dxferp;
+ 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:
- set_err(EINVAL, "bad dxfer_dir");
- return false;
+ return set_err(ENOSYS, "NVMe admin command 0x%02x not supported", in.opcode);
}
- 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;
- }
- }
+ spsq->ProtocolSpecific.ProtocolDataRequestSubValue = in.nsid; // ?
+ spsq->ProtocolSpecific.ProtocolDataOffset = sizeof(spsq->ProtocolSpecific);
+ spsq->ProtocolSpecific.ProtocolDataLength = in.size;
- if (report > 0)
- pout(" OK\n");
+ if (in.direction() & nvme_cmd_in::data_out)
+ memcpy(spsq->DataBuffer, in.buffer, in.size);
- 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);
+ 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;
}
-#endif // WIN9X_SUPPORT
/////////////////////////////////////////////////////////////////////////////
-// SPT Interface (for SCSI devices and ATA devices behind SATLs)
-// Only supported in NT and later
-/////////////////////////////////////////////////////////////////////////////
+// win_smart_interface
+// Platform specific interface
-win_scsi_device::win_scsi_device(smart_interface * intf,
- const char * dev_name, const char * req_type)
-: smart_device(intf, dev_name, "scsi", req_type)
+class win_smart_interface
+: public /*implements*/ smart_interface
{
-}
+public:
+ virtual std::string get_os_version_str();
-bool win_scsi_device::open()
-{
- const char * name = skipdev(get_dev_name()); int len = strlen(name);
- // sd[a-z],N => Physical drive 0-26, RAID port N
- char drive[1+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
- && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0)) ) {
- return open(drive[0] - 'a', -1, -1, sub_addr);
- }
- // pd<m>,N => Physical drive <m>, RAID port N
- int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
- if ( sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
- && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
- return open(pd_num, -1, -1, sub_addr);
- }
- // [a-zA-Z]: => Physical drive behind logical drive 0-25
- int logdrive = drive_letter(name);
- if (logdrive >= 0) {
- return open(-1, logdrive, -1, -1);
- }
- // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
- int tape_num = -1; n1 = -1;
- if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
- return open(-1, -1, tape_num, -1);
- }
- tape_num = -1; n1 = -1;
- if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
- return open(-1, -1, tape_num, -1);
- }
- // tape<m> => tape drive <m>
- tape_num = -1; n1 = -1;
- if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
- return open(-1, -1, tape_num, -1);
- }
+ virtual std::string get_app_examples(const char * appname);
+
+#ifndef __CYGWIN__
+ virtual int64_t get_timer_usec();
+#endif
- return set_err(EINVAL);
-}
+ virtual bool disable_system_auto_standby(bool disable);
-bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
+ 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 nvme_device * get_nvme_device(const char * name, const char * type, unsigned nsid);
+
+ virtual smart_device * autodetect_smart_device(const char * name);
+
+ virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+ virtual std::string get_valid_custom_dev_types_str();
+
+private:
+ smart_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WIN64
+// Running on 64-bit Windows as 32-bit app ?
+static bool is_wow64()
{
- char b[128];
- b[sizeof(b) - 1] = '\0';
- if (pd_num >= 0)
- snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
- else if (ld_num >= 0)
- snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
- else if (tape_num >= 0)
- snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
- else {
- set_err(EINVAL);
+ BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
+ (BOOL (WINAPI *)(HANDLE, PBOOL))
+ GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
+ if (!IsWow64Process_p)
return false;
- }
-
- // Open device
- HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
- OPEN_EXISTING, 0, 0);
- if (h == INVALID_HANDLE_VALUE) {
- set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());
+ BOOL w64 = FALSE;
+ if (!IsWow64Process_p(GetCurrentProcess(), &w64))
return false;
- }
- set_fh(h);
- return true;
+ return !!w64;
}
+#endif // _WIN64
+// Return info string about build host and OS version
+std::string win_smart_interface::get_os_version_str()
+{
+ char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
+ = SMARTMONTOOLS_BUILD_HOST;
+ if (vstr[1] < '6')
+ vstr[1] = '6';
+ char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
+ const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
+ assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
-typedef struct {
- SCSI_PASS_THROUGH_DIRECT spt;
- ULONG Filler;
- UCHAR ucSenseBuf[64];
-} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+ // Starting with Windows 8.1, GetVersionEx() does no longer report the
+ // actual OS version. 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 (!RtlGetVersion_p || RtlGetVersion_p(&vi)) {
+ if (!GetVersionExW((OSVERSIONINFOW *)&vi))
+ return vstr;
+ }
-// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
-// Used if DataTransferLength not supported by *_DIRECT.
-static long scsi_pass_through_indirect(HANDLE h,
- SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
-{
- struct SCSI_PASS_THROUGH_WITH_BUFFERS {
- SCSI_PASS_THROUGH spt;
- ULONG Filler;
- UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
- UCHAR ucDataBuf[512];
- };
+ const char * w = 0;
+ unsigned build = 0;
+ if ( vi.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
+ 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;
+ }
+ }
- SCSI_PASS_THROUGH_WITH_BUFFERS sb;
- memset(&sb, 0, sizeof(sb));
+ const char * w64 = "";
+#ifndef _WIN64
+ if (is_wow64())
+ w64 = "(64)";
+#endif
- // DATA_OUT not implemented yet
- if (!( sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
- && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
- return ERROR_INVALID_PARAMETER;
+ if (!w)
+ 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-sp%u.%u%s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
+ else if (vi.wServicePackMajor)
+ snprintf(vptr, vlen, "-%s-sp%u%s", w, vi.wServicePackMajor, w64);
+ else
+ snprintf(vptr, vlen, "-%s%s", w, w64);
+ return vstr;
+}
- sb.spt.Length = sizeof(sb.spt);
- sb.spt.CdbLength = sbd->spt.CdbLength;
- memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
- sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
- sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
- sb.spt.DataIn = sbd->spt.DataIn;
- sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
- sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
- sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
+#ifndef __CYGWIN__
+// MSVCRT only provides ftime() which uses GetSystemTime()
+// This provides only ~15ms resolution by default.
+// Use QueryPerformanceCounter instead (~300ns).
+// (Cygwin provides CLOCK_MONOTONIC which has the same effect)
+int64_t win_smart_interface::get_timer_usec()
+{
+ static int64_t freq = 0;
- DWORD num_out;
- if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
- &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
- return GetLastError();
+ LARGE_INTEGER t;
+ if (freq == 0)
+ freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
+ if (freq <= 0)
+ return smart_interface::get_timer_usec();
- sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
- if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
- memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
+ if (!QueryPerformanceCounter(&t))
+ return -1;
+ if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
+ return -1;
- sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
- if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
- memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
- return 0;
+ return (t.QuadPart * 1000000LL) / freq;
}
+#endif // __CYGWIN__
-// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
-bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
+ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
{
- int report = scsi_debugmode; // TODO
-
- if (report > 0) {
- int k, j;
- const unsigned char * ucp = iop->cmnd;
- const char * np;
- char buff[256];
- const int sz = (int)sizeof(buff);
+ const char * testname = skipdev(name);
+ if (!strncmp(testname, "csmi", 4))
+ return new win_csmi_device(this, name, type);
+ if (!strncmp(testname, "tw_cli", 6))
+ return new win_tw_cli_device(this, name, type);
+ return new win_ata_device(this, name, type);
+}
- 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;
+scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+ return new win_scsi_device(this, name, type);
+}
- 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);
- }
+nvme_device * win_smart_interface::get_nvme_device(const char * name, const char * type,
+ unsigned 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);
+}
- SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
- if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
- set_err(EINVAL, "cmnd_len too large");
- return false;
- }
- memset(&sb, 0, sizeof(sb));
- sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
- sb.spt.CdbLength = iop->cmnd_len;
- memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
- sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
- sb.spt.SenseInfoOffset =
- offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
- sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+ // Areca?
+ int disknum = -1, n1 = -1, n2 = -1;
+ int encnum = 1;
+ char devpath[32];
- bool direct = true;
- switch (iop->dxfer_dir) {
- case DXFER_NONE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
- break;
- case DXFER_FROM_DEVICE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
- sb.spt.DataTransferLength = iop->dxfer_len;
- sb.spt.DataBuffer = iop->dxferp;
- // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
- // transfers (needed for SMART STATUS check of JMicron USB bridges)
- if (sb.spt.DataTransferLength == 1)
- direct = false;
- break;
- case DXFER_TO_DEVICE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
- sb.spt.DataTransferLength = iop->dxfer_len;
- sb.spt.DataBuffer = iop->dxferp;
- break;
- default:
- set_err(EINVAL, "bad dxfer_dir");
- return false;
- }
+ if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
+ if (!(1 <= disknum && disknum <= 128)) {
+ set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
+ return 0;
+ }
+ if (!(1 <= encnum && encnum <= 8)) {
+ set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
+ return 0;
+ }
- long err = 0;
- if (direct) {
- DWORD num_out;
- if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
- &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
- err = GetLastError();
+ 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;
}
- else
- err = scsi_pass_through_indirect(get_fh(), &sb);
- if (err)
- return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
- "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
- (direct ? "_DIRECT" : ""), err);
+ // aacraid?
+ unsigned ctrnum, lun, target;
+ n1 = -1;
- iop->scsi_status = sb.spt.ScsiStatus;
- if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
- int slen = sb.ucSenseBuf[7] + 8;
+ 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;
+ }
- if (slen > (int)sizeof(sb.ucSenseBuf))
- slen = sizeof(sb.ucSenseBuf);
- if (slen > (int)iop->max_sense_len)
- slen = iop->max_sense_len;
- memcpy(iop->sensep, sb.ucSenseBuf, slen);
- iop->resp_sense_len = slen;
- if (report) {
- if (report > 1) {
- pout(" >>> Sense buffer, len=%d:\n", slen);
- dStrHex(iop->sensep, slen , 1);
+ /*
+ 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, ®Type, (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);
}
- 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_len > 0) && (sb.spt.DataTransferLength > 0))
- 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,
- (trunc ? " [only first 256 bytes shown]" : ""));
- dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+ set_err(EINVAL, "aacraid: host %u not found", ctrnum);
+ return 0;
}
+
+ return 0;
+}
+
+std::string win_smart_interface::get_valid_custom_dev_types_str()
+{
+ return "aacraid,H,L,ID, areca,N[/E]";
+}
+
+
+// Return value for device detection functions
+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)
+{
+ if (!data->desc.VendorIdOffset)
+ return false;
+ if (strcmp(data->raw + data->desc.VendorIdOffset, "ATA "))
+ return false;
return true;
}
-// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
-static long scsi_pass_through_direct(HANDLE fd, struct scsi_cmnd_io * iop)
+// Return true if Intel ICHxR RAID volume
+static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
{
- int report = scsi_debugmode; // TODO
+ 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;
+ return true;
+}
- if (report > 0) {
- int k, j;
- const unsigned char * ucp = iop->cmnd;
- const char * np;
- char buff[256];
- const int sz = (int)sizeof(buff);
+// get DEV_* for open handle
+static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
+{
+ // Get BusType from device descriptor
+ STORAGE_DEVICE_DESCRIPTOR_DATA data;
+ if (storage_query_property_ioctl(hdevice, &data))
+ return DEV_UNKNOWN;
- 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;
+ // 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;
- 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);
- }
+ if (ata_version_ex)
+ memset(ata_version_ex, 0, sizeof(*ata_version_ex));
+ return DEV_ATA;
- SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
- if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
- return EINVAL;
- }
+ case BusTypeScsi:
+ case BusTypeRAID:
+ if (is_sat(&data))
+ return DEV_SAT;
- memset(&sb, 0, sizeof(sb));
- sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
- //sb.spt.PathId = 0;
- sb.spt.TargetId = 127;
- //sb.spt.Lun = 0;
- sb.spt.CdbLength = iop->cmnd_len;
- memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
- sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
- sb.spt.SenseInfoOffset =
- offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
- sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+ // 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;
- bool direct = true;
- switch (iop->dxfer_dir) {
- case DXFER_NONE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
- break;
- case DXFER_FROM_DEVICE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
- sb.spt.DataTransferLength = iop->dxfer_len;
- sb.spt.DataBuffer = iop->dxferp;
- // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
- // transfers (needed for SMART STATUS check of JMicron USB bridges)
- if (sb.spt.DataTransferLength == 1)
- direct = false;
- break;
- case DXFER_TO_DEVICE:
- sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
- sb.spt.DataTransferLength = iop->dxfer_len;
- sb.spt.DataBuffer = iop->dxferp;
- break;
+ return DEV_SCSI;
+
+ case 0x09: // BusTypeiScsi
+ case 0x0a: // BusTypeSas
+ if (is_sat(&data))
+ return DEV_SAT;
+
+ return DEV_SCSI;
+
+ case BusTypeUsb:
+ return DEV_USB;
+
+ case 0x11: // BusTypeNvme
+ return DEV_NVME;
+
+ case 0x12: //BusTypeSCM
+ case 0x13: //BusTypeUfs
+ case 0x14: //BusTypeMax,
default:
- return EINVAL;
+ return DEV_UNKNOWN;
}
+ /*NOTREACHED*/
+}
- long err = 0;
- if (direct) {
- DWORD num_out;
- if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
- &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
- err = GetLastError();
+// get DEV_* for device path
+static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
+{
+ bool admin = true;
+ HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ admin = false;
+ h = CreateFileA(path, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return DEV_UNKNOWN;
}
- else
- err = scsi_pass_through_indirect(fd, &sb);
+ if (ata_debugmode > 1 || scsi_debugmode > 1)
+ pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
+ win_dev_type type = get_controller_type(h, admin, ata_version_ex);
+ CloseHandle(h);
+ return type;
+}
- if (err)
- {
- return err;
- }
+// get DEV_* for physical drive number
+static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
+{
+ char path[30];
+ snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
+ return get_controller_type(path, ata_version_ex);
+}
- iop->scsi_status = sb.spt.ScsiStatus;
- if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
- int slen = sb.ucSenseBuf[7] + 8;
+static win_dev_type get_phy_drive_type(int drive)
+{
+ return get_phy_drive_type(drive, 0);
+}
- if (slen > (int)sizeof(sb.ucSenseBuf))
- slen = sizeof(sb.ucSenseBuf);
- if (slen > (int)iop->max_sense_len)
- slen = iop->max_sense_len;
- memcpy(iop->sensep, sb.ucSenseBuf, 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;
+// get DEV_* for logical drive number
+static win_dev_type get_log_drive_type(int drive)
+{
+ char path[30];
+ snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
+ return get_controller_type(path);
+}
- if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
- iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
- else
- iop->resid = 0;
+static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdrive)
+{
+ phydrive = logdrive = -1;
- 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);
+ name = skipdev(name);
+ if (!strncmp(name, "st", 2))
+ return DEV_SCSI;
+ if (!strncmp(name, "nst", 3))
+ return DEV_SCSI;
+ if (!strncmp(name, "tape", 4))
+ return DEV_SCSI;
+
+ logdrive = drive_letter(name);
+ if (logdrive >= 0) {
+ win_dev_type type = get_log_drive_type(logdrive);
+ return (type != DEV_UNKNOWN ? type : DEV_SCSI);
+ }
+
+ char drive[2+1] = "";
+ if (sscanf(name, "sd%2[a-z]", drive) == 1) {
+ phydrive = sdxy_to_phydrive(drive);
+ return get_phy_drive_type(phydrive);
}
- return 0;
-}
+ if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
+ return get_phy_drive_type(phydrive);
+ return DEV_UNKNOWN;
+}
-#if 0 // For debugging areca code
-static void dumpdata(unsigned char *block, int len)
+smart_device * win_smart_interface::get_usb_device(const char * name,
+ int phydrive, int logdrive /* = -1 */)
{
- int ln = (len / 16) + 1; // total line#
- unsigned char c;
- int pos = 0;
-
- printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);
- 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
- // if a line data length < 16 then append the space to the tail of line to reach 16 chars
- printf("%02X | ", l);
- for ( pos = 0; pos < 16 && len; pos++, len-- )
- {
- c = block[l*16+pos];
- printf("%02X ", c);
- }
+ // Get USB bridge ID
+ unsigned short vendor_id = 0, product_id = 0;
+ if (!get_usb_id(phydrive, logdrive, vendor_id, product_id)) {
+ set_err(EINVAL, "Unable to read USB device ID");
+ return 0;
+ }
- if ( pos < 16 )
- {
- for ( int loop = pos; loop < 16; loop++ )
- {
- printf(" ");
- }
- }
+ // Get type name for this ID
+ const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
+ if (!usbtype)
+ return 0;
- // print ASCII char
- for ( int loop = 0; loop < pos; loop++ )
- {
- c = block[l*16+loop];
- if ( c >= 0x20 && c <= 0x7F )
- {
- printf("%c", c);
- }
- else
- {
- printf(".");
- }
- }
- printf("\n");
- }
- printf("=====================================================================\n");
+ // Return SAT/USB device for this type
+ return get_scsi_passthrough_device(usbtype, new win_scsi_device(this, name, ""));
}
-#endif
-
-// 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)
+smart_device * win_smart_interface::autodetect_smart_device(const char * name)
{
- int ioctlreturn = 0;
- sSRB_BUFFER sBuf;
- struct scsi_cmnd_io io_hdr;
- int dir = DXFER_TO_DEVICE;
-
- UINT8 cdb[10];
- UINT8 sense[32];
-
- unsigned char *areca_return_packet;
- int total = 0;
- int expected = -1;
- unsigned char return_buff[2048];
- unsigned char *ptr = &return_buff[0];
- memset(return_buff, 0, sizeof(return_buff));
+ const char * testname = skipdev(name);
+ if (str_starts_with(testname, "hd"))
+ return new win_ata_device(this, name, "");
- memset((unsigned char *)&sBuf, 0, sizeof(sBuf));
- memset(&io_hdr, 0, sizeof(io_hdr));
- memset(cdb, 0, sizeof(cdb));
- memset(sense, 0, sizeof(sense));
+ 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, "");
- sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL);
- memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));
- sBuf.srbioctl.Timeout = 10000;
- sBuf.srbioctl.ControlCode = arcmsr_cmd;
+ if (str_starts_with(testname, "nvme"))
+ return new win_nvme_device(this, name, "", 0 /* use default nsid */);
- switch ( arcmsr_cmd )
- {
- // command for writing data to driver
- 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;
- }
+ int phydrive = -1, logdrive = -1;
+ win_dev_type type = get_dev_type(name, phydrive, logdrive);
- cdb[1] = 0x01;
- cdb[2] = 0xf0;
+ if (type == DEV_ATA)
+ return new win_ata_device(this, name, "");
- io_hdr.dxfer_dir = dir;
- 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;
+ if (type == DEV_SCSI)
+ return new win_scsi_device(this, name, "");
- while ( 1 )
- {
- ioctlreturn = scsi_pass_through_direct(fd, &io_hdr);
- if ( ioctlreturn || io_hdr.scsi_status )
- {
- // errors found
- break;
- }
+ if (type == DEV_SAT)
+ return get_sat_device("sat", new win_scsi_device(this, name, ""));
- if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER )
- {
- // if succeeded, just returns the length of outgoing data
- return data_len;
- }
+ if (type == DEV_USB)
+ return get_usb_device(name, phydrive, logdrive);
- 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 (type == DEV_NVME)
+ return new win10_nvme_device(this, name, "", 0 /* use default nsid */);
- if ( total >= 7 && total >= expected )
- {
- //printf("total bytes received = %d, expected length = %d\n", total, expected);
+ return 0;
+}
- // ------ 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(...)
- return -4;
+// Scan for devices
+bool win_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;
}
- if ( ioctlreturn )
- {
- pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn);
- return -2;
+ // Check for "[*,]pd" type
+ bool pd = 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 ( io_hdr.scsi_status )
- {
- pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status);
- return -3;
+ // Set valid types
+ bool ata, scsi, sat, usb, csmi, nvme;
+ if (!type) {
+ ata = scsi = usb = sat = csmi = true;
+#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
+ nvme = true;
+#else
+ nvme = false;
+#endif
}
-
- if ( data )
- {
- memcpy(data, return_buff, total);
+ else {
+ ata = scsi = usb = sat = csmi = nvme = 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 if (!strcmp(type, "nvme"))
+ nvme = true;
+ else {
+ set_err(EINVAL,
+ "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], "
+ "sat[,pd], usb[,pd], csmi, nvme, pd", type);
+ return false;
+ }
}
- return total;
-}
-
-
-win_areca_device::win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum)
-: smart_device(intf, dev_name, "areca", "areca"),
- m_disknum(disknum),
- m_encnum(encnum)
-{
- set_fh(fh);
- set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
-}
-
-bool win_areca_device::open()
-{
- HANDLE hFh;
+ char name[32];
- if( is_open() )
- {
- return true;
- }
+ 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};
- hFh = CreateFile( get_dev_name(),
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL );
- if(hFh == INVALID_HANDLE_VALUE)
- {
- return 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');
- set_fh(hFh);
- return true;
-}
+ smart_device * dev = 0;
+ GETVERSIONINPARAMS_EX vers_ex;
-// Areca RAID Controller
-bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
-{
- // ATA input registers
- 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
- // Note: The output registers is re-sorted for areca internal use only
- typedef struct _ATA_OUTPUT_REGISTERS
- {
- unsigned char error;
- unsigned char status;
- 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 |
- // +--------------------------------------------------------------------------------+
- //
+ 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;
- //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);
+ // Interpret RAID drive map if present
+ if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
+ // Skip if too many controllers or logical drive from this controller already seen
+ if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
+ continue;
+ raid_seen[vers_ex.wControllerId] = true;
+ // Add physical drives
+ int len = strlen(name);
+ for (unsigned int pi = 0; pi < 32; 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") );
+ }
+ }
+ continue;
+ }
- // ----- 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
+ dev = new win_ata_device(this, name, "ata");
+ break;
- // ----- BEGIN TO SETUP PAYLOAD DATA -----
- memcpy(&areca_packet[7], "SmrT", 4); // areca defined password
- ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
+ case DEV_SCSI:
+ // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
+ if (!scsi)
+ continue;
+ dev = new win_scsi_device(this, name, "scsi");
+ break;
- // 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);
- }
+ case DEV_SAT:
+ // STORAGE_QUERY_PROPERTY returned VendorId "ATA "
+ if (!sat)
+ continue;
+ dev = get_sat_device("sat", new win_scsi_device(this, name, ""));
+ break;
- areca_packet[11] = m_disknum - 1; // disk#
- areca_packet[19] = m_encnum - 1; // enc#
+ case DEV_USB:
+ // STORAGE_QUERY_PROPERTY returned USB
+ if (!usb)
+ continue;
+ dev = get_usb_device(name, i);
+ if (!dev)
+ // Unknown or unsupported USB ID, return as SCSI
+ dev = new win_scsi_device(this, name, "");
+ break;
- // ----- BEGIN TO SETUP CHECKSUM -----
- for ( int loop = 3; loop < areca_packet_len - 1; loop++ )
- {
- cs += areca_packet[loop];
- }
- areca_packet[areca_packet_len-1] = cs;
+ case DEV_NVME:
+ // STORAGE_QUERY_PROPERTY returned NVMe
+ if (!nvme)
+ continue;
+ dev = new win10_nvme_device(this, name, "", 0 /* use default nsid */);
+ break;
- // ----- BEGIN TO SEND TO ARECA DRIVER ------
- int expected = 0;
- unsigned char return_buff[2048];
- memset(return_buff, 0, sizeof(return_buff));
+ default:
+ // Unknown type
+ continue;
+ }
- expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0);
- if (expected==-3) {
- return set_err(EIO);
+ devlist.push_back(dev);
+ }
}
- expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0);
- expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len);
- if ( expected > 0 )
- {
- expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff));
- }
- if ( expected < 0 )
- {
- return set_err(EIO);
- }
+ if (csmi) {
+ // Scan CSMI devices
+ for (int i = 0; i <= 9; i++) {
+ snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
+ win_csmi_device test_dev(this, name, "");
+ if (!test_dev.open_scsi())
+ continue;
- // ----- VERIFY THE CHECKSUM -----
- cs = 0;
- for ( int loop = 3; loop < expected - 1; loop++ )
- {
- cs += return_buff[loop];
- }
+ unsigned ports_used = test_dev.get_ports_used();
+ if (!ports_used)
+ continue;
- if ( return_buff[expected - 1] != cs )
- {
- return set_err(EIO);
+ for (int pi = 0; pi < 32; 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") );
+ }
+ }
}
- 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);
- }
- }
+ if (nvme) {
+ // Scan \\.\Scsi[0-31] for up to 10 NVMe devices
+ int nvme_cnt = 0;
+ for (int i = 0; i < 32; i++) {
+ snprintf(name, sizeof(name)-1, "/dev/nvme%d", i);
+ win_nvme_device test_dev(this, name, "", 0);
+ if (!test_dev.open_scsi(i)) {
+ if (test_dev.get_errno() == EACCES)
+ break;
+ continue;
+ }
- // returns with data
- if (readdata)
- {
- memcpy(in.buffer, &return_buff[7], in.size);
- }
+ if (!test_dev.probe())
+ continue;
+ if (++nvme_cnt >= 10)
+ break;
+ }
- // 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;
+ for (int i = 0; i < nvme_cnt; i++) {
+ snprintf(name, sizeof(name)-1, "/dev/nvme%d", i);
+ devlist.push_back( new win_nvme_device(this, name, "nvme", 0) );
+ }
}
return true;
}
-bool win_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+// get examples for smartctl
+std::string win_smart_interface::get_app_examples(const char * appname)
{
-#define SYNCOBJNAME "Global\\SynIoctlMutex"
- int ctlrnum = -1;
- 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)
- return set_err(EINVAL, "unable to parse device name");
-
- memset(mutexstr, 0, sizeof(mutexstr));
- sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum);
- pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
- 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");
- }
-
- // atomic access to driver
- WaitForSingleObject(hmutex, INFINITE);
- bool ok = arcmsr_ata_pass_through(in,out);
- ReleaseMutex(hmutex);
+ if (strcmp(appname, "smartctl"))
+ return "";
+ return "=================================================== SMARTCTL EXAMPLES =====\n\n"
+ " smartctl -a /dev/sda (Prints all SMART information)\n\n"
+ " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
+ " (Enables SMART on first disk)\n\n"
+ " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n"
+ " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
+ " (Prints Self-Test & Attribute errors)\n"
+ " smartctl -a /dev/sda\n"
+ " (Prints all information for disk on PhysicalDrive 0)\n"
+ " smartctl -a /dev/pd3\n"
+ " (Prints all information for disk on PhysicalDrive 3)\n"
+ " smartctl -a /dev/tape1\n"
+ " (Prints all information for SCSI tape on Tape 1)\n"
+ " smartctl -A /dev/hdb,3\n"
+ " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
+ " smartctl -A /dev/tw_cli/c0/p1\n"
+ " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
+ " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
+ " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
+ " on 1st Areca RAID controller)\n"
+ "\n"
+ " ATA SMART access methods and ordering may be specified by modifiers\n"
+ " following the device name: /dev/hdX:[saicm], where\n"
+ " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
+ " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
+ " 'm': IOCTL_SCSI_MINIPORT_*.\n"
+ + strprintf(
+ " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
+ );
+}
- if(hmutex)
- {
- CloseHandle(hmutex);
- }
- if ( (HLOCAL)pSD )
- {
- LocalFree((HLOCAL)pSD);
+bool win_smart_interface::disable_system_auto_standby(bool disable)
+{
+ if (disable) {
+ SYSTEM_POWER_STATUS ps;
+ if (!GetSystemPowerStatus(&ps))
+ return set_err(ENOSYS, "Unknown power status");
+ if (ps.ACLineStatus != 1) {
+ SetThreadExecutionState(ES_CONTINUOUS);
+ if (ps.ACLineStatus == 0)
+ set_err(EIO, "AC offline");
+ else
+ set_err(EIO, "Unknown AC line status");
+ return false;
+ }
}
- return ok;
+ if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
+ return set_err(ENOSYS);
+ return true;
}
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-
} // namespace
/////////////////////////////////////////////////////////////////////////////
SetDllDirectoryA_p("");
}
- // Select interface for Windows flavor
- if (GetVersion() & 0x80000000) {
-#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);
- }
+ static os_win32::win_smart_interface the_win_interface;
+ smart_interface::set(&the_win_interface);
}