--- embedaddon/smartmontools/os_win32.cpp 2012/02/21 16:32:16 1.1.1.1 +++ embedaddon/smartmontools/os_win32.cpp 2012/10/09 09:36:45 1.1.1.2 @@ -3,7 +3,8 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2004-11 Christian Franke + * Copyright (C) 2004-12 Christian Franke + * Copyright (C) 2012 Hank Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -85,7 +86,7 @@ #define SELECT_WIN_32_64(x32, x64) (x64) #endif -const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp,v 1.1.1.1 2012/02/21 16:32:16 misho Exp $"; +const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp,v 1.1.1.2 2012/10/09 09:36:45 misho Exp $"; // Disable Win9x/ME specific code if no longer supported by compiler. #ifdef _WIN64 @@ -531,6 +532,57 @@ 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 @@ -543,6 +595,10 @@ class win_smart_interface (public) 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, // const char * pattern = 0); @@ -583,6 +639,8 @@ class winnt_smart_interface : public /*extends*/ win_smart_interface { public: + virtual bool disable_system_auto_standby(bool disable); + virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); @@ -590,6 +648,10 @@ class winnt_smart_interface virtual scsi_device * get_scsi_device(const char * name, const char * type); 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(); }; @@ -657,6 +719,9 @@ std::string win_smart_interface::get_os_version_str() case VER_PLATFORM_WIN32_NT <<16|0x0600| 1: w = (vi.wProductType == VER_NT_WORKSTATION ? "win7" : "2008r2"); break; + case VER_PLATFORM_WIN32_NT <<16|0x0600| 2: + w = (vi.wProductType == VER_NT_WORKSTATION ? "win8" + : "2012"); break; default: w = 0; break; } @@ -679,6 +744,31 @@ std::string win_smart_interface::get_os_version_str() return vstr; } +#ifndef __CYGWIN__ +// MSVCRT only provides ftime() which uses GetSystemTime() +// This provides only ~15ms resolution by default. +// Use QueryPerformanceCounter instead (~300ns). +// (Cygwin provides CLOCK_MONOTONIC which has the same effect) +int64_t win_smart_interface::get_timer_usec() +{ + static int64_t freq = 0; + + LARGE_INTEGER t; + if (freq == 0) + freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1); + if (freq <= 0) + return smart_interface::get_timer_usec(); + + if (!QueryPerformanceCounter(&t)) + return -1; + if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000)) + return -1; + + return (t.QuadPart * 1000000LL) / freq; +} +#endif // __CYGWIN__ + + // Return value for device detection functions enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB }; @@ -793,6 +883,62 @@ smart_device * win_smart_interface::autodetect_smart_d 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 * dev = win_smart_interface::autodetect_smart_device(name); @@ -1006,6 +1152,9 @@ std::string win_smart_interface::get_app_examples(cons " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n" " smartctl -A /dev/tw_cli/c0/p1\n" " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n" + " smartctl --all --device=areca,3/1 /dev/arcmsr0\n" + " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n" + " on 1st Areca RAID controller)\n" "\n" " ATA SMART access methods and ordering may be specified by modifiers\n" " following the device name: /dev/hdX:[saicm], where\n" @@ -1018,6 +1167,28 @@ 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 ///////////////////////////////////////////////////////////////////////////// @@ -1898,7 +2069,7 @@ bool win_tw_cli_device::open() // Show tw_cli error message err++; err[strcspn(err, "\r\n")] = 0; - return set_err(EIO, err); + return set_err(EIO, "%s", err); } return set_err(EIO); } @@ -2163,12 +2334,6 @@ static int get_identify_from_device_property(HANDLE hd ///////////////////////////////////////////////////////////////////////////// // 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 static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id) { @@ -3778,7 +3943,7 @@ bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); - pout(buff); + pout("%s", buff); } ASPI_SRB srb; @@ -4021,7 +4186,7 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cm } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); - pout(buff); + pout("%s", buff); } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; @@ -4117,6 +4282,636 @@ bool win_scsi_device::scsi_pass_through(struct scsi_cm dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } return true; +} + +// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c +static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop) +{ + int report = scsi_debugmode; // TODO + + if (report > 0) { + int k, j; + const unsigned char * ucp = iop->cmnd; + const char * np; + char buff[256]; + const int sz = (int)sizeof(buff); + + np = scsi_get_opcode_name(ucp[0]); + j = snprintf(buff, sz, " [%s: ", np ? np : ""); + for (k = 0; k < (int)iop->cmnd_len; ++k) + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); + if ((report > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " + "data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); + pout("%s", buff); + } + + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb; + if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) { + return EINVAL; + } + + memset(&sb, 0, sizeof(sb)); + sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + //sb.spt.PathId = 0; + sb.spt.TargetId = targetid; + //sb.spt.Lun = 0; + sb.spt.CdbLength = iop->cmnd_len; + memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len); + sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf); + sb.spt.SenseInfoOffset = + offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60); + + bool direct = true; + switch (iop->dxfer_dir) { + case DXFER_NONE: + sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + break; + case DXFER_FROM_DEVICE: + sb.spt.DataIn = SCSI_IOCTL_DATA_IN; + sb.spt.DataTransferLength = iop->dxfer_len; + sb.spt.DataBuffer = iop->dxferp; + // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte + // transfers (needed for SMART STATUS check of JMicron USB bridges) + if (sb.spt.DataTransferLength == 1) + direct = false; + break; + case DXFER_TO_DEVICE: + sb.spt.DataIn = SCSI_IOCTL_DATA_OUT; + sb.spt.DataTransferLength = iop->dxfer_len; + sb.spt.DataBuffer = iop->dxferp; + break; + default: + return EINVAL; + } + + long err = 0; + if (direct) { + DWORD num_out; + if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT, + &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0)) + err = GetLastError(); + } + else + err = scsi_pass_through_indirect(fd, &sb); + + if (err) + { + return err; + } + + iop->scsi_status = sb.spt.ScsiStatus; + if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) { + int slen = sb.ucSenseBuf[7] + 8; + + if (slen > (int)sizeof(sb.ucSenseBuf)) + slen = sizeof(sb.ucSenseBuf); + if (slen > (int)iop->max_sense_len) + slen = iop->max_sense_len; + memcpy(iop->sensep, sb.ucSenseBuf, slen); + iop->resp_sense_len = slen; + if (report) { + if (report > 1) { + pout(" >>> Sense buffer, len=%d:\n", slen); + dStrHex(iop->sensep, slen , 1); + } + if ((iop->sensep[0] & 0x7f) > 0x71) + pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", + iop->scsi_status, iop->sensep[1] & 0xf, + 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; }