version 1.1.1.1, 2012/02/21 16:32:16
|
version 1.1.1.2, 2012/10/09 09:36:45
|
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-11 Christian Franke <smartmontools-support@lists.sourceforge.net> | * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net> |
| * 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 |
* it under the terms of the GNU General Public License as published by |
* it under the terms of the GNU General Public License as published by |
Line 531 class win_tw_cli_device (private)
|
Line 532 class win_tw_cli_device (private)
|
}; |
}; |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
/// Areca RAID support |
|
|
|
/* ARECA IO CONTROL CODE*/ |
|
#define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004 |
|
#define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008 |
|
#define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C |
|
#define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010 |
|
#define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018 |
|
#define ARECA_SIG_STR "ARCMSR" |
|
|
|
|
|
// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver |
|
typedef struct _SRB_IO_CONTROL |
|
{ |
|
unsigned int HeaderLength; |
|
unsigned char Signature[8]; |
|
unsigned int Timeout; |
|
unsigned int ControlCode; |
|
unsigned int ReturnCode; |
|
unsigned int Length; |
|
} sSRB_IO_CONTROL; |
|
|
|
typedef struct _SRB_BUFFER |
|
{ |
|
sSRB_IO_CONTROL srbioctl; |
|
unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware |
|
} sSRB_BUFFER; |
|
|
|
class win_areca_device |
|
: public /*implements*/ ata_device, |
|
public /*extends*/ win_smart_device |
|
{ |
|
public: |
|
win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum = 1); |
|
|
|
static int arcmsr_command_handler(HANDLE fh, unsigned long arcmsr_cmd, unsigned char *data, int data_len); |
|
|
|
protected: |
|
virtual bool open(); |
|
|
|
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: |
|
int m_disknum; ///< Disk number. |
|
int m_encnum; ///< Enclosure number. |
|
}; |
|
|
|
|
////////////////////////////////////////////////////////////////////// |
////////////////////////////////////////////////////////////////////// |
// Platform specific interfaces |
// Platform specific interfaces |
|
|
Line 543 class win_smart_interface (public)
|
Line 595 class win_smart_interface (public)
|
|
|
virtual std::string get_app_examples(const char * appname); |
virtual std::string get_app_examples(const char * appname); |
|
|
|
#ifndef __CYGWIN__ |
|
virtual int64_t get_timer_usec(); |
|
#endif |
|
|
//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); |
|
|
Line 583 class winnt_smart_interface
|
Line 639 class winnt_smart_interface
|
: public /*extends*/ win_smart_interface |
: public /*extends*/ win_smart_interface |
{ |
{ |
public: |
public: |
|
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); |
|
|
Line 590 class winnt_smart_interface
|
Line 648 class winnt_smart_interface
|
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); |
|
|
|
virtual smart_device * get_custom_smart_device(const char * name, const char * type); |
|
|
|
virtual std::string get_valid_custom_dev_types_str(); |
}; |
}; |
|
|
|
|
Line 657 std::string win_smart_interface::get_os_version_str()
|
Line 719 std::string win_smart_interface::get_os_version_str()
|
case VER_PLATFORM_WIN32_NT <<16|0x0600| 1: |
case VER_PLATFORM_WIN32_NT <<16|0x0600| 1: |
w = (vi.wProductType == VER_NT_WORKSTATION ? "win7" |
w = (vi.wProductType == VER_NT_WORKSTATION ? "win7" |
: "2008r2"); break; |
: "2008r2"); break; |
|
case VER_PLATFORM_WIN32_NT <<16|0x0600| 2: |
|
w = (vi.wProductType == VER_NT_WORKSTATION ? "win8" |
|
: "2012"); break; |
default: w = 0; break; |
default: w = 0; break; |
} |
} |
|
|
Line 679 std::string win_smart_interface::get_os_version_str()
|
Line 744 std::string win_smart_interface::get_os_version_str()
|
return vstr; |
return vstr; |
} |
} |
|
|
|
#ifndef __CYGWIN__ |
|
// MSVCRT only provides ftime() which uses GetSystemTime() |
|
// This provides only ~15ms resolution by default. |
|
// Use QueryPerformanceCounter instead (~300ns). |
|
// (Cygwin provides CLOCK_MONOTONIC which has the same effect) |
|
int64_t win_smart_interface::get_timer_usec() |
|
{ |
|
static int64_t freq = 0; |
|
|
|
LARGE_INTEGER t; |
|
if (freq == 0) |
|
freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1); |
|
if (freq <= 0) |
|
return smart_interface::get_timer_usec(); |
|
|
|
if (!QueryPerformanceCounter(&t)) |
|
return -1; |
|
if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000)) |
|
return -1; |
|
|
|
return (t.QuadPart * 1000000LL) / freq; |
|
} |
|
#endif // __CYGWIN__ |
|
|
|
|
// Return value for device detection functions |
// Return value for device detection functions |
enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB }; |
enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB }; |
|
|
Line 793 smart_device * win_smart_interface::autodetect_smart_d
|
Line 883 smart_device * win_smart_interface::autodetect_smart_d
|
return 0; |
return 0; |
} |
} |
|
|
|
|
|
smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, const char * type) |
|
{ |
|
// Areca? |
|
int disknum = -1, n1 = -1, n2 = -1; |
|
int encnum = 1; |
|
HANDLE fh = INVALID_HANDLE_VALUE; |
|
char devpath[32]; |
|
|
|
if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { |
|
if (!(1 <= disknum && disknum <= 128)) { |
|
set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); |
|
return 0; |
|
} |
|
if (!(1 <= encnum && encnum <= 8)) { |
|
set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); |
|
return 0; |
|
} |
|
|
|
name = skipdev(name); |
|
#define ARECA_MAX_CTLR_NUM 16 |
|
n1 = -1; |
|
int ctlrindex = 0; |
|
if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) { |
|
/* |
|
1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and |
|
2. map arcmsrX into "\\\\.\\scsiX" |
|
*/ |
|
for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) { |
|
memset(devpath, 0, sizeof(devpath)); |
|
sprintf(devpath, "\\\\.\\scsi%d:", idx); |
|
if ( (fh = CreateFile( devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, |
|
NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE ) { |
|
if (win_areca_device::arcmsr_command_handler(fh, ARCMSR_IOCTL_RETURN_CODE_3F, NULL, 0) == 0) { |
|
if (ctlrindex-- == 0) { |
|
return new win_areca_device(this, devpath, fh, disknum, encnum); |
|
} |
|
} |
|
CloseHandle(fh); |
|
} |
|
} |
|
set_err(ENOENT, "No Areca controller found"); |
|
} |
|
else |
|
set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX"); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
std::string winnt_smart_interface::get_valid_custom_dev_types_str() |
|
{ |
|
return "areca,N[/E]"; |
|
} |
|
|
|
|
smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) |
smart_device * winnt_smart_interface::autodetect_smart_device(const char * name) |
{ |
{ |
smart_device * dev = win_smart_interface::autodetect_smart_device(name); |
smart_device * dev = win_smart_interface::autodetect_smart_device(name); |
Line 1006 std::string win_smart_interface::get_app_examples(cons
|
Line 1152 std::string win_smart_interface::get_app_examples(cons
|
" (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n" |
" (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n" |
" smartctl -A /dev/tw_cli/c0/p1\n" |
" smartctl -A /dev/tw_cli/c0/p1\n" |
" (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n" |
" (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n" |
|
" smartctl --all --device=areca,3/1 /dev/arcmsr0\n" |
|
" (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n" |
|
" on 1st Areca RAID controller)\n" |
"\n" |
"\n" |
" 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" |
Line 1018 std::string win_smart_interface::get_app_examples(cons
|
Line 1167 std::string win_smart_interface::get_app_examples(cons
|
} |
} |
|
|
|
|
|
bool winnt_smart_interface::disable_system_auto_standby(bool disable) |
|
{ |
|
if (disable) { |
|
SYSTEM_POWER_STATUS ps; |
|
if (!GetSystemPowerStatus(&ps)) |
|
return set_err(ENOSYS, "Unknown power status"); |
|
if (ps.ACLineStatus != 1) { |
|
SetThreadExecutionState(ES_CONTINUOUS); |
|
if (ps.ACLineStatus == 0) |
|
set_err(EIO, "AC offline"); |
|
else |
|
set_err(EIO, "Unknown AC line status"); |
|
return false; |
|
} |
|
} |
|
|
|
if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0))) |
|
return set_err(ENOSYS); |
|
return true; |
|
} |
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
// ATA Interface |
// ATA Interface |
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
Line 1898 bool win_tw_cli_device::open()
|
Line 2069 bool win_tw_cli_device::open()
|
// Show tw_cli error message |
// Show tw_cli error message |
err++; |
err++; |
err[strcspn(err, "\r\n")] = 0; |
err[strcspn(err, "\r\n")] = 0; |
return set_err(EIO, err); | return set_err(EIO, "%s", err); |
} |
} |
return set_err(EIO); |
return set_err(EIO); |
} |
} |
Line 2163 static int get_identify_from_device_property(HANDLE hd
|
Line 2334 static int get_identify_from_device_property(HANDLE hd
|
///////////////////////////////////////////////////////////////////////////// |
///////////////////////////////////////////////////////////////////////////// |
// USB ID detection using WMI |
// USB ID detection using WMI |
|
|
// Return true if STR starts with PREFIX. |
|
static inline bool str_starts_with(const std::string & str, const char * prefix) |
|
{ |
|
return !strncmp(str.c_str(), prefix, strlen(prefix)); |
|
} |
|
|
|
// Get USB ID for a physical drive number |
// Get USB ID for a physical drive number |
static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id) |
static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id) |
{ |
{ |
Line 3778 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io *
|
Line 3943 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io *
|
} |
} |
else |
else |
j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
pout(buff); | pout("%s", buff); |
} |
} |
|
|
ASPI_SRB srb; |
ASPI_SRB srb; |
Line 4021 bool win_scsi_device::scsi_pass_through(struct scsi_cm
|
Line 4186 bool win_scsi_device::scsi_pass_through(struct scsi_cm
|
} |
} |
else |
else |
j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
pout(buff); | pout("%s", buff); |
} |
} |
|
|
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; |
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; |
Line 4117 bool win_scsi_device::scsi_pass_through(struct scsi_cm
|
Line 4282 bool win_scsi_device::scsi_pass_through(struct scsi_cm
|
dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
} |
} |
return true; |
return true; |
|
} |
|
|
|
// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c |
|
static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop) |
|
{ |
|
int report = scsi_debugmode; // TODO |
|
|
|
if (report > 0) { |
|
int k, j; |
|
const unsigned char * ucp = iop->cmnd; |
|
const char * np; |
|
char buff[256]; |
|
const int sz = (int)sizeof(buff); |
|
|
|
np = scsi_get_opcode_name(ucp[0]); |
|
j = snprintf(buff, sz, " [%s: ", np ? np : "<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); |
|
} |
|
|
|
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; |
|
if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) { |
|
return EINVAL; |
|
} |
|
|
|
memset(&sb, 0, sizeof(sb)); |
|
sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); |
|
//sb.spt.PathId = 0; |
|
sb.spt.TargetId = targetid; |
|
//sb.spt.Lun = 0; |
|
sb.spt.CdbLength = iop->cmnd_len; |
|
memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len); |
|
sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf); |
|
sb.spt.SenseInfoOffset = |
|
offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); |
|
sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60); |
|
|
|
bool direct = true; |
|
switch (iop->dxfer_dir) { |
|
case DXFER_NONE: |
|
sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; |
|
break; |
|
case DXFER_FROM_DEVICE: |
|
sb.spt.DataIn = SCSI_IOCTL_DATA_IN; |
|
sb.spt.DataTransferLength = iop->dxfer_len; |
|
sb.spt.DataBuffer = iop->dxferp; |
|
// IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte |
|
// transfers (needed for SMART STATUS check of JMicron USB bridges) |
|
if (sb.spt.DataTransferLength == 1) |
|
direct = false; |
|
break; |
|
case DXFER_TO_DEVICE: |
|
sb.spt.DataIn = SCSI_IOCTL_DATA_OUT; |
|
sb.spt.DataTransferLength = iop->dxfer_len; |
|
sb.spt.DataBuffer = iop->dxferp; |
|
break; |
|
default: |
|
return EINVAL; |
|
} |
|
|
|
long err = 0; |
|
if (direct) { |
|
DWORD num_out; |
|
if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT, |
|
&sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0)) |
|
err = GetLastError(); |
|
} |
|
else |
|
err = scsi_pass_through_indirect(fd, &sb); |
|
|
|
if (err) |
|
{ |
|
return err; |
|
} |
|
|
|
iop->scsi_status = sb.spt.ScsiStatus; |
|
if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { |
|
int slen = sb.ucSenseBuf[7] + 8; |
|
|
|
if (slen > (int)sizeof(sb.ucSenseBuf)) |
|
slen = sizeof(sb.ucSenseBuf); |
|
if (slen > (int)iop->max_sense_len) |
|
slen = iop->max_sense_len; |
|
memcpy(iop->sensep, sb.ucSenseBuf, slen); |
|
iop->resp_sense_len = slen; |
|
if (report) { |
|
if (report > 1) { |
|
pout(" >>> Sense buffer, len=%d:\n", slen); |
|
dStrHex(iop->sensep, slen , 1); |
|
} |
|
if ((iop->sensep[0] & 0x7f) > 0x71) |
|
pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", |
|
iop->scsi_status, iop->sensep[1] & 0xf, |
|
iop->sensep[2], iop->sensep[3]); |
|
else |
|
pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", |
|
iop->scsi_status, iop->sensep[2] & 0xf, |
|
iop->sensep[12], iop->sensep[13]); |
|
} |
|
} else |
|
iop->resp_sense_len = 0; |
|
|
|
if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0)) |
|
iop->resid = iop->dxfer_len - sb.spt.DataTransferLength; |
|
else |
|
iop->resid = 0; |
|
|
|
if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) { |
|
int trunc = (iop->dxfer_len > 256) ? 1 : 0; |
|
pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, |
|
(trunc ? " [only first 256 bytes shown]" : "")); |
|
dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
#if 0 // For debugging areca code |
|
|
|
static void dumpdata(unsigned char *block, int len) |
|
{ |
|
int ln = (len / 16) + 1; // total line# |
|
unsigned char c; |
|
int pos = 0; |
|
|
|
printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); |
|
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); |
|
printf("=====================================================================\n"); |
|
|
|
for ( int l = 0; l < ln && len; l++ ) |
|
{ |
|
// printf the line# and the HEX data |
|
// if a line data length < 16 then append the space to the tail of line to reach 16 chars |
|
printf("%02X | ", l); |
|
for ( pos = 0; pos < 16 && len; pos++, len-- ) |
|
{ |
|
c = block[l*16+pos]; |
|
printf("%02X ", c); |
|
} |
|
|
|
if ( pos < 16 ) |
|
{ |
|
for ( int loop = pos; loop < 16; loop++ ) |
|
{ |
|
printf(" "); |
|
} |
|
} |
|
|
|
// print ASCII char |
|
for ( int loop = 0; loop < pos; loop++ ) |
|
{ |
|
c = block[l*16+loop]; |
|
if ( c >= 0x20 && c <= 0x7F ) |
|
{ |
|
printf("%c", c); |
|
} |
|
else |
|
{ |
|
printf("."); |
|
} |
|
} |
|
printf("\n"); |
|
} |
|
printf("=====================================================================\n"); |
|
} |
|
|
|
#endif |
|
|
|
// PURPOSE |
|
// This is an interface routine meant to isolate the OS dependent |
|
// parts of the code, and to provide a debugging interface. Each |
|
// different port and OS needs to provide it's own interface. This |
|
// is the Windows interface to the Areca "arcmsr" driver. It allows ATA |
|
// commands to be passed through the SCSI driver. |
|
// DETAILED DESCRIPTION OF ARGUMENTS |
|
// fd: is the file descriptor provided by open() |
|
// disknum is the disk number (0 to 127) in the RAID array |
|
// command: defines the different operations. |
|
// select: additional input data if needed (which log, which type of |
|
// self-test). |
|
// data: location to write output data, if needed (512 bytes). |
|
// Note: not all commands use all arguments. |
|
// RETURN VALUES |
|
// -1 if the command failed |
|
// 0 if the command succeeded, |
|
// STATUS_CHECK routine: |
|
// -1 if the command failed |
|
// 0 if the command succeeded and disk SMART status is "OK" |
|
// 1 if the command succeeded and disk SMART status is "FAILING" |
|
int win_areca_device::arcmsr_command_handler(HANDLE fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len) |
|
{ |
|
int ioctlreturn = 0; |
|
sSRB_BUFFER sBuf; |
|
struct scsi_cmnd_io io_hdr; |
|
int dir = DXFER_TO_DEVICE; |
|
|
|
UINT8 cdb[10]; |
|
UINT8 sense[32]; |
|
|
|
unsigned char *areca_return_packet; |
|
int total = 0; |
|
int expected = -1; |
|
unsigned char return_buff[2048]; |
|
unsigned char *ptr = &return_buff[0]; |
|
memset(return_buff, 0, sizeof(return_buff)); |
|
|
|
memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); |
|
memset(&io_hdr, 0, sizeof(io_hdr)); |
|
memset(cdb, 0, sizeof(cdb)); |
|
memset(sense, 0, sizeof(sense)); |
|
|
|
|
|
sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); |
|
memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); |
|
sBuf.srbioctl.Timeout = 10000; |
|
sBuf.srbioctl.ControlCode = arcmsr_cmd; |
|
|
|
switch ( arcmsr_cmd ) |
|
{ |
|
// command for writing data to driver |
|
case ARCMSR_IOCTL_WRITE_WQBUFFER: |
|
if ( data && data_len ) |
|
{ |
|
sBuf.srbioctl.Length = data_len; |
|
memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); |
|
} |
|
// commands for clearing related buffer of driver |
|
case ARCMSR_IOCTL_CLEAR_RQBUFFER: |
|
case ARCMSR_IOCTL_CLEAR_WQBUFFER: |
|
cdb[0] = 0x3B; //SCSI_WRITE_BUF command; |
|
break; |
|
// command for reading data from driver |
|
case ARCMSR_IOCTL_READ_RQBUFFER: |
|
// command for identifying driver |
|
case ARCMSR_IOCTL_RETURN_CODE_3F: |
|
cdb[0] = 0x3C; //SCSI_READ_BUF command; |
|
dir = DXFER_FROM_DEVICE; |
|
break; |
|
default: |
|
// unknown arcmsr commands |
|
return -1; |
|
} |
|
|
|
cdb[1] = 0x01; |
|
cdb[2] = 0xf0; |
|
|
|
io_hdr.dxfer_dir = dir; |
|
io_hdr.dxfer_len = sizeof(sBuf); |
|
io_hdr.dxferp = (unsigned char *)&sBuf; |
|
io_hdr.cmnd = cdb; |
|
io_hdr.cmnd_len = sizeof(cdb); |
|
io_hdr.sensep = sense; |
|
io_hdr.max_sense_len = sizeof(sense); |
|
io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; |
|
|
|
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 ) |
|
{ |
|
// if succeeded, just returns the length of outgoing data |
|
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(...) |
|
return -4; |
|
} |
|
|
|
if ( ioctlreturn ) |
|
{ |
|
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) |
|
: smart_device(intf, dev_name, "areca", "areca"), |
|
m_disknum(disknum), |
|
m_encnum(encnum) |
|
{ |
|
set_fh(fh); |
|
set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); |
|
} |
|
|
|
bool win_areca_device::open() |
|
{ |
|
HANDLE hFh; |
|
|
|
if( is_open() ) |
|
{ |
|
return true; |
|
} |
|
|
|
hFh = CreateFile( get_dev_name(), |
|
GENERIC_READ|GENERIC_WRITE, |
|
FILE_SHARE_READ|FILE_SHARE_WRITE, |
|
NULL, |
|
OPEN_EXISTING, |
|
0, |
|
NULL ); |
|
if(hFh == INVALID_HANDLE_VALUE) |
|
{ |
|
return false; |
|
} |
|
|
|
set_fh(hFh); |
|
return true; |
|
} |
|
|
|
// Areca RAID Controller |
|
bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) |
|
{ |
|
// ATA input registers |
|
typedef struct _ATA_INPUT_REGISTERS |
|
{ |
|
unsigned char features; |
|
unsigned char sector_count; |
|
unsigned char sector_number; |
|
unsigned char cylinder_low; |
|
unsigned char cylinder_high; |
|
unsigned char device_head; |
|
unsigned char command; |
|
unsigned char reserved[8]; |
|
unsigned char data[512]; // [in/out] buffer for outgoing/incoming data |
|
} sATA_INPUT_REGISTERS; |
|
|
|
// ATA output registers |
|
// Note: The output registers is re-sorted for areca internal use only |
|
typedef struct _ATA_OUTPUT_REGISTERS |
|
{ |
|
unsigned char error; |
|
unsigned char status; |
|
unsigned char sector_count; |
|
unsigned char sector_number; |
|
unsigned char cylinder_low; |
|
unsigned char cylinder_high; |
|
} sATA_OUTPUT_REGISTERS; |
|
|
|
// Areca packet format for outgoing: |
|
// B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 |
|
// B[3~4] : 2 bytes command length + variant data length, little endian |
|
// B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c |
|
// B[6~last-1] : variant bytes payload data |
|
// B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) |
|
// |
|
// |
|
// header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte |
|
// +--------------------------------------------------------------------------------+ |
|
// + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | |
|
// +--------------------------------------------------------------------------------+ |
|
// |
|
|
|
//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# |
|
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]; |
|
} |
|
areca_packet[areca_packet_len-1] = cs; |
|
|
|
// ----- BEGIN TO SEND TO ARECA DRIVER ------ |
|
int expected = 0; |
|
unsigned char return_buff[2048]; |
|
memset(return_buff, 0, sizeof(return_buff)); |
|
|
|
expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0); |
|
if (expected==-3) { |
|
return set_err(EIO); |
|
} |
|
|
|
expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0); |
|
expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len); |
|
if ( expected > 0 ) |
|
{ |
|
expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff)); |
|
} |
|
if ( expected < 0 ) |
|
{ |
|
return set_err(EIO); |
|
} |
|
|
|
// ----- VERIFY THE CHECKSUM ----- |
|
cs = 0; |
|
for ( int loop = 3; loop < expected - 1; loop++ ) |
|
{ |
|
cs += return_buff[loop]; |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
// returns with data |
|
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_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) |
|
{ |
|
#define SYNCOBJNAME "Global\\SynIoctlMutex" |
|
int ctlrnum = -1; |
|
char mutexstr[64]; |
|
SECURITY_ATTRIBUTES sa; |
|
PSECURITY_DESCRIPTOR pSD; |
|
HANDLE hmutex; |
|
|
|
if (!ata_cmd_is_ok(in, |
|
true, // data_out_support |
|
false, // TODO: multi_sector_support |
|
true) // ata_48bit_support |
|
) |
|
return false; |
|
|
|
// Support 48-bit commands with zero high bytes |
|
if (in.in_regs.is_real_48bit_cmd()) |
|
return set_err(ENOSYS, "48-bit ATA commands not fully supported by Areca"); |
|
|
|
if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1) |
|
return set_err(EINVAL, "unable to parse device name"); |
|
|
|
memset(mutexstr, 0, sizeof(mutexstr)); |
|
sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum); |
|
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); |
|
if ( !InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) ) |
|
{ |
|
LocalFree((HLOCAL)pSD); |
|
return set_err(EIO, "InitializeSecurityDescriptor failed"); |
|
} |
|
|
|
if ( !SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE) ) |
|
{ |
|
LocalFree((HLOCAL)pSD); |
|
return set_err(EIO, "SetSecurityDescriptor failed"); |
|
} |
|
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
|
sa.lpSecurityDescriptor = pSD; |
|
sa.bInheritHandle = TRUE; |
|
hmutex = CreateMutex(&sa, FALSE, mutexstr); |
|
if ( hmutex == NULL ) |
|
{ |
|
LocalFree((HLOCAL)pSD); |
|
return set_err(EIO, "CreateMutex failed"); |
|
} |
|
|
|
// atomic access to driver |
|
WaitForSingleObject(hmutex, INFINITE); |
|
bool ok = arcmsr_ata_pass_through(in,out); |
|
ReleaseMutex(hmutex); |
|
|
|
if(hmutex) |
|
{ |
|
CloseHandle(hmutex); |
|
} |
|
|
|
if ( (HLOCAL)pSD ) |
|
{ |
|
LocalFree((HLOCAL)pSD); |
|
} |
|
|
|
return ok; |
} |
} |
|
|
|
|