X-Git-Url: https://git.proxmox.com/?p=mirror_smartmontools-debian.git;a=blobdiff_plain;f=os_win32.cpp;h=2414d131be55dc0e39e6f4485c069961ef531fc7;hp=ba9364cd1ec64b216889bff82e2c3f43357ad57a;hb=12d5f9dc1acacc1ef1ab1b84b6ab73ba56d1f2f1;hpb=f4ebf3d14d1fc0a257b7183a18d1b1505c163361 diff --git a/os_win32.cpp b/os_win32.cpp index ba9364c..2414d13 100644 --- a/os_win32.cpp +++ b/os_win32.cpp @@ -3,7 +3,8 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-9 Christian Franke + * Copyright (C) 2004-14 Christian Franke + * Copyright (C) 2012 Hank Wu * * 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 @@ -16,15 +17,20 @@ */ #include "config.h" +#define WINVER 0x0502 +#define _WIN32_WINNT WINVER + #include "int64.h" #include "atacmds.h" -#include "extern.h" -extern smartmonctrl * con; // con->permissive,reportataioctl #include "scsicmds.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 @@ -32,25 +38,275 @@ extern smartmonctrl * con; // con->permissive,reportataioctl #include #else #undef assert -#define assert(x) /**/ +#define assert(x) /* */ #endif -#define WIN32_LEAN_AND_MEAN -#include #include // offsetof() #include // access() +// WIN32_LEAN_AND_MEAN may be required to prevent inclusion of +#define WIN32_LEAN_AND_MEAN +#include + +#if HAVE_NTDDDISK_H +// i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32 +// (Missing: FILE_DEVICE_SCSI) +#include +#include +#include +#include +#elif HAVE_DDK_NTDDDISK_H +// older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc +// (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI) +#include +#include +#include +#else +// MSVC10, older MinGW +// (Missing: IOCTL_SCSI_MINIPORT_*) +#include +#include +#endif + +#ifndef _WIN32 +// csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin +#define _WIN32 +#endif + +// CSMI support +#include "csmisas.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) +#else +#define SELECT_WIN_32_64(x32, x64) (x64) +#endif + +const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3923 2014-06-25 17:10:46Z chrfranke $"; + +///////////////////////////////////////////////////////////////////////////// +// Windows I/O-controls, some declarations are missing in the include files + +extern "C" { + +// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) + +ASSERT_CONST(SMART_GET_VERSION, 0x074080); +ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084); +ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088); +ASSERT_SIZEOF(GETVERSIONINPARAMS, 24); +ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1); +ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1); + + +// IDE PASS THROUGH (2000, XP, undocumented) + +#ifndef IOCTL_IDE_PASS_THROUGH + +#define IOCTL_IDE_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#endif // IOCTL_IDE_PASS_THROUGH + +#pragma pack(1) + +typedef struct { + IDEREGS IdeReg; + ULONG DataBufferSize; + UCHAR DataBuffer[1]; +} ATA_PASS_THROUGH; + +#pragma pack() + +ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028); +ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1); + + +// ATA PASS THROUGH (Win2003, XP SP2) + +#ifndef IOCTL_ATA_PASS_THROUGH + +#define IOCTL_ATA_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _ATA_PASS_THROUGH_EX { + USHORT Length; + USHORT AtaFlags; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR ReservedAsUchar; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG ReservedAsUlong; + ULONG_PTR DataBufferOffset; + UCHAR PreviousTaskFile[8]; + UCHAR CurrentTaskFile[8]; +} ATA_PASS_THROUGH_EX; + +#define ATA_FLAGS_DRDY_REQUIRED 0x01 +#define ATA_FLAGS_DATA_IN 0x02 +#define ATA_FLAGS_DATA_OUT 0x04 +#define ATA_FLAGS_48BIT_COMMAND 0x08 +#define ATA_FLAGS_USE_DMA 0x10 +#define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista + +#endif // IOCTL_ATA_PASS_THROUGH + +ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c); +ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48)); + + +// IOCTL_SCSI_PASS_THROUGH[_DIRECT] + +ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004); +ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014); +ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56)); +ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT, SELECT_WIN_32_64(44, 56)); + + +// SMART IOCTL via SCSI MINIPORT ioctl + +#ifndef FILE_DEVICE_SCSI +#define FILE_DEVICE_SCSI 0x001b +#endif + +#ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION + +#define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500) +#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) +#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502) +#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503) +#define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504) +#define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505) +#define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506) +#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507) +#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508) +#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509) +#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a) +#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b) +#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c) + +#endif // IOCTL_SCSI_MINIPORT_SMART_VERSION + +ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008); +ASSERT_SIZEOF(SRB_IO_CONTROL, 28); + + +// IOCTL_STORAGE_QUERY_PROPERTY + +#ifndef IOCTL_STORAGE_QUERY_PROPERTY + +#define IOCTL_STORAGE_QUERY_PROPERTY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) +typedef struct _STORAGE_DEVICE_DESCRIPTOR { + ULONG Version; + ULONG Size; + UCHAR DeviceType; + UCHAR DeviceTypeModifier; + BOOLEAN RemovableMedia; + BOOLEAN CommandQueueing; + ULONG VendorIdOffset; + ULONG ProductIdOffset; + ULONG ProductRevisionOffset; + ULONG SerialNumberOffset; + STORAGE_BUS_TYPE BusType; + ULONG RawPropertiesLength; + UCHAR RawDeviceProperties[1]; +} STORAGE_DEVICE_DESCRIPTOR; -// Needed by '-V' option (CVS versioning) of smartd/smartctl -const char *os_XXXX_c_cvsid="$Id: os_win32.cpp 3050 2010-01-27 19:58:38Z chrfranke $" -ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; +typedef enum _STORAGE_QUERY_TYPE { + PropertyStandardQuery = 0, + PropertyExistsQuery, + PropertyMaskQuery, + PropertyQueryMaxDefined +} STORAGE_QUERY_TYPE; +typedef enum _STORAGE_PROPERTY_ID { + StorageDeviceProperty = 0, + StorageAdapterProperty, + StorageDeviceIdProperty, + StorageDeviceUniqueIdProperty, + StorageDeviceWriteCacheProperty, + StorageMiniportProperty, + StorageAccessAlignmentProperty +} STORAGE_PROPERTY_ID; + +typedef struct _STORAGE_PROPERTY_QUERY { + STORAGE_PROPERTY_ID PropertyId; + STORAGE_QUERY_TYPE QueryType; + UCHAR AdditionalParameters[1]; +} STORAGE_PROPERTY_QUERY; + +#endif // IOCTL_STORAGE_QUERY_PROPERTY + +ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400); +ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3); +ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3); + + +// IOCTL_STORAGE_PREDICT_FAILURE + +ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100); +ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512); + + +// 3ware specific versions of SMART ioctl structs + +#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters + +#pragma pack(1) + +typedef struct _GETVERSIONINPARAMS_EX { + BYTE bVersion; + BYTE bRevision; + BYTE bReserved; + BYTE bIDEDeviceMap; + DWORD fCapabilities; + DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map + WORD wIdentifier; // Vendor specific identifier + WORD wControllerId; // 3ware specific: Controller ID (0,1,...) + ULONG dwReserved[2]; +} GETVERSIONINPARAMS_EX; + +typedef struct _SENDCMDINPARAMS_EX { + DWORD cBufferSize; + IDEREGS irDriveRegs; + BYTE bDriveNumber; + BYTE bPortNumber; // 3ware specific: port number + WORD wIdentifier; // Vendor specific identifier + DWORD dwReserved[4]; + BYTE bBuffer[1]; +} SENDCMDINPARAMS_EX; + +#pragma pack() + +ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS)); +ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS)); + + +// CSMI structs + +ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL)); +ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204); +ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080); +ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168); + +} // extern "C" ///////////////////////////////////////////////////////////////////////////// @@ -112,7 +368,10 @@ private: std::string m_options; bool m_usr_options; // options set by user? bool m_admin; // open with admin access? - int m_drive, m_port; + 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; }; @@ -137,23 +396,76 @@ private: ///////////////////////////////////////////////////////////////////////////// -class win_aspi_device -: public /*implements*/ scsi_device +class csmi_device +: virtual public /*extends*/ smart_device { public: - win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type); + /// Get bitmask of used ports + unsigned get_ports_used(); - virtual bool is_open() const; +protected: + csmi_device() + : smart_device(never_called) + { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); } + + /// Get phy info + bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info); + + /// Select physical drive + bool select_port(int port); + + /// 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; + +private: + CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy +}; + + +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) { } +}; + + +////////////////////////////////////////////////////////////////////// + +class win_csmi_device +: public /*implements*/ csmi_ata_device +{ +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 scsi_pass_through(scsi_cmnd_io * iop); + virtual bool is_open() const; + + bool open_scsi(); + +protected: + virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer, + unsigned csmi_bufsiz); private: - int m_adapter; - unsigned char m_id; + HANDLE m_fh; ///< Controller device handle + int m_port; ///< Port number }; @@ -181,10 +493,49 @@ private: }; +///////////////////////////////////////////////////////////////////////////// +/// Areca RAID support + +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class win_areca_ata_device +: public /*implements*/ areca_ata_device, + public /*extends*/ win_smart_device +{ +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); + +private: + HANDLE m_mutex; +}; + +/////////////////////////////////////////////////////////////////// +// 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; +}; + + ////////////////////////////////////////////////////////////////////// -// Platform specific interfaces +// Platform specific interface -// Common to all windows flavors class win_smart_interface : public /*implements part of*/ smart_interface { @@ -193,64 +544,37 @@ public: virtual std::string get_app_examples(const char * appname); +#ifndef __CYGWIN__ + virtual int64_t get_timer_usec(); +#endif + + virtual bool disable_system_auto_standby(bool disable); + virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); protected: virtual ata_device * get_ata_device(const char * name, const char * type); -//virtual scsi_device * get_scsi_device(const char * name, const char * type); - - virtual smart_device * autodetect_smart_device(const char * name); - - virtual bool ata_scan(smart_device_list & devlist) = 0; - - virtual bool scsi_scan(smart_device_list & devlist) = 0; -}; - -// Win9x/ME reduced functionality -class win9x_smart_interface -: public /*extends*/ win_smart_interface -{ -protected: - virtual scsi_device * get_scsi_device(const char * name, const char * type); - - virtual bool ata_scan(smart_device_list & devlist); - - virtual bool scsi_scan(smart_device_list & devlist); -}; - -// WinNT,2000,XP,... -class winnt_smart_interface -: public /*extends*/ win_smart_interface -{ -protected: virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual smart_device * autodetect_smart_device(const char * name); - virtual bool ata_scan(smart_device_list & devlist); + virtual smart_device * get_custom_smart_device(const char * name, const char * type); - virtual bool scsi_scan(smart_device_list & devlist); + virtual std::string get_valid_custom_dev_types_str(); }; ////////////////////////////////////////////////////////////////////// -// Running on Win9x/ME ? -static inline bool is_win9x() -{ - return !!(GetVersion() & 0x80000000); -} - +#ifndef _WIN64 // Running on 64-bit Windows as 32-bit app ? static bool is_wow64() { - HMODULE hk = GetModuleHandleA("kernel32"); - if (!hk) - return false; BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) = - (BOOL (WINAPI *)(HANDLE, PBOOL))GetProcAddress(hk, "IsWow64Process"); + (BOOL (WINAPI *)(HANDLE, PBOOL)) + GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); if (!IsWow64Process_p) return false; BOOL w64 = FALSE; @@ -258,6 +582,7 @@ static bool is_wow64() return false; return !!w64; } +#endif // _WIN64 // Return info string about build host and OS version std::string win_smart_interface::get_os_version_str() @@ -279,40 +604,63 @@ std::string win_smart_interface::get_os_version_str() return vstr; } - if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff) - return vstr; - - const char * w; - switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) { - case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0: - w = (vi.szCSDVersion[1] == 'B' || - vi.szCSDVersion[1] == 'C' ? "95-osr2" : "95"); break; - case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10: - w = (vi.szCSDVersion[1] == 'A' ? "98se" : "98"); break; - case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me"; break; - //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break; - case VER_PLATFORM_WIN32_NT <<16|0x0400| 0: w = "nt4"; break; - case VER_PLATFORM_WIN32_NT <<16|0x0500| 0: w = "2000"; break; - case VER_PLATFORM_WIN32_NT <<16|0x0500| 1: - w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp" - : "xp-mc"); break; - case VER_PLATFORM_WIN32_NT <<16|0x0500| 2: - w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003" - : "2003r2"); break; - case VER_PLATFORM_WIN32_NT <<16|0x0600| 0: - w = (vi.wProductType == VER_NT_WORKSTATION ? "vista" - : "2008" ); break; - case VER_PLATFORM_WIN32_NT <<16|0x0600| 1: - w = (vi.wProductType == VER_NT_WORKSTATION ? "win7" - : "2008r2"); break; - default: w = 0; break; - } - - const char * w64 = (is_wow64() ? "(64)" : ""); + const char * w = 0; + if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + + if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) { + // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the + // actual OS version, see: + // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + + ULONGLONG major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (unsigned major = vi.dwMajorVersion; major <= 9; major++) { + OSVERSIONINFOEXA vi2; memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major; + if (!VerifyVersionInfo(&vi2, VER_MAJORVERSION, major_equal)) + continue; + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; vi.dwMinorVersion = 0; + } + + ULONGLONG minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (unsigned minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfo(&vi2, VER_MINORVERSION, minor_equal)) + continue; + vi.dwMinorVersion = minor; + break; + } + + break; + } + } + + if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { + bool ws = (vi.wProductType <= VER_NT_WORKSTATION); + switch (vi.dwMajorVersion << 4 | vi.dwMinorVersion) { + case 0x50: w = "2000"; break; + case 0x51: w = "xp"; break; + case 0x52: w = (!GetSystemMetrics(89/*SM_SERVERR2*/) + ? "2003" : "2003r2"); break; + case 0x60: w = (ws ? "vista" : "2008" ); break; + case 0x61: w = (ws ? "win7" : "2008r2"); break; + case 0x62: w = (ws ? "win8" : "2012" ); break; + case 0x63: w = (ws ? "win8.1": "2012r2"); break; + } + } + } + + const char * w64 = ""; +#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); + snprintf(vptr, vlen, "-%s%u.%u%s", + (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"), + (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); else if (vi.wServicePackMinor) snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor); else if (vi.wServicePackMajor) @@ -322,10 +670,36 @@ std::string win_smart_interface::get_os_version_str() 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)) + return -1; + + return (t.QuadPart * 1000000LL) / freq; +} +#endif // __CYGWIN__ + + // Return value for device detection functions enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB }; static win_dev_type get_phy_drive_type(int drive); +static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex); static win_dev_type get_log_drive_type(int drive); static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id); @@ -335,11 +709,11 @@ static const char * ata_get_def_options(void); static int is_permissive() { - if (!con->permissive) { + if (!failuretest_permissive) { pout("To continue, add one or more '-T permissive' options.\n"); return 0; } - con->permissive--; + failuretest_permissive--; return 1; } @@ -364,22 +738,24 @@ static const char * skipdev(const char * 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); } -scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type) +scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type) { - return new win_aspi_device(this, name, type); + return new win_scsi_device(this, name, type); } -scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type) +static int sdxy_to_phydrive(const char (& xy)[2+1]) { - const char * testname = skipdev(name); - if (!strncmp(testname, "scsi", 4)) - return new win_aspi_device(this, name, type); - return new win_scsi_device(this, name, type); + int phydrive = xy[0] - 'a'; + if (xy[1]) + phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a'); + return phydrive; } static win_dev_type get_dev_type(const char * name, int & phydrive) @@ -399,9 +775,9 @@ static win_dev_type get_dev_type(const char * name, int & phydrive) return (type != DEV_UNKNOWN ? type : DEV_SCSI); } - char drive[1+1] = ""; - if (sscanf(name, "sd%1[a-z]", drive) == 1) { - phydrive = drive[0] - 'a'; + char drive[2+1] = ""; + if (sscanf(name, "sd%2[a-z]", drive) == 1) { + phydrive = sdxy_to_phydrive(drive); return get_phy_drive_type(phydrive); } @@ -411,23 +787,69 @@ static win_dev_type get_dev_type(const char * name, int & phydrive) return DEV_UNKNOWN; } +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]; + + 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; + } + + 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; +} + +std::string win_smart_interface::get_valid_custom_dev_types_str() +{ + return "areca,N[/E]"; +} + + smart_device * win_smart_interface::autodetect_smart_device(const char * name) { const char * testname = skipdev(name); - if (!strncmp(testname, "hd", 2)) + if (str_starts_with(testname, "hd")) return new win_ata_device(this, name, ""); - if (!strncmp(testname, "scsi", 4)) - return new win_aspi_device(this, name, ""); - if (!strncmp(testname, "tw_cli", 6)) + + if (str_starts_with(testname, "tw_cli")) return new win_tw_cli_device(this, name, ""); - return 0; -} -smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) -{ - smart_device * dev = win_smart_interface::autodetect_smart_device(name); - if (dev) - return dev; + if (str_starts_with(testname, "csmi")) + return new win_csmi_device(this, name, ""); int phydrive = -1; win_dev_type type = get_dev_type(name, phydrive); @@ -456,7 +878,8 @@ smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) } -// makes a list of ATA or SCSI devices for the DEVICESCAN directive +// Scan for devices + bool win_smart_interface::scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern /* = 0*/) { @@ -465,14 +888,143 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist, return false; } - if (!type || !strcmp(type, "ata")) { - if (!ata_scan(devlist)) - return false; + // 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 (!type || !strcmp(type, "scsi")) { - if (!scsi_scan(devlist)) + // 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[,pd], scsi[,pd], usb[,pd], csmi, pd", type); return false; + } + } + + char name[20]; + + if (ata || scsi || usb) { + // Scan up to 128 drives and 2 3ware controllers + const int max_raid = 2; + bool raid_seen[max_raid] = {false, false}; + + for (int i = 0; i < 128; i++) { + if (pd) + snprintf(name, sizeof(name), "/dev/pd%d", i); + else if (i + 'a' <= 'z') + snprintf(name, sizeof(name), "/dev/sd%c", i + 'a'); + else + snprintf(name, sizeof(name), "/dev/sd%c%c", + i / ('z'-'a'+1) - 1 + 'a', + i % ('z'-'a'+1) + 'a'); + + GETVERSIONINPARAMS_EX vers_ex; + + switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) { + case DEV_ATA: + // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA + if (!ata) + continue; + + // 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)) { + snprintf(name+len, sizeof(name)-1-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; + + case DEV_SCSI: + // STORAGE_QUERY_PROPERTY returned SCSI/SAS/... + if (!scsi) + continue; + devlist.push_back( new win_scsi_device(this, name, "scsi") ); + break; + + case DEV_USB: + // STORAGE_QUERY_PROPERTY returned USB + if (!usb) + continue; + { + // TODO: Use common function for this and autodetect_smart_device() + // Get USB bridge ID + unsigned short vendor_id = 0, product_id = 0; + if (!get_usb_id(i, vendor_id, product_id)) + continue; + // Get type name for this ID + const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id); + if (!usbtype) + continue; + // Return SAT/USB device for this type + ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, "")); + if (!dev) + continue; + devlist.push_back(dev); + } + break; + + default: + // Unknown type + break; + } + } + } + + 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; + + unsigned ports_used = test_dev.get_ports_used(); + if (!ports_used) + continue; + + for (int pi = 0; pi < 32; pi++) { + if (!(ports_used & (1 << pi))) + continue; + snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi); + devlist.push_back( new win_csmi_device(this, name, "ata") ); + } + } } return true; } @@ -484,172 +1036,71 @@ 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" + " 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/hda (Executes extended disk self-test)\n\n" - " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\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/scsi21\n" - " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n" " smartctl -a /dev/sda\n" - " (Prints all information for SCSI disk on PhysicalDrive 0)\n" + " (Prints all information for disk on PhysicalDrive 0)\n" " smartctl -a /dev/pd3\n" - " (Prints all information for SCSI disk on PhysicalDrive 3)\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, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n" - " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\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() ); } -///////////////////////////////////////////////////////////////////////////// -// ATA Interface -///////////////////////////////////////////////////////////////////////////// - -// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection) - -#define FILE_READ_ACCESS 0x0001 -#define FILE_WRITE_ACCESS 0x0002 -#define METHOD_BUFFERED 0 -#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) - -#define FILE_DEVICE_DISK 7 -#define IOCTL_DISK_BASE FILE_DEVICE_DISK - -#define SMART_GET_VERSION \ - CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) +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; + } + } -#define SMART_SEND_DRIVE_COMMAND \ - CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0))) + return set_err(ENOSYS); + return true; +} -#define SMART_RCV_DRIVE_DATA \ - CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) -ASSERT_CONST(SMART_GET_VERSION , 0x074080); -ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084); -ASSERT_CONST(SMART_RCV_DRIVE_DATA , 0x07c088); +///////////////////////////////////////////////////////////////////////////// +// ATA Interface +///////////////////////////////////////////////////////////////////////////// #define SMART_CYL_LOW 0x4F #define SMART_CYL_HI 0xC2 - -#pragma pack(1) - -typedef struct _GETVERSIONOUTPARAMS { - UCHAR bVersion; - UCHAR bRevision; - UCHAR bReserved; - UCHAR bIDEDeviceMap; - ULONG fCapabilities; - ULONG dwReserved[4]; -} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS; - -ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24); - - -#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters - -typedef struct _GETVERSIONINPARAMS_EX { - BYTE bVersion; - BYTE bRevision; - BYTE bReserved; - BYTE bIDEDeviceMap; - DWORD fCapabilities; - DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map - WORD wIdentifier; // Vendor specific identifier - WORD wControllerId; // 3ware specific: Controller ID (0,1,...) - ULONG dwReserved[2]; -} GETVERSIONINPARAMS_EX, *PGETVERSIONINPARAMS_EX, *LPGETVERSIONINPARAMS_EX; - -ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONOUTPARAMS)); - - -typedef struct _IDEREGS { - UCHAR bFeaturesReg; - UCHAR bSectorCountReg; - UCHAR bSectorNumberReg; - UCHAR bCylLowReg; - UCHAR bCylHighReg; - UCHAR bDriveHeadReg; - UCHAR bCommandReg; - UCHAR bReserved; -} IDEREGS, *PIDEREGS, *LPIDEREGS; - -typedef struct _SENDCMDINPARAMS { - ULONG cBufferSize; - IDEREGS irDriveRegs; - UCHAR bDriveNumber; - UCHAR bReserved[3]; - ULONG dwReserved[4]; - UCHAR bBuffer[1]; -} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS; - -ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1); - -typedef struct _SENDCMDINPARAMS_EX { - DWORD cBufferSize; - IDEREGS irDriveRegs; - BYTE bDriveNumber; - BYTE bPortNumber; // 3ware specific: port number - WORD wIdentifier; // Vendor specific identifier - DWORD dwReserved[4]; - BYTE bBuffer[1]; -} SENDCMDINPARAMS_EX, *PSENDCMDINPARAMS_EX, *LPSENDCMDINPARAMS_EX; - -ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS)); - - -/* DRIVERSTATUS.bDriverError constants (just for info, not used) -#define SMART_NO_ERROR 0 -#define SMART_IDE_ERROR 1 -#define SMART_INVALID_FLAG 2 -#define SMART_INVALID_COMMAND 3 -#define SMART_INVALID_BUFFER 4 -#define SMART_INVALID_DRIVE 5 -#define SMART_INVALID_IOCTL 6 -#define SMART_ERROR_NO_MEM 7 -#define SMART_INVALID_REGISTER 8 -#define SMART_NOT_SUPPORTED 9 -#define SMART_NO_IDE_DEVICE 10 -*/ - -typedef struct _DRIVERSTATUS { - UCHAR bDriverError; - UCHAR bIDEError; - UCHAR bReserved[2]; - ULONG dwReserved[2]; -} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS; - -typedef struct _SENDCMDOUTPARAMS { - ULONG cBufferSize; - DRIVERSTATUS DriverStatus; - UCHAR bBuffer[1]; -} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS; - -ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1); - -#pragma pack() - - -///////////////////////////////////////////////////////////////////////////// - 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); + (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) @@ -666,27 +1117,27 @@ static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro) static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0) { - GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers)); + GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers)); const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers; DWORD num_out; if (!DeviceIoControl(hdevice, SMART_GET_VERSION, NULL, 0, &vers, sizeof(vers), &num_out, NULL)) { - if (con->reportataioctl) - pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError()); + if (ata_debugmode) + pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } - assert(num_out == sizeof(GETVERSIONOUTPARAMS)); + assert(num_out == sizeof(GETVERSIONINPARAMS)); - if (con->reportataioctl > 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 (ata_debugmode > 1) { + pout(" SMART_GET_VERSION suceeded, 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%08lx\n", - vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx); + pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n", + vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx); } if (ata_version_ex) @@ -699,7 +1150,7 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version // call SMART_* ioctl -static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port) +static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port) { SENDCMDINPARAMS inpar; SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar; @@ -712,9 +1163,14 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u 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; + + // Older drivers may require bits 5 and 7 set + // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete + inpar.irDriveRegs.bDriveHeadReg |= 0xa0; + + // Drive number 0-3 was required on Win9x/ME only + //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4; + //inpar.bDriveNumber = drive; if (port >= 0) { // Set RAID port @@ -743,9 +1199,9 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u 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() + // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through() long err = GetLastError(); - if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) { + if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) { pout(" %s failed, Error=%ld\n", name, err); print_ide_regs_io(regs, NULL); } @@ -759,7 +1215,7 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u outpar = (const SENDCMDOUTPARAMS *)outbuf; if (outpar->DriverStatus.bDriverError) { - if (con->reportataioctl) { + 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); @@ -768,58 +1224,34 @@ static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, u return -1; } - if (con->reportataioctl > 1) { - pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name, - num_out, outpar->cBufferSize); + if (ata_debugmode > 1) { + pout(" %s suceeded, 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)); } if (datasize) - memcpy(data, outpar->bBuffer, 512); - else if (regs->bFeaturesReg == ATA_SMART_STATUS) { - if (nonempty(const_cast(outpar->bBuffer), sizeof(IDEREGS))) - *regs = *(const IDEREGS *)(outpar->bBuffer); - else { // Workaround for driver not returning regs - if (con->reportataioctl) - pout(" WARNING: driver does not return ATA registers in output buffer!\n"); - *regs = inpar.irDriveRegs; - } - } - - 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 - -#define FILE_DEVICE_CONTROLLER 4 -#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER - -#define IOCTL_IDE_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028); - -#pragma pack(1) - -typedef struct { - IDEREGS IdeReg; - ULONG DataBufferSize; - UCHAR DataBuffer[1]; -} ATA_PASS_THROUGH; - -ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1); + 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; + } + } -#pragma pack() + 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 static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) { @@ -845,7 +1277,7 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH, buf, size, buf, size, &num_out, NULL)) { long err = GetLastError(); - if (con->reportataioctl) { + if (ata_debugmode) { pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } @@ -856,7 +1288,7 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u // Check ATA status if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) { - if (con->reportataioctl) { + if (ata_debugmode) { pout(" IOCTL_IDE_PASS_THROUGH command failed:\n"); print_ide_regs_io(regs, &buf->IdeReg); } @@ -869,9 +1301,9 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u if (datasize) { if ( num_out != size || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) { - if (con->reportataioctl) { - pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n", - num_out, buf->DataBufferSize); + 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); @@ -881,9 +1313,9 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u memcpy(data, buf->DataBuffer, datasize); } - if (con->reportataioctl > 1) { - pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n", - num_out, buf->DataBufferSize); + if (ata_debugmode > 1) { + pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n", + (unsigned)num_out, (unsigned)buf->DataBufferSize); print_ide_regs_io(regs, &buf->IdeReg); } *regs = buf->IdeReg; @@ -895,41 +1327,8 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u ///////////////////////////////////////////////////////////////////////////// - // ATA PASS THROUGH (Win2003, XP SP2) -#define IOCTL_ATA_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c); - -typedef struct _ATA_PASS_THROUGH_EX { - USHORT Length; - USHORT AtaFlags; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR ReservedAsUchar; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG ReservedAsUlong; - ULONG/*_PTR*/ DataBufferOffset; - UCHAR PreviousTaskFile[8]; - UCHAR CurrentTaskFile[8]; -} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX; - -ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40); - -#define ATA_FLAGS_DRDY_REQUIRED 0x01 -#define ATA_FLAGS_DATA_IN 0x02 -#define ATA_FLAGS_DATA_OUT 0x04 -#define ATA_FLAGS_48BIT_COMMAND 0x08 -#define ATA_FLAGS_USE_DMA 0x10 -#define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista - - -///////////////////////////////////////////////////////////////////////////// - // Warning: // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data // transfer per command. Therefore, multi-sector transfers are only supported @@ -999,7 +1398,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH, &ab, size, &ab, size, &num_out, NULL)) { long err = GetLastError(); - if (con->reportataioctl) { + if (ata_debugmode) { pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } @@ -1009,7 +1408,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev // Check ATA status if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) { - if (con->reportataioctl) { + if (ata_debugmode) { pout(" IOCTL_ATA_PASS_THROUGH command failed:\n"); print_ide_regs_io(regs, ctfregs); } @@ -1021,8 +1420,8 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev if (datasize > 0) { if ( num_out != size || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) { - if (con->reportataioctl) { - pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out); + if (ata_debugmode) { + pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } errno = EIO; @@ -1031,8 +1430,8 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev memcpy(data, ab.ucDataBuf, datasize); } - if (con->reportataioctl > 1) { - pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out); + if (ata_debugmode > 1) { + pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, ctfregs); } *regs = *ctfregs; @@ -1044,122 +1443,6 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev ///////////////////////////////////////////////////////////////////////////// - -// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only) - -#define IOCTL_SCSI_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004); - -#define SCSI_IOCTL_DATA_OUT 0 -#define SCSI_IOCTL_DATA_IN 1 -#define SCSI_IOCTL_DATA_UNSPECIFIED 2 -// undocumented SCSI opcode to for ATA passthrough -#define SCSIOP_ATA_PASSTHROUGH 0xCC - -typedef struct _SCSI_PASS_THROUGH { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG/*_PTR*/ DataBufferOffset; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; - -ASSERT_SIZEOF(SCSI_PASS_THROUGH, 44); - - -///////////////////////////////////////////////////////////////////////////// - -static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize) -{ - typedef struct { - SCSI_PASS_THROUGH spt; - ULONG Filler; - UCHAR ucSenseBuf[32]; - UCHAR ucDataBuf[512]; - } SCSI_PASS_THROUGH_WITH_BUFFERS; - - SCSI_PASS_THROUGH_WITH_BUFFERS sb; - IDEREGS * cdbregs; - unsigned int size; - DWORD num_out; - const unsigned char magic = 0xcf; - - memset(&sb, 0, sizeof(sb)); - sb.spt.Length = sizeof(SCSI_PASS_THROUGH); - //sb.spt.PathId = 0; - sb.spt.TargetId = 1; - //sb.spt.Lun = 0; - sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24; - sb.spt.TimeOutValue = 10; - sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf); - size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf); - sb.spt.DataBufferOffset = size; - - if (datasize) { - if (datasize > sizeof(sb.ucDataBuf)) { - errno = EINVAL; - return -1; - } - sb.spt.DataIn = SCSI_IOCTL_DATA_IN; - sb.spt.DataTransferLength = datasize; - size += datasize; - sb.ucDataBuf[0] = magic; - } - else { - sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; - //sb.spt.DataTransferLength = 0; - } - - // Use pseudo SCSI command followed by registers - sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH; - cdbregs = (IDEREGS *)(sb.spt.Cdb+2); - *cdbregs = *regs; - - if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH, - &sb, size, &sb, size, &num_out, NULL)) { - long err = GetLastError(); - if (con->reportataioctl) - pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err); - errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); - return -1; - } - - // Cannot check ATA status, because command does not return IDEREGS - - // Check and copy data - if (datasize) { - if ( num_out != size - || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) { - if (con->reportataioctl) { - pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out); - print_ide_regs_io(regs, NULL); - } - errno = EIO; - return -1; - } - memcpy(data, sb.ucDataBuf, datasize); - } - - if (con->reportataioctl > 1) { - pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out); - print_ide_regs_io(regs, NULL); - } - return 0; -} - - -///////////////////////////////////////////////////////////////////////////// - // SMART IOCTL via SCSI MINIPORT ioctl // This function is handled by ATAPI port driver (atapi.sys) or by SCSI @@ -1167,40 +1450,6 @@ static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char // 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) -#define IOCTL_SCSI_MINIPORT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008); - -typedef struct _SRB_IO_CONTROL { - ULONG HeaderLength; - UCHAR Signature[8]; - ULONG Timeout; - ULONG ControlCode; - ULONG ReturnCode; - ULONG Length; -} SRB_IO_CONTROL, *PSRB_IO_CONTROL; - -ASSERT_SIZEOF(SRB_IO_CONTROL, 28); - -#define FILE_DEVICE_SCSI 0x001b - -#define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500) -#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) -#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502) -#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503) -#define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504) -#define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505) -#define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506) -#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507) -#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508) -#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509) -#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a) -#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b) -#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c) - -///////////////////////////////////////////////////////////////////////////// - static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize) { // Select code @@ -1284,7 +1533,7 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &sb, size, &sb, size, &num_out, NULL)) { long err = GetLastError(); - if (con->reportataioctl) { + if (ata_debugmode) { pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err); print_ide_regs_io(regs, NULL); } @@ -1294,8 +1543,8 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha // Check result if (sb.srbc.ReturnCode) { - if (con->reportataioctl) { - pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, 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; @@ -1303,7 +1552,7 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha } if (sb.params.out.DriverStatus.bDriverError) { - if (con->reportataioctl) { + 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); @@ -1312,9 +1561,9 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha return -1; } - if (con->reportataioctl > 1) { - pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name, - num_out, sb.params.out.cBufferSize); + if (ata_debugmode > 1) { + pout(" IOCTL_SCSI_MINIPORT_%s suceeded, 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)); } @@ -1322,7 +1571,7 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha if (datasize > 0) memcpy(data, sb.params.out.bBuffer, datasize); else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS) - *regs = *(const IDEREGS *)(sb.params.out.bBuffer); + memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS)); return 0; } @@ -1346,7 +1595,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d return -1; } memset(&sb, 0, sizeof(sb)); - strcpy((char *)sb.srbc.Signature, "<3ware>"); + strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature)); sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL); sb.srbc.Timeout = 60; // seconds sb.srbc.ControlCode = 0xA0000000; @@ -1359,7 +1608,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) { long err = GetLastError(); - if (con->reportataioctl) { + if (ata_debugmode) { pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err); print_ide_regs_io(regs, NULL); } @@ -1368,8 +1617,8 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d } if (sb.srbc.ReturnCode) { - if (con->reportataioctl) { - pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", 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; @@ -1380,8 +1629,8 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d if (datasize > 0) memcpy(data, sb.buffer, datasize); - if (con->reportataioctl > 1) { - pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out); + if (ata_debugmode > 1) { + pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out); print_ide_regs_io(regs, &sb.regs); } *regs = sb.regs; @@ -1399,7 +1648,7 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice) { SRB_IO_CONTROL srbc; memset(&srbc, 0, sizeof(srbc)); - strcpy((char *)srbc.Signature, "<3ware>"); + strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature)); srbc.HeaderLength = sizeof(SRB_IO_CONTROL); srbc.Timeout = 60; // seconds srbc.ControlCode = 0xCC010014; @@ -1410,18 +1659,18 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice) if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT, &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) { long err = GetLastError(); - if (con->reportataioctl) + 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 (con->reportataioctl) - pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", 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 (con->reportataioctl > 1) + if (ata_debugmode > 1) pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n"); return 0; } @@ -1566,7 +1815,7 @@ bool win_tw_cli_device::open() // 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 (con->reportataioctl > 1) + if (ata_debugmode > 1) pout("%s: Run: \"%s\"\n", name, cmd); size = run_cmd(cmd, buffer, sizeof(buffer)); } @@ -1574,7 +1823,7 @@ bool win_tw_cli_device::open() return set_err(EINVAL); } - if (con->reportataioctl > 1) + if (ata_debugmode > 1) pout("%s: Read %d bytes\n", name, size); if (size <= 0) return set_err(ENOENT); @@ -1582,7 +1831,7 @@ bool win_tw_cli_device::open() return set_err(EIO); buffer[size] = 0; - if (con->reportataioctl > 1) + if (ata_debugmode > 1) pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":"")); // Fake identify sector @@ -1599,7 +1848,6 @@ bool win_tw_cli_device::open() id->words047_079[60-47] = (unsigned short)(nblocks ); // secs_16 id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32 } - id->major_rev_num = 0x1<<3; // ATA-3 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 @@ -1641,7 +1889,7 @@ bool win_tw_cli_device::open() // Show tw_cli error message err++; err[strcspn(err, "\r\n")] = 0; - return set_err(EIO, err); + return set_err(EIO, "%s", err); } return set_err(EIO); } @@ -1674,20 +1922,6 @@ int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*se break; memcpy(data, &m_smart_buf, 512); return 0; - case READ_THRESHOLDS: - if (!m_smart_valid) - break; - // Fake zero thresholds - { - const ata_smart_values * sv = &m_smart_buf; - ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data; - memset(tr, 0, 512); - // TODO: Indicate missing thresholds in ataprint.cpp:PrintSmartAttribWithThres() - // (ATA_SMART_READ_THRESHOLDS is marked obsolete since ATA-5) - for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) - tr->chksum -= tr->thres_entries[i].id = sv->vendor_attributes[i].id; - } - return 0; case ENABLE: case STATUS: case STATUS_CHECK: // Fake "good" SMART status @@ -1702,77 +1936,8 @@ int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*se ///////////////////////////////////////////////////////////////////////////// - // IOCTL_STORAGE_QUERY_PROPERTY -#define FILE_DEVICE_MASS_STORAGE 0x0000002d -#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE -#define FILE_ANY_ACCESS 0 - -#define IOCTL_STORAGE_QUERY_PROPERTY \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - -typedef enum _STORAGE_BUS_TYPE { - BusTypeUnknown = 0x00, - BusTypeScsi = 0x01, - BusTypeAtapi = 0x02, - BusTypeAta = 0x03, - BusType1394 = 0x04, - BusTypeSsa = 0x05, - BusTypeFibre = 0x06, - BusTypeUsb = 0x07, - BusTypeRAID = 0x08, - BusTypeiScsi = 0x09, - BusTypeSas = 0x0A, - BusTypeSata = 0x0B, - BusTypeSd = 0x0C, - BusTypeMmc = 0x0D, - BusTypeMax = 0x0E, - BusTypeMaxReserved = 0x7F -} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; - -typedef struct _STORAGE_DEVICE_DESCRIPTOR { - ULONG Version; - ULONG Size; - UCHAR DeviceType; - UCHAR DeviceTypeModifier; - BOOLEAN RemovableMedia; - BOOLEAN CommandQueueing; - ULONG VendorIdOffset; - ULONG ProductIdOffset; - ULONG ProductRevisionOffset; - ULONG SerialNumberOffset; - STORAGE_BUS_TYPE BusType; - ULONG RawPropertiesLength; - UCHAR RawDeviceProperties[1]; -} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; - -typedef enum _STORAGE_QUERY_TYPE { - PropertyStandardQuery = 0, - PropertyExistsQuery, - PropertyMaskQuery, - PropertyQueryMaxDefined -} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; - -typedef enum _STORAGE_PROPERTY_ID { - StorageDeviceProperty = 0, - StorageAdapterProperty, - StorageDeviceIdProperty, - StorageDeviceUniqueIdProperty, - StorageDeviceWriteCacheProperty, - StorageMiniportProperty, - StorageAccessAlignmentProperty -} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; - -typedef struct _STORAGE_PROPERTY_QUERY { - STORAGE_PROPERTY_ID PropertyId; - STORAGE_QUERY_TYPE QueryType; - UCHAR AdditionalParameters[1]; -} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; - - -///////////////////////////////////////////////////////////////////////////// - union STORAGE_DEVICE_DESCRIPTOR_DATA { STORAGE_DEVICE_DESCRIPTOR desc; char raw[256]; @@ -1789,22 +1954,22 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) { - if (con->reportataioctl > 1 || con->reportscsiioctl > 1) - pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError()); + if (ata_debugmode > 1 || scsi_debugmode > 1) + pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } - if (con->reportataioctl > 1 || con->reportscsiioctl > 1) { + 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 : ""), - (data->desc.ProductIdOffset ? data->raw+data->desc.ProductIdOffset : ""), - (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : ""), + (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 ); } @@ -1813,23 +1978,8 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO ///////////////////////////////////////////////////////////////////////////// - // IOCTL_STORAGE_PREDICT_FAILURE -#define IOCTL_STORAGE_PREDICT_FAILURE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS) - -typedef struct _STORAGE_PREDICT_FAILURE { - ULONG PredictFailure; - UCHAR VendorSpecific[512]; -} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE; - -ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512); - - -///////////////////////////////////////////////////////////////////////////// - - // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value // or -1 on error, opionally return VendorSpecific data. // (This works without admin rights) @@ -1842,17 +1992,17 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0) DWORD num_out; if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE, 0, 0, &pred, sizeof(pred), &num_out, NULL)) { - if (con->reportataioctl > 1) - pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError()); + if (ata_debugmode > 1) + pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError()); errno = ENOSYS; return -1; } - if (con->reportataioctl > 1) { + if (ata_debugmode > 1) { pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n" - " PredictFailure: 0x%08lx\n" + " PredictFailure: 0x%08x\n" " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n", - pred.PredictFailure, + (unsigned)pred.PredictFailure, pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2], pred.VendorSpecific[sizeof(pred.VendorSpecific)-1] ); @@ -1865,31 +2015,52 @@ static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0) ///////////////////////////////////////////////////////////////////////////// +// 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) { - // Try SMART_GET_VERSION first to detect ATA SMART support - // for drivers reporting BusTypeScsi (3ware) - if (admin && smart_get_version(hdevice, ata_version_ex) >= 0) - return DEV_ATA; - // Get BusType from device descriptor STORAGE_DEVICE_DESCRIPTOR_DATA data; if (storage_query_property_ioctl(hdevice, &data)) return DEV_UNKNOWN; - switch (data.desc.BusType) { + // Newer BusType* values are missing in older includes + switch ((int)data.desc.BusType) { case BusTypeAta: - case BusTypeSata: + case 0x0b: // BusTypeSata if (ata_version_ex) memset(ata_version_ex, 0, sizeof(*ata_version_ex)); return DEV_ATA; + case BusTypeScsi: - case BusTypeiScsi: - case BusTypeSas: + 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; + + case 0x09: // BusTypeiScsi + case 0x0a: // BusTypeSas return DEV_SCSI; + case BusTypeUsb: return DEV_USB; + default: return DEV_UNKNOWN; } @@ -1909,7 +2080,7 @@ static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX if (h == INVALID_HANDLE_VALUE) return DEV_UNKNOWN; } - if (con->reportataioctl > 1 || con->reportscsiioctl > 1) + 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); @@ -1945,196 +2116,204 @@ static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device return -1; memset(id, 0, sizeof(*id)); - if (data.desc.ProductIdOffset) - copy_swapped(id->model, data.raw+data.desc.ProductIdOffset, sizeof(id->model)); + + // Some drivers split ATA model string into VendorId and ProductId, + // others return it as ProductId only. + char model[sizeof(id->model) + 1] = ""; + + 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]; + } + + 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 (data.desc.ProductRevisionOffset) copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev)); - id->major_rev_num = 0x1<<3; // ATA-3 + 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; } - -///////////////////////////////////////////////////////////////////////////// -// USB ID detection using WMI - -// Run a command, split stdout into lines. -// Return number of lines read, -1 on error. -static int run_cmd(std::vector & lines, const char * cmd, ...) +// Get Serial Number in IDENTIFY from WMI +static bool get_serial_from_wmi(int drive, ata_identify_device * id) { - lines.clear(); - - va_list ap; va_start(ap, cmd); - std::string cmdline = vstrprintf(cmd, ap); - va_end(ap); + bool debug = (ata_debugmode > 1); - if (con->reportscsiioctl > 1) - pout("Run: \"%s\"\n", cmdline.c_str()); - - char buffer[16*1024]; - int size = run_cmd(cmdline.c_str(), buffer, sizeof(buffer)); + wbem_services ws; + if (!ws.connect()) { + if (debug) + pout("WMI connect failed\n"); + return false; + } - if (con->reportscsiioctl > 1) - pout("Read %d bytes\n", size); - if (!(0 < size && size < (int)sizeof(buffer)-1)) - return -1; + wbem_object wo; + if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE " + "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive)) + return false; - buffer[size] = 0; + 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()); - for (int i = 0; buffer[i]; ) { - int len = strcspn(buffer+i, "\r\n"); - lines.push_back(std::string(buffer+i, len)); - i += len; - i += strspn(buffer+i, "\r\n"); - } - if (con->reportscsiioctl > 1) { - for (unsigned i = 0; i < lines.size(); i++) - printf("'%s'\n", lines[i].c_str()); - } - return lines.size(); + copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no)); + return true; } -// Quote string for WMI -static std::string wmi_quote(const char * s, int len) -{ - std::string r; - for (int i = 0; i < len; i++) { - char c = s[i]; - if (c == '\\') - r += '\\'; - r += c; - } - return r; -} + +///////////////////////////////////////////////////////////////////////////// +// 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 debug = (scsi_debugmode > 1); + + wbem_services ws; + if (!ws.connect()) { + if (debug) + pout("WMI connect failed\n"); + return false; + } + // Get device name - std::vector result; - if (run_cmd(result, - "wmic PATH Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\" GET Model", - drive) != 2) + wbem_object wo; + if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive)) return false; - std::string name = result[1]; + std::string name = wo.get_str("Model"); + if (debug) + pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str()); // Get USB_CONTROLLER -> DEVICE associations - std::vector assoc; - int n = run_cmd(assoc, "wmic PATH Win32_USBControllerDevice GET Antecedent,Dependent"); - if (n < 2) + wbem_enumerator we; + if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice")) return false; - regular_expression regex("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"(USBSTOR\\\\[^\"]*)\" *$", - REG_EXTENDED); - if (regex.empty()) // TODO: throw in constructor? - 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; - int usbstoridx = -1; - std::string usbcontr; - for (int i = 2; i < n; i++) { - // Find next 'USB_CONTROLLER USBSTORAGE_DEVICE' pair - regmatch_t match[3]; - const char * s = assoc[i].c_str(); - if (!regex.execute(s, 3, match)) - continue; + const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED); + + while (we.next(wo)) { + prev_ant = ant; + // Find next 'USB_CONTROLLER, DEVICE' pair + ant = wo.get_str("Antecedent"); + dep = wo.get_str("Dependent"); - // USBSTOR device found, compare Name - if (run_cmd(result, - "wmic PATH Win32_PnPEntity WHERE DeviceID=\"%s\" GET Name", - wmi_quote(s + match[2].rm_so, match[2].rm_eo - match[2].rm_so).c_str() - ) != 2) + if (debug && ant != prev_ant) + pout(" %s:\n", ant.c_str()); + + // 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; - if (result[1] != name) + } + + 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()); + + // 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"); - // Name must be uniqe - if (usbstoridx >= 0) - return false; + // Continue if not name of physical disk drive + if (name2 != name) { + if (debug) + pout(" +---> (\"%s\")\n", name2.c_str()); + continue; + } - usbstoridx = i; - usbcontr.assign(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so); - } + // 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; + } - // Found ? - if (usbstoridx <= 0) - 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; + } + } - // The entry preceding USBSTOR should be the USB bridge device - regex.compile("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"USB\\\\VID_(....&PID_....)[^\"]*\" *$", - REG_EXTENDED); - if (regex.empty()) - return false; - regmatch_t match[3]; - const char * s = assoc[usbstoridx-1].c_str(); - if (!regex.execute(s, 3, match)) - 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); - // Both devices must be associated to same controller - if (usbcontr != std::string(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so)) - return false; + // Continue to check for duplicate names ... + } + else { + if (debug) + pout(" | \"%s\"\n", devid.c_str()); + } + } - // Parse USB ID - int nc = -1; - if (!(sscanf(s + match[2].rm_so, "%4hx&PID_%4hx%n", - &vendor_id, &product_id, &nc) == 2 && nc == 4+5+4)) + if (!usb_venid) return false; - if (con->reportscsiioctl > 1) - pout("USB ID = 0x%04x:0x%04x\n", vendor_id, product_id); + vendor_id = usb_venid; + product_id = usb_proid; + return true; } ///////////////////////////////////////////////////////////////////////////// -// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003) +// Call GetDevicePowerState() // returns: 1=active, 0=standby, -1=error // (This would also work for SCSI drives) static int get_device_power_state(HANDLE hdevice) -{ - static HINSTANCE h_kernel_dll = 0; -#ifdef __CYGWIN__ - static DWORD kernel_dll_pid = 0; -#endif - static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0; - - BOOL state = TRUE; - - if (!GetDevicePowerState_p -#ifdef __CYGWIN__ - || kernel_dll_pid != GetCurrentProcessId() // detect fork() -#endif - ) { - if (h_kernel_dll == INVALID_HANDLE_VALUE) { - errno = ENOSYS; - return -1; - } - if (!(h_kernel_dll = LoadLibraryA("KERNEL32.DLL"))) { - pout("Cannot load KERNEL32.DLL, Error=%ld\n", GetLastError()); - h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE; - errno = ENOSYS; - return -1; - } - if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *)) - GetProcAddress(h_kernel_dll, "GetDevicePowerState"))) { - if (con->reportataioctl) - pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError()); - FreeLibrary(h_kernel_dll); - h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE; - errno = ENOSYS; - return -1; - } -#ifdef __CYGWIN__ - kernel_dll_pid = GetCurrentProcessId(); -#endif - } - - if (!GetDevicePowerState_p(hdevice, &state)) { +{ + BOOL state = TRUE; + if (!GetDevicePowerState(hdevice, &state)) { long err = GetLastError(); - if (con->reportataioctl) + 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, @@ -2142,7 +2321,7 @@ static int get_device_power_state(HANDLE hdevice) return -1; } - if (con->reportataioctl > 1) + if (ata_debugmode > 1) pout(" GetDevicePowerState() succeeded, state=%d\n", state); return state; } @@ -2150,66 +2329,12 @@ static int get_device_power_state(HANDLE hdevice) ///////////////////////////////////////////////////////////////////////////// -// Print SMARTVSD error message, return errno - -static int smartvsd_error() -{ - char path[MAX_PATH]; - unsigned len; - if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2)) - return ENOENT; - // SMARTVSD.VXD present? - strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD"); - if (!access(path, 0)) { - // Yes, standard IDE driver used? - HANDLE h; - if ( (h = CreateFileA("\\\\.\\ESDI_506", - GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE - && GetLastError() == ERROR_FILE_NOT_FOUND ) { - pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n"); - return ENOENT; - } - else { - if (h != INVALID_HANDLE_VALUE) // should not happen - CloseHandle(h); - pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n"); - return ENOSYS; - } - } - else { - strcpy(path+len, "\\SMARTVSD.VXD"); - if (!access(path, 0)) { - // Some Windows versions install SMARTVSD.VXD in SYSTEM directory - // (http://support.microsoft.com/kb/265854/en-us). - path[len] = 0; - pout("SMART driver is not properly installed,\n" - " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n" - " and reboot Windows.\n", path, path); - } - else { - // Some Windows versions do not provide SMARTVSD.VXD - // (http://support.microsoft.com/kb/199886/en-us). - path[len] = 0; - pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path); - } - return ENOSYS; - } -} - - // Get default ATA device options static const char * ata_get_def_options() { - 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_* + return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH, + // STORAGE_*, SCSI_MINIPORT_* } @@ -2241,7 +2366,9 @@ win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, co : smart_device(intf, dev_name, "ata", req_type), m_usr_options(false), m_admin(false), - m_drive(0), + m_phydrive(-1), + m_id_is_cached(false), + m_is_3ware(false), m_port(-1), m_smartver_state(0) { @@ -2257,18 +2384,18 @@ win_ata_device::~win_ata_device() throw() bool win_ata_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]([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); } - // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options + // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1; 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); + 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); } // pd,N => Physical drive , RAID port N int phydrive = -1; port = ~0; n1 = -1; n2 = -1; @@ -2288,29 +2415,25 @@ bool win_ata_device::open() bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port) { - // path depends on Windows Version - bool win9x = is_win9x(); // TODO: Member variable + m_phydrive = -1; 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') + 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); // Open device HANDLE h = INVALID_HANDLE_VALUE; - if (win9x || !(*options && !options[strspn(options, "fp")])) { + if (!(*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) { + if (h == INVALID_HANDLE_VALUE) { // Open without admin rights m_admin = false; h = CreateFileA(devpath, 0, @@ -2319,9 +2442,8 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int } if (h == INVALID_HANDLE_VALUE) { long err = GetLastError(); - pout("Cannot open device %s, Error=%ld\n", devpath, err); if (err == ERROR_FILE_NOT_FOUND) - set_err((win9x && phydrive <= 3 ? smartvsd_error() : ENOENT), "%s: not found", devpath); + set_err(ENOENT, "%s: not found", devpath); else if (err == ERROR_ACCESS_DENIED) set_err(EACCES, "%s: access denied", devpath); else @@ -2330,7 +2452,16 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int } set_fh(h); - if (con->reportataioctl > 1) + // 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)" :"")); m_usr_options = false; @@ -2347,21 +2478,23 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int 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) + // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call + m_port = port; + if (port < 0) return true; - // Win9X/ME: Get drive map - // RAID: Get port map + // 3ware RAID: Get port map GETVERSIONINPARAMS_EX vers_ex; - int devmap = smart_get_version(h, (port >= 0 ? &vers_ex : 0)); + int devmap = smart_get_version(h, &vers_ex); + + // 3ware RAID if vendor id present + m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE); unsigned long portmap = 0; if (port >= 0 && devmap >= 0) { // 3ware RAID: check vendor id - if (vers_ex.wIdentifier != SMART_VENDOR_3WARE) { - pout("SMART_GET_VERSION returns unknown Identifier = %04x\n" + 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; @@ -2379,7 +2512,7 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int } m_smartver_state = 1; - if (port >= 0) { + { // 3ware RAID: update devicemap first if (!update_3ware_devicemap_ioctl(h)) { @@ -2394,92 +2527,6 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port); } } - return true; - } - - // Win9x/ME: Check device presence & type - if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) { - unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10; - // Win9x drive existence check may not work as expected - // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01 - // (The related KB Article Q196120 is no longer available) - if (!is_permissive()) { - close(); - return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)", - devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap); - } - } - // Drive number must be passed to ioctl - m_drive = (phydrive & 0x3); - return true; -} - - -// Scan for ATA drives on Win9x/ME - -bool win9x_smart_interface::ata_scan(smart_device_list & devlist) -{ - // Open device - const char devpath[] = "\\\\.\\SMARTVSD"; - HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); - if (h == INVALID_HANDLE_VALUE) { - if (con->reportataioctl > 1) - pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError()); - return true; // SMARTVSD.VXD missing or no ATA devices - } - - // Get drive map - int devmap = smart_get_version(h); - CloseHandle(h); - if (devmap < 0) - return true; // Should not happen - - // Check ATA device presence, remove ATAPI devices - devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf); - char name[20]; - for (int i = 0; i < 4; i++) { - if (!(devmap & (1 << i))) - continue; - sprintf(name, "/dev/hd%c", 'a'+i); - devlist.push_back( new win_ata_device(this, name, "ata") ); - } - return true; -} - - -// Scan for ATA drives - -bool winnt_smart_interface::ata_scan(smart_device_list & devlist) -{ - const int max_raid = 2; - bool raid_seen[max_raid] = {false, false}; - - char name[20]; - for (int i = 0; i <= 9; i++) { - GETVERSIONINPARAMS_EX vers_ex; - if (get_phy_drive_type(i, &vers_ex) != DEV_ATA) - continue; - - // Interpret RAID drive map if present - if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) { - // Skip if more than 2 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 - for (int pi = 0; pi < 32; pi++) { - if (vers_ex.dwDeviceMapEx & (1L << pi)) { - sprintf(name, "/dev/sd%c,%u", 'a'+i, pi); - devlist.push_back( new win_ata_device(this, name, "ata") ); - } - } - continue; - } - - // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA - sprintf(name, "/dev/sd%c", 'a'+i); - devlist.push_back( new win_ata_device(this, name, "ata") ); } return true; @@ -2493,13 +2540,19 @@ 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_ok(in, - true, // data_out_support - false, // !multi_sector_support - true) // ata_48bit_support + if (!ata_cmd_is_supported(in, + ata_device::supports_data_out | + ata_device::supports_output_regs | + ata_device::supports_48bit) ) return false; + // 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"); + // Determine ioctl functions valid for this ATA cmd const char * valid_options = 0; @@ -2508,7 +2561,7 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 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"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; case ATA_CHECK_POWER_MODE: @@ -2526,21 +2579,21 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 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"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; 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*/ || is_win9x() ? - "saicm3" : "aicm3"); + // 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; case ATA_SMART_READ_LOG_SECTOR: - // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME + // SMART_RCV_DRIVE_DATA does not support READ_LOG // Try SCSI_MINIPORT also to skip buggy class driver // SMART functions do not support multi sector I/O. if (in.size == 512) - valid_options = (m_usr_options || is_win9x() ? "saicm3" : "aicm3"); + valid_options = (m_usr_options ? "saim3" : "aim3"); else valid_options = "a"; break; @@ -2552,11 +2605,7 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) break; 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"); + valid_options = (m_usr_options ? "saimf" : "saif"); break; default: @@ -2577,11 +2626,9 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) || 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"; + // ATA/IDE_PASS_THROUGH + valid_options = "ai"; } if (!m_admin) { @@ -2642,6 +2689,7 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) // 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(); for (int i = 0; ; i++) { @@ -2677,21 +2725,29 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) } if (!m_smartver_state) { assert(m_port == -1); - if (smart_get_version(get_fh()) < 0) { - if (!con->permissive) { + 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; } - con->permissive--; + failuretest_permissive--; } + else { + // 3ware RAID if vendor id present + m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE); + } + m_smartver_state = 1; } - rc = smart_ioctl(get_fh(), m_drive, ®s, data, datasize, m_port); + 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, @@ -2703,12 +2759,12 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 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); + if (rc == 0 && m_phydrive >= 0) + get_serial_from_wmi(m_phydrive, (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: @@ -2716,31 +2772,19 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) if (rc > 0) rc = 0; break; - case ATA_SMART_READ_THRESHOLDS: - { - ata_smart_values sv; - rc = storage_predict_failure_ioctl(get_fh(), (char *)&sv); - if (rc < 0) - break; - rc = 0; - // Fake zero thresholds - ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data; - memset(tr, 0, 512); - for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) - tr->chksum -= tr->thres_entries[i].id = sv.vendor_attributes[i].id; - } - break; case ATA_SMART_ENABLE: rc = 0; break; case ATA_SMART_STATUS: rc = storage_predict_failure_ioctl(get_fh()); - if (rc >= 0) { - if (rc > 0) { - regs.bCylHighReg = 0x2c; regs.bCylLowReg = 0xf4; - rc = 0; - } - out_regs_set = true; + 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: @@ -2803,548 +2847,399 @@ bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) hi.lba_high = prev_regs.bCylHighReg; } } + + 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; + return true; } // Return true if OS caches the ATA identify sector bool win_ata_device::ata_identify_is_cached() const { - // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false - // TODO: Set according to ioctl used. - return (m_port < 0 && !is_win9x()); + return m_id_is_cached; } -///////////////////////////////////////////////////////////////////////////// -// ASPI Interface (for SCSI devices) -///////////////////////////////////////////////////////////////////////////// - -#pragma pack(1) - -#define ASPI_SENSE_SIZE 18 - -// ASPI SCSI Request block header - -typedef struct { - unsigned char cmd; // 00: Command code - unsigned char status; // 01: ASPI status - unsigned char adapter; // 02: Host adapter number - unsigned char flags; // 03: Request flags - unsigned char reserved[4]; // 04: 0 -} ASPI_SRB_HEAD; - -// SRB for host adapter inquiry - -typedef struct { - ASPI_SRB_HEAD h; // 00: Header - unsigned char adapters; // 08: Number of adapters - unsigned char target_id; // 09: Target ID ? - char manager_id[16]; // 10: SCSI manager ID - char adapter_id[16]; // 26: Host adapter ID - unsigned char parameters[16]; // 42: Host adapter unique parmameters -} ASPI_SRB_INQUIRY; - -// SRB for get device type +////////////////////////////////////////////////////////////////////// +// csmi_ata_device -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; +bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info) +{ + // 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; -// SRB for SCSI I/O + 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); + } -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; + // 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; -#pragma pack() + phy_info = phy_info_buf.Information; -// 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 + const int max_number_of_phys = sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); + if (phy_info.bNumberOfPhys > max_number_of_phys) + return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys); + if (scsi_debugmode > 1) { + pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys); + for (int i = 0; i < max_number_of_phys; i++) { + const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; + const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached; + if (id.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) + continue; -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; + 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]); } - if (con->reportscsiioctl > 1) - pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i); - Sleep(100); } - return 0; -} + return true; +} -// Get ASPI entrypoint from wnaspi32.dll - -static FARPROC aspi_get_address(const char * name, int verbose) +unsigned csmi_device::get_ports_used() { - 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; + CSMI_SAS_PHY_INFO phy_info; + if (!get_phy_info(phy_info)) return 0; + + unsigned ports_used = 0; + for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) { + const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; + if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) + continue; + if (pe.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) + continue; + switch (pe.Attached.bTargetPortProtocol) { + case CSMI_SAS_PROTOCOL_SATA: + case CSMI_SAS_PROTOCOL_STP: + break; + default: + continue; + } + + if (pe.bPortIdentifier == 0xff) + // Older (<= 9.*) Intel RST driver + ports_used |= (1 << i); + else + ports_used |= (1 << pe.bPortIdentifier); } - return addr; + + return ports_used; } -static int aspi_open_dll(int verbose) +bool csmi_device::select_port(int port) { - UINT (*aspi_info)(void); - UINT info, rc; + CSMI_SAS_PHY_INFO phy_info; + if (!get_phy_info(phy_info)) + return false; - assert(!aspi_entry_valid()); + // Find port + int max_port = -1, port_index = -1; + for (unsigned i = 0; i < sizeof(phy_info.Phy) / sizeof(phy_info.Phy[0]); i++) { + const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i]; + if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED) + continue; - // 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 (pe.bPortIdentifier == 0xff) { + // Older (<= 9.*) Intel RST driver + max_port = phy_info.bNumberOfPhys - 1; + if (i >= phy_info.bNumberOfPhys) + break; + if ((int)i != port) + continue; + } + else { + if (pe.bPortIdentifier > max_port) + max_port = pe.bPortIdentifier; + if (pe.bPortIdentifier != port) + continue; + } - if (h_aspi_dll == INVALID_HANDLE_VALUE) { - // do not retry - errno = ENOENT; - return -1; + port_index = i; + break; } - // 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 (con->reportscsiioctl > 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); + if (port_index < 0) { + if (port <= max_port) + return set_err(ENOENT, "Port %d is disabled", port); + else + return set_err(ENOENT, "Port %d does not exist (#ports: %d)", port, + max_port + 1); } - // 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; + 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); - // Init ASPI manager and get number of adapters - info = (aspi_info)(); - if (con->reportscsiioctl > 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; + 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); } - if (con->reportscsiioctl) - pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":"")); - -#ifdef __CYGWIN__ - // save PID to detect fork() in aspi_entry_valid() - aspi_dll_pid = GetCurrentProcessId(); -#endif - assert(aspi_entry_valid()); - return 0; -} - - -static int aspi_io_call(ASPI_SRB * srb, unsigned timeout) -{ - HANDLE event; - // Create event - if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) { - pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO; - } - srb->i.event_handle = event; - srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY; - // Start ASPI request - aspi_entry(srb); - if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) { - // Wait for event - DWORD rc = WaitForSingleObject(event, timeout*1000L); - if (rc != WAIT_OBJECT_0) { - if (rc == WAIT_TIMEOUT) { - pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n", - srb->h.adapter, srb->i.target_id, timeout); - } - else { - pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n", - (unsigned long)event, rc, rc, GetLastError()); - } - // TODO: ASPI_ABORT_IO command - aspi_entry = 0; - h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE; - return -EIO; - } - } - CloseHandle(event); - return 0; + m_phy_ent = phy_ent; + return true; } -win_aspi_device::win_aspi_device(smart_interface * intf, - const char * dev_name, const char * req_type) -: smart_device(intf, dev_name, "scsi", req_type), - m_adapter(-1), m_id(0) +bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) { -} + 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, + "CMSI") + ) + return false; -bool win_aspi_device::is_open() const -{ - return (m_adapter >= 0); -} + // 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(); -bool win_aspi_device::open() -{ - // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0 - unsigned adapter = ~0, id = ~0; int n1 = -1; - const char * name = skipdev(get_dev_name()); - if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name) - && adapter <= 9 && id < 16)) - return set_err(EINVAL); + // 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; - if (!aspi_entry_valid()) { - if (aspi_open_dll(1/*verbose*/)) - return set_err(ENOENT); + // 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); } - // 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); + // 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; + } + + // Call ioctl + if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) { + return false; } - // 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); + // 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]; + } } - else if (con->reportscsiioctl) - pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype); - m_adapter = (int)adapter; m_id = (unsigned char)id; + // 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; } -bool win_aspi_device::close() +////////////////////////////////////////////////////////////////////// +// 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_port(-1) { - // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads - return true; } +win_csmi_device::~win_csmi_device() throw() +{ + if (m_fh != INVALID_HANDLE_VALUE) + CloseHandle(m_fh); +} -// Scan for ASPI drives +bool win_csmi_device::is_open() const +{ + return (m_fh != INVALID_HANDLE_VALUE); +} -bool win9x_smart_interface::scsi_scan(smart_device_list & devlist) +bool win_csmi_device::close() { - if (!aspi_entry_valid()) { - if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/)) - return true; - } + if (m_fh == INVALID_HANDLE_VALUE) + return true; + BOOL rc = CloseHandle(m_fh); + m_fh = INVALID_HANDLE_VALUE; + return !!rc; +} - for (unsigned ad = 0; ad < num_aspi_adapters; ad++) { - ASPI_SRB srb; - if (ad > 9) { - if (con->reportscsiioctl) - pout(" ASPI Adapter %u: Ignored\n", ad); - continue; - } +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); - // Get adapter name - memset(&srb, 0, sizeof(srb)); - srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE; - srb.h.adapter = ad; - if (aspi_call(&srb)) - break; + // Open controller handle + char devpath[30]; + snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no); - if (srb.h.status != ASPI_STATUS_NO_ERROR) { - if (con->reportscsiioctl) - pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status); - continue; - } + HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0); - if (con->reportscsiioctl) { - 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); - } + 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; + } - 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 (con->reportscsiioctl > 1) - pout(" ID %u: No such device (Status=0x%02x)\n", id, srb.h.status); - continue; - } + if (scsi_debugmode > 1) + pout(" %s: successfully opened\n", devpath); - if (!ignore && srb.t.devtype == 0x00/*HDD*/) { - if (con->reportscsiioctl) - 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 (con->reportscsiioctl) - pout(" ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype); - } - } + m_fh = h; + m_port = port; return true; } -// Interface to ASPI SCSI devices -bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop) +bool win_csmi_device::open() { - int report = con->reportscsiioctl; // TODO - - if (m_adapter < 0) { - set_err(EBADF); + if (!open_scsi()) return false; - } - - if (!aspi_entry_valid()) { - set_err(EBADF); - return false; - } - if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) { - set_err(EINVAL, "bad CDB length"); + // Get Phy info for this drive + if (!select_port(m_port)) { + close(); return false; } - if (report > 0) { - // From os_linux.c - int k, j; - const unsigned char * ucp = iop->cmnd; - const char * np; - char buff[256]; - const int sz = (int)sizeof(buff); - - np = scsi_get_opcode_name(ucp[0]); - j = snprintf(buff, sz, " [%s: ", np ? np : ""); - for (k = 0; k < (int)iop->cmnd_len; ++k) - j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); - if ((report > 1) && - (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { - int trunc = (iop->dxfer_len > 256) ? 1 : 0; - - j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " - "data, len=%d%s:\n", (int)iop->dxfer_len, - (trunc ? " [only first 256 bytes shown]" : "")); - dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); - } - else - j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); - pout(buff); - } - - ASPI_SRB srb; - memset(&srb, 0, sizeof(srb)); - srb.h.cmd = ASPI_CMD_EXECUTE_IO; - srb.h.adapter = m_adapter; - srb.i.target_id = m_id; - //srb.i.lun = 0; - srb.i.sense_size = ASPI_SENSE_SIZE; - srb.i.cdb_size = iop->cmnd_len; - memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len); + return true; +} - switch (iop->dxfer_dir) { - case DXFER_NONE: - srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER; - break; - case DXFER_FROM_DEVICE: - srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST; - srb.i.data_size = iop->dxfer_len; - srb.i.data_addr = iop->dxferp; - break; - case DXFER_TO_DEVICE: - srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET; - srb.i.data_size = iop->dxfer_len; - srb.i.data_addr = iop->dxferp; - break; + +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: - set_err(EINVAL, "bad dxfer_dir"); - return false; + return set_err(ENOSYS, "Unknown CSMI code=%u", code); } - 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; - } - } + // 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); - if (report > 0) - pout(" OK\n"); + // 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); + } - 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); + // 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 (scsi_debugmode > 1) + pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code, (unsigned)num_out); + return true; } @@ -3363,11 +3258,11 @@ win_scsi_device::win_scsi_device(smart_interface * intf, 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 + // 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(drive[0] - 'a', -1, -1, sub_addr); + return open(sdxy_to_phydrive(drive), -1, -1, sub_addr); } // pd,N => Physical drive , RAID port N int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1; @@ -3418,7 +3313,7 @@ bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr* 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()); + set_err(ENODEV, "%s: Open failed, Error=%u", b, (unsigned)GetLastError()); return false; } set_fh(h); @@ -3426,39 +3321,6 @@ bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr* } -bool winnt_smart_interface::scsi_scan(smart_device_list & devlist) -{ - char name[20]; - for (int i = 0; i <= 9; i++) { - if (get_phy_drive_type(i) != DEV_SCSI) - continue; - // STORAGE_QUERY_PROPERTY returned SCSI/SAS/... - sprintf(name, "/dev/sd%c", 'a'+i); - devlist.push_back( new win_scsi_device(this, name, "scsi") ); - } - return true; -} - - -#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -typedef struct _SCSI_PASS_THROUGH_DIRECT { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - PVOID DataBuffer; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH_DIRECT; - typedef struct { SCSI_PASS_THROUGH_DIRECT spt; ULONG Filler; @@ -3515,7 +3377,7 @@ static long scsi_pass_through_indirect(HANDLE h, // 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 = con->reportscsiioctl; // TODO + int report = scsi_debugmode; // TODO if (report > 0) { int k, j; @@ -3539,7 +3401,7 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); - pout(buff); + pout("%s", buff); } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; @@ -3567,7 +3429,7 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) 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 briges) + // transfers (needed for SMART STATUS check of JMicron USB bridges) if (sb.spt.DataTransferLength == 1) direct = false; break; @@ -3607,6 +3469,136 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) 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; + + if (iop->dxfer_len > sb.spt.DataTransferLength) + iop->resid = iop->dxfer_len - sb.spt.DataTransferLength; + else + iop->resid = 0; + + if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + pout(" Incoming data, len=%d, 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; +} + +// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c +static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, 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 : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout("%s", buff); + } + + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; + if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) { + return EINVAL; + } + + 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); + + 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; + } + + 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); + + if (err) + { + return err; + } + + iop->scsi_status = sb.spt.ScsiStatus; + if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { + int slen = sb.ucSenseBuf[7] + 8; + + 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, @@ -3619,17 +3611,223 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) } else iop->resp_sense_len = 0; - if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0)) + if (iop->dxfer_len > sb.spt.DataTransferLength) iop->resid = iop->dxfer_len - sb.spt.DataTransferLength; else iop->resid = 0; if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) { int trunc = (iop->dxfer_len > 256) ? 1 : 0; - pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, + pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } + + return 0; +} + +// Areca RAID Controller(SAS Device) +win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca") +{ + set_fh(INVALID_HANDLE_VALUE); + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} + +bool win_areca_scsi_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; +} + +smart_device * win_areca_scsi_device::autodetect_open() +{ + return this; +} + +int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + 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; + } + } + + return ioctlreturn; +} + +bool win_areca_scsi_device::arcmsr_lock() +{ +#define SYNCOBJNAME "Global\\SynIoctlMutex" + int ctlrnum = -1; + char mutexstr[64]; + + 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"); + } + + // atomic access to driver + WaitForSingleObject(m_mutex, INFINITE); + + return true; +} + + +bool win_areca_scsi_device::arcmsr_unlock() +{ + if( m_mutex != NULL) + { + ReleaseMutex(m_mutex); + CloseHandle(m_mutex); + } + + return true; +} + + +// Areca RAID Controller(SATA Disk) +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; +} + +smart_device * win_areca_ata_device::autodetect_open() +{ + int is_ata = 1; + + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; + } + + if(is_ata == 1) + { + // SATA device + return this; + } + + // 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(); +} + +int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + 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; + } + } + + return ioctlreturn; +} + +bool win_areca_ata_device::arcmsr_lock() +{ +#define SYNCOBJNAME "Global\\SynIoctlMutex" + int ctlrnum = -1; + char mutexstr[64]; + + 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"); + } + + // atomic access to driver + WaitForSingleObject(m_mutex, INFINITE); + + return true; +} + + +bool win_areca_ata_device::arcmsr_unlock() +{ + if( m_mutex != NULL) + { + ReleaseMutex(m_mutex); + CloseHandle(m_mutex); + } + return true; } @@ -3644,14 +3842,40 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop) // Initialize platform interface and register with smi() void smart_interface::init() { - // Select interface for Windows flavor - if (os_win32::is_win9x()) { - static os_win32::win9x_smart_interface the_win9x_interface; - smart_interface::set(&the_win9x_interface); - } - else { - static os_win32::winnt_smart_interface the_winnt_interface; - smart_interface::set(&the_winnt_interface); + { + // Remove "." from DLL search path if supported + // to prevent DLL preloading attacks + BOOL (WINAPI * SetDllDirectoryA_p)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR)) + GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA"); + if (SetDllDirectoryA_p) + SetDllDirectoryA_p(""); } + + static os_win32::win_smart_interface the_win_interface; + smart_interface::set(&the_win_interface); } + +#ifndef __CYGWIN__ + +// Get exe directory +// (prototype in utiliy.h) +std::string get_exe_dir() +{ + char path[MAX_PATH]; + // Get path of this exe + if (!GetModuleFileNameA(GetModuleHandleA(0), path, sizeof(path))) + throw std::runtime_error("GetModuleFileName() failed"); + // Replace backslash by slash + int sl = -1; + for (int i = 0; path[i]; i++) + if (path[i] == '\\') { + path[i] = '/'; sl = i; + } + // Remove filename + if (sl >= 0) + path[sl] = 0; + return path; +} + +#endif