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