File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / os_win32.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Oct 9 09:36:45 2012 UTC (11 years, 8 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v5_43, HEAD
smartmontools

    1: /*
    2:  * os_win32.cpp
    3:  *
    4:  * Home page of code is: http://smartmontools.sourceforge.net
    5:  *
    6:  * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
    7:  * Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
    8:  *
    9:  * This program is free software; you can redistribute it and/or modify
   10:  * it under the terms of the GNU General Public License as published by
   11:  * the Free Software Foundation; either version 2, or (at your option)
   12:  * any later version.
   13:  *
   14:  * You should have received a copy of the GNU General Public License
   15:  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
   16:  *
   17:  */
   18: 
   19: #include "config.h"
   20: #define WINVER 0x0502
   21: #define _WIN32_WINNT WINVER
   22: 
   23: #include "int64.h"
   24: #include "atacmds.h"
   25: #include "scsicmds.h"
   26: #include "utility.h"
   27: #include "smartctl.h" // TODO: Do not use smartctl only variables here
   28: 
   29: #include "dev_interface.h"
   30: #include "dev_ata_cmd_set.h"
   31: 
   32: #include "os_win32/wmiquery.h"
   33: 
   34: #include <errno.h>
   35: 
   36: #ifdef _DEBUG
   37: #include <assert.h>
   38: #else
   39: #undef assert
   40: #define assert(x) /* */
   41: #endif
   42: 
   43: #include <stddef.h> // offsetof()
   44: #include <io.h> // access()
   45: 
   46: // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
   47: #define WIN32_LEAN_AND_MEAN
   48: #include <windows.h>
   49: 
   50: #if HAVE_NTDDDISK_H
   51: // i686-w64-mingw32, x86_64-w64-mingw32
   52: // (Missing: FILE_DEVICE_SCSI)
   53: #include <devioctl.h>
   54: #include <ntdddisk.h>
   55: #include <ntddscsi.h>
   56: #include <ntddstor.h>
   57: #elif HAVE_DDK_NTDDDISK_H
   58: // i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
   59: // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
   60: #include <ddk/ntdddisk.h>
   61: #include <ddk/ntddscsi.h>
   62: #include <ddk/ntddstor.h>
   63: #else
   64: // MSVC10, older MinGW
   65: // (Missing: IOCTL_SCSI_MINIPORT_*)
   66: #include <ntddscsi.h>
   67: #include <winioctl.h>
   68: #endif
   69: 
   70: // CSMI support
   71: #include "csmisas.h"
   72: 
   73: #ifdef __CYGWIN__
   74: #include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
   75: #endif
   76: 
   77: // Macro to check constants at compile time using a dummy typedef
   78: #define ASSERT_CONST(c, n) \
   79:   typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
   80: #define ASSERT_SIZEOF(t, n) \
   81:   typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
   82: 
   83: #ifndef _WIN64
   84: #define SELECT_WIN_32_64(x32, x64) (x32)
   85: #else
   86: #define SELECT_WIN_32_64(x32, x64) (x64)
   87: #endif
   88: 
   89: const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp,v 1.1.1.2 2012/10/09 09:36:45 misho Exp $";
   90: 
   91: // Disable Win9x/ME specific code if no longer supported by compiler.
   92: #ifdef _WIN64
   93:   #undef WIN9X_SUPPORT
   94: #elif !defined(WIN9X_SUPPORT)
   95:   #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
   96:     // Win9x/ME support was dropped in Cygwin 1.7
   97:   #elif defined(_MSC_VER) && (_MSC_VER >= 1500)
   98:     // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)
   99:   #else
  100:     #define WIN9X_SUPPORT 1
  101:   #endif
  102: #endif
  103: 
  104: /////////////////////////////////////////////////////////////////////////////
  105: // Windows I/O-controls, some declarations are missing in the include files
  106: 
  107: extern "C" {
  108: 
  109: // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
  110: 
  111: ASSERT_CONST(SMART_GET_VERSION, 0x074080);
  112: ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
  113: ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088);
  114: ASSERT_SIZEOF(GETVERSIONINPARAMS, 24);
  115: ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
  116: ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
  117: 
  118: 
  119: // IDE PASS THROUGH (2000, XP, undocumented)
  120: 
  121: #ifndef IOCTL_IDE_PASS_THROUGH
  122: 
  123: #define IOCTL_IDE_PASS_THROUGH \
  124:   CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  125: 
  126: #endif // IOCTL_IDE_PASS_THROUGH
  127: 
  128: #pragma pack(1)
  129: 
  130: typedef struct {
  131:   IDEREGS IdeReg;
  132:   ULONG DataBufferSize;
  133:   UCHAR DataBuffer[1];
  134: } ATA_PASS_THROUGH;
  135: 
  136: #pragma pack()
  137: 
  138: ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
  139: ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
  140: 
  141: 
  142: // ATA PASS THROUGH (Win2003, XP SP2)
  143: 
  144: #ifndef IOCTL_ATA_PASS_THROUGH
  145: 
  146: #define IOCTL_ATA_PASS_THROUGH \
  147:   CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  148: 
  149: typedef struct _ATA_PASS_THROUGH_EX {
  150:   USHORT Length;
  151:   USHORT AtaFlags;
  152:   UCHAR PathId;
  153:   UCHAR TargetId;
  154:   UCHAR Lun;
  155:   UCHAR ReservedAsUchar;
  156:   ULONG DataTransferLength;
  157:   ULONG TimeOutValue;
  158:   ULONG ReservedAsUlong;
  159:   ULONG_PTR DataBufferOffset;
  160:   UCHAR PreviousTaskFile[8];
  161:   UCHAR CurrentTaskFile[8];
  162: } ATA_PASS_THROUGH_EX;
  163: 
  164: #define ATA_FLAGS_DRDY_REQUIRED 0x01
  165: #define ATA_FLAGS_DATA_IN       0x02
  166: #define ATA_FLAGS_DATA_OUT      0x04
  167: #define ATA_FLAGS_48BIT_COMMAND 0x08
  168: #define ATA_FLAGS_USE_DMA       0x10
  169: #define ATA_FLAGS_NO_MULTIPLE   0x20 // Vista
  170: 
  171: #endif // IOCTL_ATA_PASS_THROUGH
  172: 
  173: ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
  174: ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48));
  175: 
  176: 
  177: // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
  178: 
  179: ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
  180: ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014);
  181: ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56));
  182: ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT, SELECT_WIN_32_64(44, 56));
  183: 
  184: 
  185: // SMART IOCTL via SCSI MINIPORT ioctl
  186: 
  187: #ifndef FILE_DEVICE_SCSI
  188: #define FILE_DEVICE_SCSI 0x001b
  189: #endif
  190: 
  191: #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
  192: 
  193: #define IOCTL_SCSI_MINIPORT_SMART_VERSION               ((FILE_DEVICE_SCSI << 16) + 0x0500)
  194: #define IOCTL_SCSI_MINIPORT_IDENTIFY                    ((FILE_DEVICE_SCSI << 16) + 0x0501)
  195: #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS          ((FILE_DEVICE_SCSI << 16) + 0x0502)
  196: #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS       ((FILE_DEVICE_SCSI << 16) + 0x0503)
  197: #define IOCTL_SCSI_MINIPORT_ENABLE_SMART                ((FILE_DEVICE_SCSI << 16) + 0x0504)
  198: #define IOCTL_SCSI_MINIPORT_DISABLE_SMART               ((FILE_DEVICE_SCSI << 16) + 0x0505)
  199: #define IOCTL_SCSI_MINIPORT_RETURN_STATUS               ((FILE_DEVICE_SCSI << 16) + 0x0506)
  200: #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE     ((FILE_DEVICE_SCSI << 16) + 0x0507)
  201: #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES       ((FILE_DEVICE_SCSI << 16) + 0x0508)
  202: #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS       ((FILE_DEVICE_SCSI << 16) + 0x0509)
  203: #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
  204: #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG              ((FILE_DEVICE_SCSI << 16) + 0x050b)
  205: #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG             ((FILE_DEVICE_SCSI << 16) + 0x050c)
  206: 
  207: #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
  208: 
  209: ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
  210: ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
  211: 
  212: 
  213: // IOCTL_STORAGE_QUERY_PROPERTY
  214: 
  215: #ifndef IOCTL_STORAGE_QUERY_PROPERTY
  216: 
  217: #define IOCTL_STORAGE_QUERY_PROPERTY \
  218:   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
  219: 
  220: typedef struct _STORAGE_DEVICE_DESCRIPTOR {
  221:   ULONG Version;
  222:   ULONG Size;
  223:   UCHAR DeviceType;
  224:   UCHAR DeviceTypeModifier;
  225:   BOOLEAN RemovableMedia;
  226:   BOOLEAN CommandQueueing;
  227:   ULONG VendorIdOffset;
  228:   ULONG ProductIdOffset;
  229:   ULONG ProductRevisionOffset;
  230:   ULONG SerialNumberOffset;
  231:   STORAGE_BUS_TYPE BusType;
  232:   ULONG RawPropertiesLength;
  233:   UCHAR RawDeviceProperties[1];
  234: } STORAGE_DEVICE_DESCRIPTOR;
  235: 
  236: typedef enum _STORAGE_QUERY_TYPE {
  237:   PropertyStandardQuery = 0,
  238:   PropertyExistsQuery,
  239:   PropertyMaskQuery,
  240:   PropertyQueryMaxDefined
  241: } STORAGE_QUERY_TYPE;
  242: 
  243: typedef enum _STORAGE_PROPERTY_ID {
  244:   StorageDeviceProperty = 0,
  245:   StorageAdapterProperty,
  246:   StorageDeviceIdProperty,
  247:   StorageDeviceUniqueIdProperty,
  248:   StorageDeviceWriteCacheProperty,
  249:   StorageMiniportProperty,
  250:   StorageAccessAlignmentProperty
  251: } STORAGE_PROPERTY_ID;
  252: 
  253: typedef struct _STORAGE_PROPERTY_QUERY {
  254:   STORAGE_PROPERTY_ID PropertyId;
  255:   STORAGE_QUERY_TYPE QueryType;
  256:   UCHAR AdditionalParameters[1];
  257: } STORAGE_PROPERTY_QUERY;
  258: 
  259: #endif // IOCTL_STORAGE_QUERY_PROPERTY
  260: 
  261: ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400);
  262: ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3);
  263: ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3);
  264: 
  265: 
  266: // IOCTL_STORAGE_PREDICT_FAILURE
  267: 
  268: ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100);
  269: ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
  270: 
  271: 
  272: // 3ware specific versions of SMART ioctl structs
  273: 
  274: #define SMART_VENDOR_3WARE      0x13C1  // identifies 3ware specific parameters
  275: 
  276: #pragma pack(1)
  277: 
  278: typedef struct _GETVERSIONINPARAMS_EX {
  279:   BYTE bVersion;
  280:   BYTE bRevision;
  281:   BYTE bReserved;
  282:   BYTE bIDEDeviceMap;
  283:   DWORD fCapabilities;
  284:   DWORD dwDeviceMapEx;  // 3ware specific: RAID drive bit map
  285:   WORD wIdentifier;     // Vendor specific identifier
  286:   WORD wControllerId;   // 3ware specific: Controller ID (0,1,...)
  287:   ULONG dwReserved[2];
  288: } GETVERSIONINPARAMS_EX;
  289: 
  290: typedef struct _SENDCMDINPARAMS_EX {
  291:   DWORD cBufferSize;
  292:   IDEREGS irDriveRegs;
  293:   BYTE bDriveNumber;
  294:   BYTE bPortNumber;     // 3ware specific: port number
  295:   WORD wIdentifier;     // Vendor specific identifier
  296:   DWORD dwReserved[4];
  297:   BYTE bBuffer[1];
  298: } SENDCMDINPARAMS_EX;
  299: 
  300: #pragma pack()
  301: 
  302: ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS));
  303: ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
  304: 
  305: 
  306: // CSMI structs
  307: 
  308: ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL));
  309: ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204);
  310: ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080);
  311: ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168);
  312: 
  313: } // extern "C"
  314: 
  315: /////////////////////////////////////////////////////////////////////////////
  316: 
  317: namespace os_win32 { // no need to publish anything, name provided for Doxygen
  318: 
  319: #ifdef _MSC_VER
  320: #pragma warning(disable:4250)
  321: #endif
  322: 
  323: // Running on Win9x/ME ?
  324: #if WIN9X_SUPPORT
  325: // Set true in win9x_smart_interface ctor.
  326: static bool win9x = false;
  327: #else
  328: // Never true (const allows compiler to remove dead code).
  329: const  bool win9x = false;
  330: #endif
  331: 
  332: 
  333: class win_smart_device
  334: : virtual public /*implements*/ smart_device
  335: {
  336: public:
  337:   win_smart_device()
  338:     : smart_device(never_called),
  339:       m_fh(INVALID_HANDLE_VALUE)
  340:     { }
  341: 
  342:   virtual ~win_smart_device() throw();
  343: 
  344:   virtual bool is_open() const;
  345: 
  346:   virtual bool close();
  347: 
  348: protected:
  349:   /// Set handle for open() in derived classes.
  350:   void set_fh(HANDLE fh)
  351:     { m_fh = fh; }
  352: 
  353:   /// Return handle for derived classes.
  354:   HANDLE get_fh() const
  355:     { return m_fh; }
  356: 
  357: private:
  358:   HANDLE m_fh; ///< File handle
  359: };
  360: 
  361: 
  362: /////////////////////////////////////////////////////////////////////////////
  363: 
  364: class win_ata_device
  365: : public /*implements*/ ata_device,
  366:   public /*extends*/ win_smart_device
  367: {
  368: public:
  369:   win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
  370: 
  371:   virtual ~win_ata_device() throw();
  372: 
  373:   virtual bool open();
  374: 
  375:   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  376: 
  377:   virtual bool ata_identify_is_cached() const;
  378: 
  379: private:
  380:   bool open(int phydrive, int logdrive, const char * options, int port);
  381: 
  382:   std::string m_options;
  383:   bool m_usr_options; // options set by user?
  384:   bool m_admin; // open with admin access?
  385:   bool m_id_is_cached; // ata_identify_is_cached() return value.
  386:   bool m_is_3ware; // AMCC/3ware controller detected?
  387:   int m_drive, m_port;
  388:   int m_smartver_state;
  389: };
  390: 
  391: 
  392: /////////////////////////////////////////////////////////////////////////////
  393: 
  394: class win_scsi_device
  395: : public /*implements*/ scsi_device,
  396:   virtual public /*extends*/ win_smart_device
  397: {
  398: public:
  399:   win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
  400: 
  401:   virtual bool open();
  402: 
  403:   virtual bool scsi_pass_through(scsi_cmnd_io * iop);
  404: 
  405: private:
  406:   bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
  407: };
  408: 
  409: 
  410: /////////////////////////////////////////////////////////////////////////////
  411: 
  412: #if WIN9X_SUPPORT
  413: 
  414: class win_aspi_device
  415: : public /*implements*/ scsi_device
  416: {
  417: public:
  418:   win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type);
  419: 
  420:   virtual bool is_open() const;
  421: 
  422:   virtual bool open();
  423: 
  424:   virtual bool close();
  425: 
  426:   virtual bool scsi_pass_through(scsi_cmnd_io * iop);
  427: 
  428: private:
  429:   int m_adapter;
  430:   unsigned char m_id;
  431: };
  432: 
  433: #endif // WIN9X_SUPPORT
  434: 
  435: 
  436: //////////////////////////////////////////////////////////////////////
  437: 
  438: class csmi_device
  439: : virtual public /*extends*/ smart_device
  440: {
  441: public:
  442:   /// Get phy info
  443:   bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
  444: 
  445:   /// Check physical drive existence
  446:   bool check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no);
  447: 
  448: protected:
  449:   csmi_device()
  450:     : smart_device(never_called)
  451:     { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
  452: 
  453:   /// Select physical drive
  454:   bool select_phy(unsigned phy_no);
  455: 
  456:   /// Get info for selected physical drive
  457:   const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
  458:     { return m_phy_ent; }
  459: 
  460:   /// Call platform-specific CSMI ioctl
  461:   virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
  462:     unsigned csmi_bufsiz) = 0;
  463: 
  464: private:
  465:   CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
  466: };
  467: 
  468: 
  469: class csmi_ata_device
  470: : virtual public /*extends*/ csmi_device,
  471:   virtual public /*implements*/ ata_device
  472: {
  473: public:
  474:   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  475: 
  476: protected:
  477:   csmi_ata_device()
  478:     : smart_device(never_called) { }
  479: };
  480: 
  481: 
  482: //////////////////////////////////////////////////////////////////////
  483: 
  484: class win_csmi_device
  485: : public /*implements*/ csmi_ata_device
  486: {
  487: public:
  488:   win_csmi_device(smart_interface * intf, const char * dev_name,
  489:     const char * req_type);
  490: 
  491:   virtual ~win_csmi_device() throw();
  492: 
  493:   virtual bool open();
  494: 
  495:   virtual bool close();
  496: 
  497:   virtual bool is_open() const;
  498: 
  499:   bool open_scsi();
  500: 
  501: protected:
  502:   virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
  503:     unsigned csmi_bufsiz);
  504: 
  505: private:
  506:   HANDLE m_fh; ///< Controller device handle
  507:   unsigned m_phy_no; ///< Physical drive number
  508: };
  509: 
  510: 
  511: //////////////////////////////////////////////////////////////////////
  512: 
  513: class win_tw_cli_device
  514: : public /*implements*/ ata_device_with_command_set
  515: {
  516: public:
  517:   win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
  518: 
  519:   virtual bool is_open() const;
  520: 
  521:   virtual bool open();
  522: 
  523:   virtual bool close();
  524: 
  525: protected:
  526:   virtual int ata_command_interface(smart_command_set command, int select, char * data);
  527: 
  528: private:
  529:   bool m_ident_valid, m_smart_valid;
  530:   ata_identify_device m_ident_buf;
  531:   ata_smart_values m_smart_buf;
  532: };
  533: 
  534: 
  535: /////////////////////////////////////////////////////////////////////////////
  536: /// Areca RAID support
  537: 
  538: /* ARECA IO CONTROL CODE*/
  539: #define ARCMSR_IOCTL_READ_RQBUFFER           0x90002004
  540: #define ARCMSR_IOCTL_WRITE_WQBUFFER          0x90002008
  541: #define ARCMSR_IOCTL_CLEAR_RQBUFFER          0x9000200C
  542: #define ARCMSR_IOCTL_CLEAR_WQBUFFER          0x90002010
  543: #define ARCMSR_IOCTL_RETURN_CODE_3F          0x90002018
  544: #define ARECA_SIG_STR              "ARCMSR"
  545: 
  546: 
  547: // The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver
  548: typedef struct _SRB_IO_CONTROL
  549: {
  550:   unsigned int HeaderLength;
  551:   unsigned char Signature[8];
  552:   unsigned int Timeout;
  553:   unsigned int ControlCode;
  554:   unsigned int ReturnCode;
  555:   unsigned int Length;
  556: } sSRB_IO_CONTROL;
  557: 
  558: typedef struct _SRB_BUFFER
  559: {
  560:   sSRB_IO_CONTROL srbioctl;
  561:   unsigned char   ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware
  562: } sSRB_BUFFER;
  563: 
  564: class win_areca_device
  565: : public /*implements*/ ata_device,
  566:   public /*extends*/ win_smart_device
  567: {
  568: public:
  569:   win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum = 1);
  570: 
  571:   static int arcmsr_command_handler(HANDLE fh, unsigned long arcmsr_cmd, unsigned char *data, int data_len);
  572: 
  573: protected:
  574:   virtual bool open();
  575: 
  576:   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  577: 
  578:   bool arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  579: 
  580: private:
  581:   int m_disknum; ///< Disk number.
  582:   int m_encnum;  ///< Enclosure number.
  583: };
  584: 
  585: 
  586: //////////////////////////////////////////////////////////////////////
  587: // Platform specific interfaces
  588: 
  589: // Common to all windows flavors
  590: class win_smart_interface
  591: : public /*implements part of*/ smart_interface
  592: {
  593: public:
  594:   virtual std::string get_os_version_str();
  595: 
  596:   virtual std::string get_app_examples(const char * appname);
  597: 
  598: #ifndef __CYGWIN__
  599:   virtual int64_t get_timer_usec();
  600: #endif
  601: 
  602: //virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
  603: //  const char * pattern = 0);
  604: 
  605: protected:
  606:   virtual ata_device * get_ata_device(const char * name, const char * type);
  607: 
  608: //virtual scsi_device * get_scsi_device(const char * name, const char * type);
  609: 
  610:   virtual smart_device * autodetect_smart_device(const char * name);
  611: };
  612: 
  613: #if WIN9X_SUPPORT
  614: 
  615: // Win9x/ME reduced functionality
  616: class win9x_smart_interface
  617: : public /*extends*/ win_smart_interface
  618: {
  619: public:
  620:   win9x_smart_interface()
  621:     { win9x = true; }
  622: 
  623:   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
  624:     const char * pattern = 0);
  625: 
  626: protected:
  627:   virtual scsi_device * get_scsi_device(const char * name, const char * type);
  628: 
  629: private:
  630:   bool ata_scan(smart_device_list & devlist);
  631: 
  632:   bool scsi_scan(smart_device_list & devlist);
  633: };
  634: 
  635: #endif // WIN9X_SUPPORT
  636: 
  637: // WinNT,2000,XP,...
  638: class winnt_smart_interface
  639: : public /*extends*/ win_smart_interface
  640: {
  641: public:
  642:   virtual bool disable_system_auto_standby(bool disable);
  643: 
  644:   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
  645:     const char * pattern = 0);
  646: 
  647: protected:
  648:   virtual scsi_device * get_scsi_device(const char * name, const char * type);
  649: 
  650:   virtual smart_device * autodetect_smart_device(const char * name);
  651: 
  652:   virtual smart_device * get_custom_smart_device(const char * name, const char * type);
  653: 
  654:   virtual std::string get_valid_custom_dev_types_str();
  655: };
  656: 
  657: 
  658: //////////////////////////////////////////////////////////////////////
  659: 
  660: #ifndef _WIN64
  661: // Running on 64-bit Windows as 32-bit app ?
  662: static bool is_wow64()
  663: {
  664:   BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
  665:     (BOOL (WINAPI *)(HANDLE, PBOOL))
  666:     GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
  667:   if (!IsWow64Process_p)
  668:     return false;
  669:   BOOL w64 = FALSE;
  670:   if (!IsWow64Process_p(GetCurrentProcess(), &w64))
  671:     return false;
  672:   return !!w64;
  673: }
  674: #endif // _WIN64
  675: 
  676: // Return info string about build host and OS version
  677: std::string win_smart_interface::get_os_version_str()
  678: {
  679:   char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
  680:     = SMARTMONTOOLS_BUILD_HOST;
  681:   if (vstr[1] < '6')
  682:     vstr[1] = '6';
  683:   char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
  684:   const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
  685:   assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
  686: 
  687:   OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
  688:   vi.dwOSVersionInfoSize = sizeof(vi);
  689:   if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
  690:     memset(&vi, 0, sizeof(vi));
  691:     vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
  692:     if (!GetVersionExA((OSVERSIONINFOA *)&vi))
  693:       return vstr;
  694:   }
  695: 
  696:   if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
  697:     return vstr;
  698: 
  699:   const char * w;
  700:   switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
  701:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
  702:       w = (vi.szCSDVersion[1] == 'B' ||
  703:            vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
  704:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
  705:       w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
  706:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
  707:   //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
  708:     case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
  709:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
  710:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
  711:       w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
  712:                                                    :   "xp-mc"); break;
  713:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
  714:       w = (!GetSystemMetrics(89/*SM_SERVERR2*/)    ?   "2003"
  715:                                                    :   "2003r2"); break;
  716:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0:
  717:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "vista"
  718:                                                    :   "2008" );  break;
  719:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 1:
  720:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win7"
  721:                                                    :   "2008r2"); break;
  722:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 2:
  723:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win8"
  724:                                                    :   "2012"); break;
  725:     default: w = 0; break;
  726:   }
  727: 
  728:   const char * w64 = "";
  729: #ifndef _WIN64
  730:   if (is_wow64())
  731:     w64 = "(64)";
  732: #endif
  733: 
  734:   if (!w)
  735:     snprintf(vptr, vlen, "-%s%lu.%lu%s",
  736:       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
  737:       vi.dwMajorVersion, vi.dwMinorVersion, w64);
  738:   else if (vi.wServicePackMinor)
  739:     snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
  740:   else if (vi.wServicePackMajor)
  741:     snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
  742:   else
  743:     snprintf(vptr, vlen, "-%s%s", w, w64);
  744:   return vstr;
  745: }
  746: 
  747: #ifndef __CYGWIN__
  748: // MSVCRT only provides ftime() which uses GetSystemTime()
  749: // This provides only ~15ms resolution by default.
  750: // Use QueryPerformanceCounter instead (~300ns).
  751: // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
  752: int64_t win_smart_interface::get_timer_usec()
  753: {
  754:   static int64_t freq = 0;
  755: 
  756:   LARGE_INTEGER t;
  757:   if (freq == 0)
  758:     freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
  759:   if (freq <= 0)
  760:     return smart_interface::get_timer_usec();
  761: 
  762:   if (!QueryPerformanceCounter(&t))
  763:     return -1;
  764:   if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
  765:     return -1;
  766: 
  767:   return (t.QuadPart * 1000000LL) / freq;
  768: }
  769: #endif // __CYGWIN__
  770: 
  771: 
  772: // Return value for device detection functions
  773: enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
  774: 
  775: static win_dev_type get_phy_drive_type(int drive);
  776: static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex);
  777: static win_dev_type get_log_drive_type(int drive);
  778: static bool get_usb_id(int drive, unsigned short & vendor_id,
  779:                        unsigned short & product_id);
  780: 
  781: static const char * ata_get_def_options(void);
  782: 
  783: 
  784: static int is_permissive()
  785: {
  786:   if (!failuretest_permissive) {
  787:     pout("To continue, add one or more '-T permissive' options.\n");
  788:     return 0;
  789:   }
  790:   failuretest_permissive--;
  791:   return 1;
  792: }
  793: 
  794: // return number for drive letter, -1 on error
  795: // "[A-Za-z]:([/\\][.]?)?" => 0-25
  796: // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
  797: static int drive_letter(const char * s)
  798: {
  799:   return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
  800:           && s[1] == ':'
  801:           && (!s[2] || (   strchr("/\\\"", s[2])
  802:                         && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
  803:           (s[0] & 0x1f) - 1 : -1);
  804: }
  805: 
  806: // Skip trailing "/dev/", do not allow "/dev/X:"
  807: static const char * skipdev(const char * s)
  808: {
  809:   return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
  810: }
  811: 
  812: ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
  813: {
  814:   const char * testname = skipdev(name);
  815:   if (!strncmp(testname, "csmi", 4))
  816:     return new win_csmi_device(this, name, type);
  817:   if (!strncmp(testname, "tw_cli", 6))
  818:     return new win_tw_cli_device(this, name, type);
  819:   return new win_ata_device(this, name, type);
  820: }
  821: 
  822: #ifdef WIN9X_SUPPORT
  823: 
  824: scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type)
  825: {
  826:   return new win_aspi_device(this, name, type);
  827: }
  828: 
  829: #endif
  830: 
  831: scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type)
  832: {
  833:   const char * testname = skipdev(name);
  834:   if (!strncmp(testname, "scsi", 4))
  835: #if WIN9X_SUPPORT
  836:     return new win_aspi_device(this, name, type);
  837: #else
  838:     return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0);
  839: #endif
  840:   return new win_scsi_device(this, name, type);
  841: }
  842: 
  843: static win_dev_type get_dev_type(const char * name, int & phydrive)
  844: {
  845:   phydrive = -1;
  846:   name = skipdev(name);
  847:   if (!strncmp(name, "st", 2))
  848:     return DEV_SCSI;
  849:   if (!strncmp(name, "nst", 3))
  850:     return DEV_SCSI;
  851:   if (!strncmp(name, "tape", 4))
  852:     return DEV_SCSI;
  853: 
  854:   int logdrive = drive_letter(name);
  855:   if (logdrive >= 0) {
  856:     win_dev_type type = get_log_drive_type(logdrive);
  857:     return (type != DEV_UNKNOWN ? type : DEV_SCSI);
  858:   }
  859: 
  860:   char drive[1+1] = "";
  861:   if (sscanf(name, "sd%1[a-z]", drive) == 1) {
  862:     phydrive = drive[0] - 'a';
  863:     return get_phy_drive_type(phydrive);
  864:   }
  865: 
  866:   phydrive = -1;
  867:   if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
  868:     return get_phy_drive_type(phydrive);
  869:   return DEV_UNKNOWN;
  870: }
  871: 
  872: smart_device * win_smart_interface::autodetect_smart_device(const char * name)
  873: {
  874:   const char * testname = skipdev(name);
  875:   if (!strncmp(testname, "hd", 2))
  876:     return new win_ata_device(this, name, "");
  877: #if WIN9X_SUPPORT
  878:   if (!strncmp(testname, "scsi", 4))
  879:     return new win_aspi_device(this, name, "");
  880: #endif
  881:   if (!strncmp(testname, "tw_cli", 6))
  882:     return new win_tw_cli_device(this, name, "");
  883:   return 0;
  884: }
  885: 
  886: 
  887: smart_device * winnt_smart_interface::get_custom_smart_device(const char * name, const char * type)
  888: {
  889:   // Areca?
  890:   int disknum = -1, n1 = -1, n2 = -1;
  891:   int encnum = 1;
  892:   HANDLE fh = INVALID_HANDLE_VALUE;
  893:   char devpath[32];
  894: 
  895:   if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
  896:     if (!(1 <= disknum && disknum <= 128)) {
  897:       set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
  898:       return 0;
  899:     }
  900:     if (!(1 <= encnum && encnum <= 8)) {
  901:       set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
  902:       return 0;
  903:     }
  904: 
  905:     name = skipdev(name);
  906: #define ARECA_MAX_CTLR_NUM  16
  907:     n1 = -1;
  908:     int ctlrindex = 0;
  909:     if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) {
  910:       /*
  911:        1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
  912:        2. map arcmsrX into "\\\\.\\scsiX"
  913:       */
  914:       for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
  915:         memset(devpath, 0, sizeof(devpath));
  916:         sprintf(devpath, "\\\\.\\scsi%d:", idx);
  917:         if ( (fh = CreateFile( devpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
  918:                                NULL, OPEN_EXISTING, 0, NULL )) != INVALID_HANDLE_VALUE ) {
  919:           if (win_areca_device::arcmsr_command_handler(fh, ARCMSR_IOCTL_RETURN_CODE_3F, NULL, 0) == 0) {
  920:             if (ctlrindex-- == 0) {
  921:               return new win_areca_device(this, devpath, fh, disknum, encnum);
  922:             }
  923:           }
  924:           CloseHandle(fh);
  925:         }
  926:       }
  927:       set_err(ENOENT, "No Areca controller found");
  928:     }
  929:     else
  930:       set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
  931:   }
  932: 
  933:   return 0;
  934: }
  935: 
  936: std::string winnt_smart_interface::get_valid_custom_dev_types_str()
  937: {
  938:   return "areca,N[/E]";
  939: }
  940: 
  941: 
  942: smart_device * winnt_smart_interface::autodetect_smart_device(const char * name)
  943: {
  944:   smart_device * dev = win_smart_interface::autodetect_smart_device(name);
  945:   if (dev)
  946:     return dev;
  947: 
  948:   if (!strncmp(skipdev(name), "csmi", 4))
  949:     return new win_csmi_device(this, name, "");
  950: 
  951:   int phydrive = -1;
  952:   win_dev_type type = get_dev_type(name, phydrive);
  953: 
  954:   if (type == DEV_ATA)
  955:     return new win_ata_device(this, name, "");
  956:   if (type == DEV_SCSI)
  957:     return new win_scsi_device(this, name, "");
  958: 
  959:   if (type == DEV_USB) {
  960:     // Get USB bridge ID
  961:     unsigned short vendor_id = 0, product_id = 0;
  962:     if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
  963:       set_err(EINVAL, "Unable to read USB device ID");
  964:       return 0;
  965:     }
  966:     // Get type name for this ID
  967:     const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
  968:     if (!usbtype)
  969:       return 0;
  970:     // Return SAT/USB device for this type
  971:     return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
  972:   }
  973: 
  974:   return 0;
  975: }
  976: 
  977: 
  978: #if WIN9X_SUPPORT
  979: 
  980: // Scan for devices on Win9x/ME
  981: 
  982: bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist,
  983:   const char * type, const char * pattern /* = 0*/)
  984: {
  985:   if (pattern) {
  986:     set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
  987:     return false;
  988:   }
  989: 
  990:   if (!type || !strcmp(type, "ata")) {
  991:     if (!ata_scan(devlist))
  992:       return false;
  993:   }
  994: 
  995:   if (!type || !strcmp(type, "scsi")) {
  996:     if (!scsi_scan(devlist))
  997:       return false;
  998:   }
  999:   return true;
 1000: }
 1001: 
 1002: #endif  // WIN9X_SUPPORT
 1003: 
 1004: 
 1005: // Scan for devices
 1006: 
 1007: bool winnt_smart_interface::scan_smart_devices(smart_device_list & devlist,
 1008:   const char * type, const char * pattern /* = 0*/)
 1009: {
 1010:   if (pattern) {
 1011:     set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
 1012:     return false;
 1013:   }
 1014: 
 1015:   // Set valid types
 1016:   bool ata, scsi, usb, csmi;
 1017:   if (!type) {
 1018:     ata = scsi = usb = csmi = true;
 1019:   }
 1020:   else {
 1021:     ata = scsi = usb = csmi = false;
 1022:     if (!strcmp(type, "ata"))
 1023:       ata = true;
 1024:     else if (!strcmp(type, "scsi"))
 1025:       scsi = true;
 1026:     else if (!strcmp(type, "usb"))
 1027:       usb = true;
 1028:     else if (!strcmp(type, "csmi"))
 1029:       csmi = true;
 1030:     else {
 1031:       set_err(EINVAL, "Invalid type '%s', valid arguments are: ata, scsi, usb, csmi", type);
 1032:       return false;
 1033:     }
 1034:   }
 1035: 
 1036:   // Scan up to 10 drives and 2 3ware controllers
 1037:   const int max_raid = 2;
 1038:   bool raid_seen[max_raid] = {false, false};
 1039: 
 1040:   char name[20];
 1041:   for (int i = 0; i <= 9; i++) {
 1042:     sprintf(name, "/dev/sd%c", 'a'+i);
 1043:     GETVERSIONINPARAMS_EX vers_ex;
 1044: 
 1045:     switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {
 1046:       case DEV_ATA:
 1047:         // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
 1048:         if (!ata)
 1049:           continue;
 1050: 
 1051:         // Interpret RAID drive map if present
 1052:         if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
 1053:           // Skip if too many controllers or logical drive from this controller already seen
 1054:           if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
 1055:             continue;
 1056:           raid_seen[vers_ex.wControllerId] = true;
 1057:           // Add physical drives
 1058:           int len = strlen(name);
 1059:           for (int pi = 0; pi < 32; pi++) {
 1060:             if (vers_ex.dwDeviceMapEx & (1L << pi)) {
 1061:               sprintf(name+len, ",%u", pi);
 1062:               devlist.push_back( new win_ata_device(this, name, "ata") );
 1063:             }
 1064:           }
 1065:         }
 1066:         else {
 1067:           devlist.push_back( new win_ata_device(this, name, "ata") );
 1068:         }
 1069:         break;
 1070: 
 1071:       case DEV_SCSI:
 1072:         // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
 1073:         if (!scsi)
 1074:           continue;
 1075:         devlist.push_back( new win_scsi_device(this, name, "scsi") );
 1076:         break;
 1077: 
 1078:       case DEV_USB:
 1079:         // STORAGE_QUERY_PROPERTY returned USB
 1080:         if (!usb)
 1081:           continue;
 1082:         {
 1083:           // TODO: Use common function for this and autodetect_smart_device()
 1084:           // Get USB bridge ID
 1085:           unsigned short vendor_id = 0, product_id = 0;
 1086:           if (!get_usb_id(i, vendor_id, product_id))
 1087:             continue;
 1088:           // Get type name for this ID
 1089:           const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
 1090:           if (!usbtype)
 1091:             continue;
 1092:           // Return SAT/USB device for this type
 1093:           ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, ""));
 1094:           if (!dev)
 1095:             continue;
 1096:           devlist.push_back(dev);
 1097:         }
 1098:         break;
 1099: 
 1100:       default:
 1101:         // Unknown type
 1102:         break;
 1103:     }
 1104:   }
 1105: 
 1106:   if (csmi) {
 1107:     // Scan CSMI devices
 1108:     for (int i = 0; i <= 9; i++) {
 1109:       snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
 1110:       win_csmi_device test_dev(this, name, "");
 1111:       if (!test_dev.open_scsi())
 1112:         continue;
 1113:       CSMI_SAS_PHY_INFO phy_info;
 1114:       if (!test_dev.get_phy_info(phy_info))
 1115:         continue;
 1116: 
 1117:       for (int pi = 0; pi < phy_info.bNumberOfPhys; pi++) {
 1118:         if (!test_dev.check_phy(phy_info, pi))
 1119:           continue;
 1120:         snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
 1121:         devlist.push_back( new win_csmi_device(this, name, "ata") );
 1122:       }
 1123:     }
 1124:   }
 1125:   return true;
 1126: }
 1127: 
 1128: 
 1129: // get examples for smartctl
 1130: std::string win_smart_interface::get_app_examples(const char * appname)
 1131: {
 1132:   if (strcmp(appname, "smartctl"))
 1133:     return "";
 1134:   return "=================================================== SMARTCTL EXAMPLES =====\n\n"
 1135:          "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
 1136:          "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
 1137:          "                                              (Enables SMART on first disk)\n\n"
 1138:          "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
 1139:          "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
 1140:          "                                      (Prints Self-Test & Attribute errors)\n"
 1141: #if WIN9X_SUPPORT
 1142:          "  smartctl -a /dev/scsi21\n"
 1143:          "             (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
 1144: #endif
 1145:          "  smartctl -a /dev/sda\n"
 1146:          "             (Prints all information for SCSI disk on PhysicalDrive 0)\n"
 1147:          "  smartctl -a /dev/pd3\n"
 1148:          "             (Prints all information for SCSI disk on PhysicalDrive 3)\n"
 1149:          "  smartctl -a /dev/tape1\n"
 1150:          "             (Prints all information for SCSI tape on Tape 1)\n"
 1151:          "  smartctl -A /dev/hdb,3\n"
 1152:          "                (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
 1153:          "  smartctl -A /dev/tw_cli/c0/p1\n"
 1154:          "            (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
 1155:          "  smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
 1156:          "           (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
 1157:          "            on 1st Areca RAID controller)\n"
 1158:          "\n"
 1159:          "  ATA SMART access methods and ordering may be specified by modifiers\n"
 1160:          "  following the device name: /dev/hdX:[saicm], where\n"
 1161:          "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
 1162:          "  'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
 1163:          "  'f': IOCTL_STORAGE_*,        'm': IOCTL_SCSI_MINIPORT_*.\n"
 1164:       + strprintf(
 1165:          "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
 1166:         );
 1167: }
 1168: 
 1169: 
 1170: bool winnt_smart_interface::disable_system_auto_standby(bool disable)
 1171: {
 1172:   if (disable) {
 1173:     SYSTEM_POWER_STATUS ps;
 1174:     if (!GetSystemPowerStatus(&ps))
 1175:       return set_err(ENOSYS, "Unknown power status");
 1176:     if (ps.ACLineStatus != 1) {
 1177:       SetThreadExecutionState(ES_CONTINUOUS);
 1178:       if (ps.ACLineStatus == 0)
 1179:         set_err(EIO, "AC offline");
 1180:       else
 1181:         set_err(EIO, "Unknown AC line status");
 1182:       return false;
 1183:     }
 1184:   }
 1185: 
 1186:   if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
 1187:     return set_err(ENOSYS);
 1188:   return true;
 1189: }
 1190: 
 1191: 
 1192: /////////////////////////////////////////////////////////////////////////////
 1193: // ATA Interface
 1194: /////////////////////////////////////////////////////////////////////////////
 1195: 
 1196: #define SMART_CYL_LOW  0x4F
 1197: #define SMART_CYL_HI   0xC2
 1198: 
 1199: static void print_ide_regs(const IDEREGS * r, int out)
 1200: {
 1201:   pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
 1202:   (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
 1203:   r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
 1204: }
 1205: 
 1206: static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
 1207: {
 1208:   pout("    Input : "); print_ide_regs(ri, 0);
 1209:   if (ro) {
 1210:     pout("    Output: "); print_ide_regs(ro, 1);
 1211:   }
 1212: }
 1213: 
 1214: /////////////////////////////////////////////////////////////////////////////
 1215: 
 1216: // call SMART_GET_VERSION, return device map or -1 on error
 1217: 
 1218: static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 1219: {
 1220:   GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
 1221:   const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
 1222:   DWORD num_out;
 1223: 
 1224:   if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
 1225:     NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
 1226:     if (ata_debugmode)
 1227:       pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
 1228:     errno = ENOSYS;
 1229:     return -1;
 1230:   }
 1231:   assert(num_out == sizeof(GETVERSIONINPARAMS));
 1232: 
 1233:   if (ata_debugmode > 1) {
 1234:     pout("  SMART_GET_VERSION suceeded, bytes returned: %lu\n"
 1235:          "    Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
 1236:       num_out, vers.bVersion, vers.bRevision,
 1237:       vers.fCapabilities, vers.bIDEDeviceMap);
 1238:     if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
 1239:       pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
 1240:       vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
 1241:   }
 1242: 
 1243:   if (ata_version_ex)
 1244:     *ata_version_ex = vers_ex;
 1245: 
 1246:   // TODO: Check vers.fCapabilities here?
 1247:   return vers.bIDEDeviceMap;
 1248: }
 1249: 
 1250: 
 1251: // call SMART_* ioctl
 1252: 
 1253: static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
 1254: {
 1255:   SENDCMDINPARAMS inpar;
 1256:   SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
 1257: 
 1258:   unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
 1259:   const SENDCMDOUTPARAMS * outpar;
 1260:   DWORD code, num_out;
 1261:   unsigned int size_out;
 1262:   const char * name;
 1263: 
 1264:   memset(&inpar, 0, sizeof(inpar));
 1265:   inpar.irDriveRegs = *regs;
 1266:   // drive is set to 0-3 on Win9x only
 1267:   inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
 1268:   inpar.bDriveNumber = drive;
 1269: 
 1270:   if (port >= 0) {
 1271:     // Set RAID port
 1272:     inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
 1273:     inpar_ex.bPortNumber = port;
 1274:   }
 1275: 
 1276:   if (datasize == 512) {
 1277:     code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
 1278:     inpar.cBufferSize = size_out = 512;
 1279:   }
 1280:   else if (datasize == 0) {
 1281:     code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
 1282:     if (regs->bFeaturesReg == ATA_SMART_STATUS)
 1283:       size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
 1284:       // Note: cBufferSize must be 0 on Win9x
 1285:     else
 1286:       size_out = 0;
 1287:   }
 1288:   else {
 1289:     errno = EINVAL;
 1290:     return -1;
 1291:   }
 1292: 
 1293:   memset(&outbuf, 0, sizeof(outbuf));
 1294: 
 1295:   if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
 1296:     outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
 1297:     // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
 1298:     long err = GetLastError();
 1299:     if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
 1300:       pout("  %s failed, Error=%ld\n", name, err);
 1301:       print_ide_regs_io(regs, NULL);
 1302:     }
 1303:     errno = (   err == ERROR_INVALID_FUNCTION/*9x*/
 1304:              || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
 1305:              || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1306:     return -1;
 1307:   }
 1308:   // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
 1309: 
 1310:   outpar = (const SENDCMDOUTPARAMS *)outbuf;
 1311: 
 1312:   if (outpar->DriverStatus.bDriverError) {
 1313:     if (ata_debugmode) {
 1314:       pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
 1315:         outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
 1316:       print_ide_regs_io(regs, NULL);
 1317:     }
 1318:     errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
 1319:     return -1;
 1320:   }
 1321: 
 1322:   if (ata_debugmode > 1) {
 1323:     pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
 1324:       num_out, outpar->cBufferSize);
 1325:     print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
 1326:       (const IDEREGS *)(outpar->bBuffer) : NULL));
 1327:   }
 1328: 
 1329:   if (datasize)
 1330:     memcpy(data, outpar->bBuffer, 512);
 1331:   else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
 1332:     if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
 1333:       memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
 1334:     else {  // Workaround for driver not returning regs
 1335:       if (ata_debugmode)
 1336:         pout("  WARNING: driver does not return ATA registers in output buffer!\n");
 1337:       *regs = inpar.irDriveRegs;
 1338:     }
 1339:   }
 1340: 
 1341:   return 0;
 1342: }
 1343: 
 1344: 
 1345: /////////////////////////////////////////////////////////////////////////////
 1346: // IDE PASS THROUGH (2000, XP, undocumented)
 1347: //
 1348: // Based on WinATA.cpp, 2002 c't/Matthias Withopf
 1349: // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
 1350: 
 1351: static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
 1352: {
 1353:   if (datasize > 512) {
 1354:     errno = EINVAL;
 1355:     return -1;
 1356:   }
 1357:   unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
 1358:   ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
 1359:   DWORD num_out;
 1360:   const unsigned char magic = 0xcf;
 1361: 
 1362:   if (!buf) {
 1363:     errno = ENOMEM;
 1364:     return -1;
 1365:   }
 1366: 
 1367:   buf->IdeReg = *regs;
 1368:   buf->DataBufferSize = datasize;
 1369:   if (datasize)
 1370:     buf->DataBuffer[0] = magic;
 1371: 
 1372:   if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
 1373:     buf, size, buf, size, &num_out, NULL)) {
 1374:     long err = GetLastError();
 1375:     if (ata_debugmode) {
 1376:       pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
 1377:       print_ide_regs_io(regs, NULL);
 1378:     }
 1379:     VirtualFree(buf, 0, MEM_RELEASE);
 1380:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1381:     return -1;
 1382:   }
 1383: 
 1384:   // Check ATA status
 1385:   if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
 1386:     if (ata_debugmode) {
 1387:       pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
 1388:       print_ide_regs_io(regs, &buf->IdeReg);
 1389:     }
 1390:     VirtualFree(buf, 0, MEM_RELEASE);
 1391:     errno = EIO;
 1392:     return -1;
 1393:   }
 1394: 
 1395:   // Check and copy data
 1396:   if (datasize) {
 1397:     if (   num_out != size
 1398:         || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
 1399:       if (ata_debugmode) {
 1400:         pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
 1401:           num_out, buf->DataBufferSize);
 1402:         print_ide_regs_io(regs, &buf->IdeReg);
 1403:       }
 1404:       VirtualFree(buf, 0, MEM_RELEASE);
 1405:       errno = EIO;
 1406:       return -1;
 1407:     }
 1408:     memcpy(data, buf->DataBuffer, datasize);
 1409:   }
 1410: 
 1411:   if (ata_debugmode > 1) {
 1412:     pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
 1413:       num_out, buf->DataBufferSize);
 1414:     print_ide_regs_io(regs, &buf->IdeReg);
 1415:   }
 1416:   *regs = buf->IdeReg;
 1417: 
 1418:   // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
 1419:   VirtualFree(buf, 0, MEM_RELEASE);
 1420:   return 0;
 1421: }
 1422: 
 1423: 
 1424: /////////////////////////////////////////////////////////////////////////////
 1425: // ATA PASS THROUGH (Win2003, XP SP2)
 1426: 
 1427: // Warning:
 1428: // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
 1429: // transfer per command. Therefore, multi-sector transfers are only supported
 1430: // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
 1431: // or READ/WRITE LOG EXT work only with single sector transfers.
 1432: // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
 1433: // See:
 1434: // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
 1435: 
 1436: static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
 1437: {
 1438:   const int max_sectors = 32; // TODO: Allocate dynamic buffer
 1439: 
 1440:   typedef struct {
 1441:     ATA_PASS_THROUGH_EX apt;
 1442:     ULONG Filler;
 1443:     UCHAR ucDataBuf[max_sectors * 512];
 1444:   } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
 1445: 
 1446:   const unsigned char magic = 0xcf;
 1447: 
 1448:   ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
 1449:   ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
 1450:   //ab.apt.PathId = 0;
 1451:   //ab.apt.TargetId = 0;
 1452:   //ab.apt.Lun = 0;
 1453:   ab.apt.TimeOutValue = 10;
 1454:   unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
 1455:   ab.apt.DataBufferOffset = size;
 1456: 
 1457:   if (datasize > 0) {
 1458:     if (datasize > (int)sizeof(ab.ucDataBuf)) {
 1459:       errno = EINVAL;
 1460:       return -1;
 1461:     }
 1462:     ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
 1463:     ab.apt.DataTransferLength = datasize;
 1464:     size += datasize;
 1465:     ab.ucDataBuf[0] = magic;
 1466:   }
 1467:   else if (datasize < 0) {
 1468:     if (-datasize > (int)sizeof(ab.ucDataBuf)) {
 1469:       errno = EINVAL;
 1470:       return -1;
 1471:     }
 1472:     ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
 1473:     ab.apt.DataTransferLength = -datasize;
 1474:     size += -datasize;
 1475:     memcpy(ab.ucDataBuf, data, -datasize);
 1476:   }
 1477:   else {
 1478:     assert(ab.apt.AtaFlags == 0);
 1479:     assert(ab.apt.DataTransferLength == 0);
 1480:   }
 1481: 
 1482:   assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
 1483:   IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
 1484:   IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
 1485:   *ctfregs = *regs;
 1486: 
 1487:   if (prev_regs) {
 1488:     *ptfregs = *prev_regs;
 1489:     ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
 1490:   }
 1491: 
 1492:   DWORD num_out;
 1493:   if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
 1494:     &ab, size, &ab, size, &num_out, NULL)) {
 1495:     long err = GetLastError();
 1496:     if (ata_debugmode) {
 1497:       pout("  IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
 1498:       print_ide_regs_io(regs, NULL);
 1499:     }
 1500:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1501:     return -1;
 1502:   }
 1503: 
 1504:   // Check ATA status
 1505:   if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
 1506:     if (ata_debugmode) {
 1507:       pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
 1508:       print_ide_regs_io(regs, ctfregs);
 1509:     }
 1510:     errno = EIO;
 1511:     return -1;
 1512:   }
 1513: 
 1514:   // Check and copy data
 1515:   if (datasize > 0) {
 1516:     if (   num_out != size
 1517:         || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
 1518:       if (ata_debugmode) {
 1519:         pout("  IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);
 1520:         print_ide_regs_io(regs, ctfregs);
 1521:       }
 1522:       errno = EIO;
 1523:       return -1;
 1524:     }
 1525:     memcpy(data, ab.ucDataBuf, datasize);
 1526:   }
 1527: 
 1528:   if (ata_debugmode > 1) {
 1529:     pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
 1530:     print_ide_regs_io(regs, ctfregs);
 1531:   }
 1532:   *regs = *ctfregs;
 1533:   if (prev_regs)
 1534:     *prev_regs = *ptfregs;
 1535: 
 1536:   return 0;
 1537: }
 1538: 
 1539: 
 1540: /////////////////////////////////////////////////////////////////////////////
 1541: // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
 1542: 
 1543: // undocumented SCSI opcode to for ATA passthrough
 1544: #define SCSIOP_ATA_PASSTHROUGH    0xCC
 1545: 
 1546: static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
 1547: {
 1548:   typedef struct {
 1549:     SCSI_PASS_THROUGH spt;
 1550:     ULONG Filler;
 1551:     UCHAR ucSenseBuf[32];
 1552:     UCHAR ucDataBuf[512];
 1553:   } SCSI_PASS_THROUGH_WITH_BUFFERS;
 1554: 
 1555:   SCSI_PASS_THROUGH_WITH_BUFFERS sb;
 1556:   IDEREGS * cdbregs;
 1557:   unsigned int size;
 1558:   DWORD num_out;
 1559:   const unsigned char magic = 0xcf;
 1560: 
 1561:   memset(&sb, 0, sizeof(sb));
 1562:   sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
 1563:   //sb.spt.PathId = 0;
 1564:   sb.spt.TargetId = 1;
 1565:   //sb.spt.Lun = 0;
 1566:   sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
 1567:   sb.spt.TimeOutValue = 10;
 1568:   sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
 1569:   size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
 1570:   sb.spt.DataBufferOffset = size;
 1571: 
 1572:   if (datasize) {
 1573:     if (datasize > sizeof(sb.ucDataBuf)) {
 1574:       errno = EINVAL;
 1575:       return -1;
 1576:     }
 1577:     sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
 1578:     sb.spt.DataTransferLength = datasize;
 1579:     size += datasize;
 1580:     sb.ucDataBuf[0] = magic;
 1581:   }
 1582:   else {
 1583:     sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
 1584:     //sb.spt.DataTransferLength = 0;
 1585:   }
 1586: 
 1587:   // Use pseudo SCSI command followed by registers
 1588:   sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
 1589:   cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
 1590:   *cdbregs = *regs;
 1591: 
 1592:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
 1593:     &sb, size, &sb, size, &num_out, NULL)) {
 1594:     long err = GetLastError();
 1595:     if (ata_debugmode)
 1596:       pout("  ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
 1597:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1598:     return -1;
 1599:   }
 1600: 
 1601:   // Cannot check ATA status, because command does not return IDEREGS
 1602: 
 1603:   // Check and copy data
 1604:   if (datasize) {
 1605:     if (   num_out != size
 1606:         || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
 1607:       if (ata_debugmode) {
 1608:         pout("  ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
 1609:         print_ide_regs_io(regs, NULL);
 1610:       }
 1611:       errno = EIO;
 1612:       return -1;
 1613:     }
 1614:     memcpy(data, sb.ucDataBuf, datasize);
 1615:   }
 1616: 
 1617:   if (ata_debugmode > 1) {
 1618:     pout("  ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
 1619:     print_ide_regs_io(regs, NULL);
 1620:   }
 1621:   return 0;
 1622: }
 1623: 
 1624: 
 1625: /////////////////////////////////////////////////////////////////////////////
 1626: // SMART IOCTL via SCSI MINIPORT ioctl
 1627: 
 1628: // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
 1629: // miniport driver (via SCSI port driver scsiport.sys).
 1630: // It can be used to skip the missing or broken handling of some SMART
 1631: // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
 1632: 
 1633: static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
 1634: {
 1635:   // Select code
 1636:   DWORD code = 0; const char * name = 0;
 1637:   if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
 1638:     code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
 1639:   }
 1640:   else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
 1641:     case ATA_SMART_READ_VALUES:
 1642:       code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
 1643:     case ATA_SMART_READ_THRESHOLDS:
 1644:       code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
 1645:     case ATA_SMART_ENABLE:
 1646:       code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
 1647:     case ATA_SMART_DISABLE:
 1648:       code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
 1649:     case ATA_SMART_STATUS:
 1650:       code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
 1651:     case ATA_SMART_AUTOSAVE:
 1652:       code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
 1653:   //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
 1654:   //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
 1655:     case ATA_SMART_IMMEDIATE_OFFLINE:
 1656:       code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
 1657:     case ATA_SMART_AUTO_OFFLINE:
 1658:       code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
 1659:     case ATA_SMART_READ_LOG_SECTOR:
 1660:       code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
 1661:     case ATA_SMART_WRITE_LOG_SECTOR:
 1662:       code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
 1663:   }
 1664:   if (!code) {
 1665:     errno = ENOSYS;
 1666:     return -1;
 1667:   }
 1668: 
 1669:   // Set SRB
 1670:   struct {
 1671:     SRB_IO_CONTROL srbc;
 1672:     union {
 1673:       SENDCMDINPARAMS in;
 1674:       SENDCMDOUTPARAMS out;
 1675:     } params;
 1676:     char space[512-1];
 1677:   } sb;
 1678:   ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
 1679:   memset(&sb, 0, sizeof(sb));
 1680: 
 1681:   unsigned size;
 1682:   if (datasize > 0) {
 1683:     if (datasize > (int)sizeof(sb.space)+1) {
 1684:       errno = EINVAL;
 1685:       return -1;
 1686:     }
 1687:     size = datasize;
 1688:   }
 1689:   else if (datasize < 0) {
 1690:     if (-datasize > (int)sizeof(sb.space)+1) {
 1691:       errno = EINVAL;
 1692:       return -1;
 1693:     }
 1694:     size = -datasize;
 1695:     memcpy(sb.params.in.bBuffer, data, size);
 1696:   }
 1697:   else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
 1698:     size = sizeof(IDEREGS);
 1699:   else
 1700:     size = 0;
 1701:   sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1702:   memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
 1703:   sb.srbc.Timeout = 60; // seconds
 1704:   sb.srbc.ControlCode = code;
 1705:   //sb.srbc.ReturnCode = 0;
 1706:   sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
 1707:   sb.params.in.irDriveRegs = *regs;
 1708:   sb.params.in.cBufferSize = size;
 1709: 
 1710:   // Call miniport ioctl
 1711:   size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
 1712:   DWORD num_out;
 1713:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1714:     &sb, size, &sb, size, &num_out, NULL)) {
 1715:     long err = GetLastError();
 1716:     if (ata_debugmode) {
 1717:       pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
 1718:       print_ide_regs_io(regs, NULL);
 1719:     }
 1720:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1721:     return -1;
 1722:   }
 1723: 
 1724:   // Check result
 1725:   if (sb.srbc.ReturnCode) {
 1726:     if (ata_debugmode) {
 1727:       pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
 1728:       print_ide_regs_io(regs, NULL);
 1729:     }
 1730:     errno = EIO;
 1731:     return -1;
 1732:   }
 1733: 
 1734:   if (sb.params.out.DriverStatus.bDriverError) {
 1735:     if (ata_debugmode) {
 1736:       pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
 1737:         sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
 1738:       print_ide_regs_io(regs, NULL);
 1739:     }
 1740:     errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
 1741:     return -1;
 1742:   }
 1743: 
 1744:   if (ata_debugmode > 1) {
 1745:     pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
 1746:       num_out, sb.params.out.cBufferSize);
 1747:     print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
 1748:                              (const IDEREGS *)(sb.params.out.bBuffer) : 0));
 1749:   }
 1750: 
 1751:   if (datasize > 0)
 1752:     memcpy(data, sb.params.out.bBuffer, datasize);
 1753:   else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
 1754:     memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
 1755: 
 1756:   return 0;
 1757: }
 1758: 
 1759: 
 1760: /////////////////////////////////////////////////////////////////////////////
 1761: 
 1762: // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
 1763: 
 1764: static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
 1765: {
 1766:   struct {
 1767:     SRB_IO_CONTROL srbc;
 1768:     IDEREGS regs;
 1769:     UCHAR buffer[512];
 1770:   } sb;
 1771:   ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
 1772: 
 1773:   if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
 1774:     errno = EINVAL;
 1775:     return -1;
 1776:   }
 1777:   memset(&sb, 0, sizeof(sb));
 1778:   strcpy((char *)sb.srbc.Signature, "<3ware>");
 1779:   sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1780:   sb.srbc.Timeout = 60; // seconds
 1781:   sb.srbc.ControlCode = 0xA0000000;
 1782:   sb.srbc.ReturnCode = 0;
 1783:   sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
 1784:   sb.regs = *regs;
 1785:   sb.regs.bReserved = port;
 1786: 
 1787:   DWORD num_out;
 1788:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1789:     &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
 1790:     long err = GetLastError();
 1791:     if (ata_debugmode) {
 1792:       pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
 1793:       print_ide_regs_io(regs, NULL);
 1794:     }
 1795:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 1796:     return -1;
 1797:   }
 1798: 
 1799:   if (sb.srbc.ReturnCode) {
 1800:     if (ata_debugmode) {
 1801:       pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
 1802:       print_ide_regs_io(regs, NULL);
 1803:     }
 1804:     errno = EIO;
 1805:     return -1;
 1806:   }
 1807: 
 1808:   // Copy data
 1809:   if (datasize > 0)
 1810:     memcpy(data, sb.buffer, datasize);
 1811: 
 1812:   if (ata_debugmode > 1) {
 1813:     pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
 1814:     print_ide_regs_io(regs, &sb.regs);
 1815:   }
 1816:   *regs = sb.regs;
 1817: 
 1818:   return 0;
 1819: }
 1820: 
 1821: 
 1822: /////////////////////////////////////////////////////////////////////////////
 1823: 
 1824: // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
 1825: // 3DM/CLI "Rescan Controller" function does not to always update it.
 1826: 
 1827: static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 1828: {
 1829:   SRB_IO_CONTROL srbc;
 1830:   memset(&srbc, 0, sizeof(srbc));
 1831:   strcpy((char *)srbc.Signature, "<3ware>");
 1832:   srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1833:   srbc.Timeout = 60; // seconds
 1834:   srbc.ControlCode = 0xCC010014;
 1835:   srbc.ReturnCode = 0;
 1836:   srbc.Length = 0;
 1837: 
 1838:   DWORD num_out;
 1839:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1840:     &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
 1841:     long err = GetLastError();
 1842:     if (ata_debugmode)
 1843:       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
 1844:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 1845:     return -1;
 1846:   }
 1847:   if (srbc.ReturnCode) {
 1848:     if (ata_debugmode)
 1849:       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
 1850:     errno = EIO;
 1851:     return -1;
 1852:   }
 1853:   if (ata_debugmode > 1)
 1854:     pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
 1855:   return 0;
 1856: }
 1857: 
 1858: 
 1859: 
 1860: /////////////////////////////////////////////////////////////////////////////
 1861: 
 1862: // Routines for pseudo device /dev/tw_cli/*
 1863: // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
 1864: 
 1865: 
 1866: // Get clipboard data
 1867: 
 1868: static int get_clipboard(char * data, int datasize)
 1869: {
 1870:   if (!OpenClipboard(NULL))
 1871:     return -1;
 1872:   HANDLE h = GetClipboardData(CF_TEXT);
 1873:   if (!h) {
 1874:     CloseClipboard();
 1875:     return 0;
 1876:   }
 1877:   const void * p = GlobalLock(h);
 1878:   int n = GlobalSize(h);
 1879:   if (n > datasize)
 1880:     n = datasize;
 1881:   memcpy(data, p, n);
 1882:   GlobalFree(h);
 1883:   CloseClipboard();
 1884:   return n;
 1885: }
 1886: 
 1887: 
 1888: // Run a command, write stdout to dataout
 1889: // TODO: Combine with daemon_win32.cpp:daemon_spawn()
 1890: 
 1891: static int run_cmd(const char * cmd, char * dataout, int outsize)
 1892: {
 1893:   // Create stdout pipe
 1894:   SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
 1895:   HANDLE pipe_out_w, h;
 1896:   if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
 1897:     return -1;
 1898:   HANDLE self = GetCurrentProcess();
 1899:   HANDLE pipe_out_r;
 1900:   if (!DuplicateHandle(self, h, self, &pipe_out_r,
 1901:     GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
 1902:     CloseHandle(pipe_out_w);
 1903:     return -1;
 1904:   }
 1905:   HANDLE pipe_err_w;
 1906:   if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
 1907:     0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
 1908:     CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
 1909:     return -1;
 1910:   }
 1911: 
 1912:   // Create process
 1913:   STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
 1914:   si.hStdInput  = INVALID_HANDLE_VALUE;
 1915:   si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
 1916:   si.dwFlags = STARTF_USESTDHANDLES;
 1917:   PROCESS_INFORMATION pi;
 1918:   if (!CreateProcess(
 1919:     NULL, const_cast<char *>(cmd),
 1920:     NULL, NULL, TRUE/*inherit*/,
 1921:     CREATE_NO_WINDOW/*do not create a new console window*/,
 1922:     NULL, NULL, &si, &pi)) {
 1923:     CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
 1924:     return -1;
 1925:   }
 1926:   CloseHandle(pi.hThread);
 1927:   CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
 1928: 
 1929:   // Copy stdout to output buffer
 1930:   int i = 0;
 1931:   while (i < outsize) {
 1932:     DWORD num_read;
 1933:     if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
 1934:       break;
 1935:     i += num_read;
 1936:   }
 1937:   CloseHandle(pipe_out_r);
 1938:   // Wait for process
 1939:   WaitForSingleObject(pi.hProcess, INFINITE);
 1940:   CloseHandle(pi.hProcess);
 1941:   return i;
 1942: }
 1943: 
 1944: 
 1945: static const char * findstr(const char * str, const char * sub)
 1946: {
 1947:   const char * s = strstr(str, sub);
 1948:   return (s ? s+strlen(sub) : "");
 1949: }
 1950: 
 1951: 
 1952: static void copy_swapped(unsigned char * dest, const char * src, int destsize)
 1953: {
 1954:   int srclen = strcspn(src, "\r\n");
 1955:   int i;
 1956:   for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
 1957:     dest[i] = src[i+1]; dest[i+1] = src[i];
 1958:   }
 1959:   if (i < destsize-1 && i < srclen)
 1960:     dest[i+1] = src[i];
 1961: }
 1962: 
 1963: 
 1964: // TODO: This is OS independent
 1965: 
 1966: win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
 1967: : smart_device(intf, dev_name, "tw_cli", req_type),
 1968:   m_ident_valid(false), m_smart_valid(false)
 1969: {
 1970:   memset(&m_ident_buf, 0, sizeof(m_ident_buf));
 1971:   memset(&m_smart_buf, 0, sizeof(m_smart_buf));
 1972: }
 1973: 
 1974: 
 1975: bool win_tw_cli_device::is_open() const
 1976: {
 1977:   return (m_ident_valid || m_smart_valid);
 1978: }
 1979: 
 1980: 
 1981: bool win_tw_cli_device::open()
 1982: {
 1983:   m_ident_valid = m_smart_valid = false;
 1984:   const char * name = skipdev(get_dev_name());
 1985:   // Read tw_cli or 3DM browser output into buffer
 1986:   char buffer[4096];
 1987:   int size = -1, n1 = -1, n2 = -1;
 1988:   if (!strcmp(name, "tw_cli/clip")) { // read clipboard
 1989:     size = get_clipboard(buffer, sizeof(buffer));
 1990:   }
 1991:   else if (!strcmp(name, "tw_cli/stdin")) {  // read stdin
 1992:     size = fread(buffer, 1, sizeof(buffer), stdin);
 1993:   }
 1994:   else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
 1995:     // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
 1996:     char cmd[100];
 1997:     snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
 1998:     if (ata_debugmode > 1)
 1999:       pout("%s: Run: \"%s\"\n", name, cmd);
 2000:     size = run_cmd(cmd, buffer, sizeof(buffer));
 2001:   }
 2002:   else {
 2003:     return set_err(EINVAL);
 2004:   }
 2005: 
 2006:   if (ata_debugmode > 1)
 2007:     pout("%s: Read %d bytes\n", name, size);
 2008:   if (size <= 0)
 2009:     return set_err(ENOENT);
 2010:   if (size >= (int)sizeof(buffer))
 2011:     return set_err(EIO);
 2012: 
 2013:   buffer[size] = 0;
 2014:   if (ata_debugmode > 1)
 2015:     pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
 2016: 
 2017:   // Fake identify sector
 2018:   ASSERT_SIZEOF(ata_identify_device, 512);
 2019:   ata_identify_device * id = &m_ident_buf;
 2020:   memset(id, 0, sizeof(*id));
 2021:   copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
 2022:   copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
 2023:   copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
 2024:   unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
 2025:   sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
 2026:   if (nblocks) {
 2027:     id->words047_079[49-47] = 0x0200; // size valid
 2028:     id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
 2029:     id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
 2030:   }
 2031:   id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
 2032:   id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
 2033: 
 2034:   // Parse smart data hex dump
 2035:   const char * s = findstr(buffer, "Drive Smart Data:");
 2036:   if (!*s)
 2037:     s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
 2038:   if (!*s) {
 2039:     s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
 2040:     if (*s) {
 2041:       const char * s1 = findstr(s, "<td class"); // html version
 2042:       if (*s1)
 2043:         s = s1;
 2044:       s += strcspn(s, "\r\n");
 2045:     }
 2046:     else
 2047:       s = buffer; // try raw hex dump without header
 2048:   }
 2049:   unsigned char * sd = (unsigned char *)&m_smart_buf;
 2050:   int i = 0;
 2051:   for (;;) {
 2052:     unsigned x = ~0; int n = -1;
 2053:     if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
 2054:       break;
 2055:     sd[i] = (unsigned char)x;
 2056:     if (!(++i < 512 && n > 0))
 2057:       break;
 2058:     s += n;
 2059:     if (*s == '<') // "<br>"
 2060:       s += strcspn(s, "\r\n");
 2061:   }
 2062:   if (i < 512) {
 2063:     if (!id->model[1]) {
 2064:       // No useful data found
 2065:       char * err = strstr(buffer, "Error:");
 2066:       if (!err)
 2067:         err = strstr(buffer, "error :");
 2068:       if (err && (err = strchr(err, ':'))) {
 2069:         // Show tw_cli error message
 2070:         err++;
 2071:         err[strcspn(err, "\r\n")] = 0;
 2072:         return set_err(EIO, "%s", err);
 2073:       }
 2074:       return set_err(EIO);
 2075:     }
 2076:     sd = 0;
 2077:   }
 2078: 
 2079:   m_ident_valid = true;
 2080:   m_smart_valid = !!sd;
 2081:   return true;
 2082: }
 2083: 
 2084: 
 2085: bool win_tw_cli_device::close()
 2086: {
 2087:   m_ident_valid = m_smart_valid = false;
 2088:   return true;
 2089: }
 2090: 
 2091: 
 2092: int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
 2093: {
 2094:   switch (command) {
 2095:     case IDENTIFY:
 2096:       if (!m_ident_valid)
 2097:         break;
 2098:       memcpy(data, &m_ident_buf, 512);
 2099:       return 0;
 2100:     case READ_VALUES:
 2101:       if (!m_smart_valid)
 2102:         break;
 2103:       memcpy(data, &m_smart_buf, 512);
 2104:       return 0;
 2105:     case ENABLE:
 2106:     case STATUS:
 2107:     case STATUS_CHECK: // Fake "good" SMART status
 2108:       return 0;
 2109:     default:
 2110:       break;
 2111:   }
 2112:   // Arrive here for all unsupported commands
 2113:   set_err(ENOSYS);
 2114:   return -1;
 2115: }
 2116: 
 2117: 
 2118: /////////////////////////////////////////////////////////////////////////////
 2119: // IOCTL_STORAGE_QUERY_PROPERTY
 2120: 
 2121: union STORAGE_DEVICE_DESCRIPTOR_DATA {
 2122:   STORAGE_DEVICE_DESCRIPTOR desc;
 2123:   char raw[256];
 2124: };
 2125: 
 2126: // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
 2127: // (This works without admin rights)
 2128: 
 2129: static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 2130: {
 2131:   STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
 2132:   memset(data, 0, sizeof(*data));
 2133: 
 2134:   DWORD num_out;
 2135:   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
 2136:     &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
 2137:     if (ata_debugmode > 1 || scsi_debugmode > 1)
 2138:       pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
 2139:     errno = ENOSYS;
 2140:     return -1;
 2141:   }
 2142: 
 2143:   if (ata_debugmode > 1 || scsi_debugmode > 1) {
 2144:     pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
 2145:          "    Vendor:   \"%s\"\n"
 2146:          "    Product:  \"%s\"\n"
 2147:          "    Revision: \"%s\"\n"
 2148:          "    Removable: %s\n"
 2149:          "    BusType:   0x%02x\n",
 2150:          (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : "(null)"),
 2151:          (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : "(null)"),
 2152:          (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
 2153:          (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
 2154:     );
 2155:   }
 2156:   return 0;
 2157: }
 2158: 
 2159: 
 2160: /////////////////////////////////////////////////////////////////////////////
 2161: // IOCTL_STORAGE_PREDICT_FAILURE
 2162: 
 2163: // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
 2164: // or -1 on error, opionally return VendorSpecific data.
 2165: // (This works without admin rights)
 2166: 
 2167: static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
 2168: {
 2169:   STORAGE_PREDICT_FAILURE pred;
 2170:   memset(&pred, 0, sizeof(pred));
 2171: 
 2172:   DWORD num_out;
 2173:   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
 2174:     0, 0, &pred, sizeof(pred), &num_out, NULL)) {
 2175:     if (ata_debugmode > 1)
 2176:       pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
 2177:     errno = ENOSYS;
 2178:     return -1;
 2179:   }
 2180: 
 2181:   if (ata_debugmode > 1) {
 2182:     pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
 2183:          "    PredictFailure: 0x%08lx\n"
 2184:          "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
 2185:          pred.PredictFailure,
 2186:          pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
 2187:          pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
 2188:     );
 2189:   }
 2190:   if (data)
 2191:     memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
 2192:   return (!pred.PredictFailure ? 0 : 1);
 2193: }
 2194: 
 2195: 
 2196: /////////////////////////////////////////////////////////////////////////////
 2197: 
 2198: // Return true if Intel ICHxR RAID volume
 2199: static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 2200: {
 2201:   if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset))
 2202:     return false;
 2203:   const char * vendor = data->raw + data->desc.VendorIdOffset;
 2204:   if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5)))
 2205:     return false;
 2206:   if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5))
 2207:     return false;
 2208:   return true;
 2209: }
 2210: 
 2211: // get DEV_* for open handle
 2212: static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
 2213: {
 2214:   // Get BusType from device descriptor
 2215:   STORAGE_DEVICE_DESCRIPTOR_DATA data;
 2216:   if (storage_query_property_ioctl(hdevice, &data))
 2217:     return DEV_UNKNOWN;
 2218: 
 2219:   // Newer BusType* values are missing in older includes
 2220:   switch ((int)data.desc.BusType) {
 2221:     case BusTypeAta:
 2222:     case 0x0b: // BusTypeSata
 2223:       if (ata_version_ex)
 2224:         memset(ata_version_ex, 0, sizeof(*ata_version_ex));
 2225:       return DEV_ATA;
 2226: 
 2227:     case BusTypeScsi:
 2228:     case BusTypeRAID:
 2229:       // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
 2230:       if (is_intel_raid_volume(&data))
 2231:         return DEV_SCSI;
 2232:       // LSI/3ware RAID volume: supports SMART_*
 2233:       if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
 2234:         return DEV_ATA;
 2235:       return DEV_SCSI;
 2236: 
 2237:     case 0x09: // BusTypeiScsi
 2238:     case 0x0a: // BusTypeSas
 2239:       return DEV_SCSI;
 2240: 
 2241:     case BusTypeUsb:
 2242:       return DEV_USB;
 2243: 
 2244:     default:
 2245:       return DEV_UNKNOWN;
 2246:   }
 2247:   /*NOTREACHED*/
 2248: }
 2249: 
 2250: // get DEV_* for device path
 2251: static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 2252: {
 2253:   bool admin = true;
 2254:   HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
 2255:     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 2256:   if (h == INVALID_HANDLE_VALUE) {
 2257:     admin = false;
 2258:     h = CreateFileA(path, 0,
 2259:       FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 2260:     if (h == INVALID_HANDLE_VALUE)
 2261:       return DEV_UNKNOWN;
 2262:   }
 2263:   if (ata_debugmode > 1 || scsi_debugmode > 1)
 2264:     pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
 2265:   win_dev_type type = get_controller_type(h, admin, ata_version_ex);
 2266:   CloseHandle(h);
 2267:   return type;
 2268: }
 2269: 
 2270: // get DEV_* for physical drive number
 2271: static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
 2272: {
 2273:   char path[30];
 2274:   snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
 2275:   return get_controller_type(path, ata_version_ex);
 2276: }
 2277: 
 2278: static win_dev_type get_phy_drive_type(int drive)
 2279: {
 2280:   return get_phy_drive_type(drive, 0);
 2281: }
 2282: 
 2283: // get DEV_* for logical drive number
 2284: static win_dev_type get_log_drive_type(int drive)
 2285: {
 2286:   char path[30];
 2287:   snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
 2288:   return get_controller_type(path);
 2289: }
 2290: 
 2291: // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
 2292: static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
 2293: {
 2294:   STORAGE_DEVICE_DESCRIPTOR_DATA data;
 2295:   if (storage_query_property_ioctl(hdevice, &data))
 2296:     return -1;
 2297: 
 2298:   memset(id, 0, sizeof(*id));
 2299: 
 2300:   // Some drivers split ATA model string into VendorId and ProductId,
 2301:   // others return it as ProductId only.
 2302:   char model[sizeof(id->model) + 1] = "";
 2303: 
 2304:   unsigned i = 0;
 2305:   if (data.desc.VendorIdOffset) {
 2306:     for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++)
 2307:       model[i] = data.raw[data.desc.VendorIdOffset+i];
 2308:   }
 2309: 
 2310:   if (data.desc.ProductIdOffset) {
 2311:     while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
 2312:       i--;
 2313:     // Ignore VendorId "ATA"
 2314:     if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
 2315:       i = 0;
 2316:     for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
 2317:       model[i] = data.raw[data.desc.ProductIdOffset+j];
 2318:   }
 2319: 
 2320:   while (i > 0 && model[i-1] == ' ')
 2321:     i--;
 2322:   model[i] = 0;
 2323:   copy_swapped(id->model, model, sizeof(id->model));
 2324: 
 2325:   if (data.desc.ProductRevisionOffset)
 2326:     copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
 2327: 
 2328:   id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
 2329:   id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
 2330:   return 0;
 2331: }
 2332: 
 2333: 
 2334: /////////////////////////////////////////////////////////////////////////////
 2335: // USB ID detection using WMI
 2336: 
 2337: // Get USB ID for a physical drive number
 2338: static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
 2339: {
 2340:   bool debug = (scsi_debugmode > 1);
 2341: 
 2342:   wbem_services ws;
 2343:   if (!ws.connect()) {
 2344:     if (debug)
 2345:       pout("WMI connect failed\n");
 2346:     return false;
 2347:   }
 2348: 
 2349:   // Get device name
 2350:   wbem_object wo;
 2351:   if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
 2352:     return false;
 2353: 
 2354:   std::string name = wo.get_str("Model");
 2355:   if (debug)
 2356:     pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str());
 2357: 
 2358:   // Get USB_CONTROLLER -> DEVICE associations
 2359:   wbem_enumerator we;
 2360:   if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
 2361:     return false;
 2362: 
 2363:   unsigned short usb_venid = 0, prev_usb_venid = 0;
 2364:   unsigned short usb_proid = 0, prev_usb_proid = 0;
 2365:   std::string prev_usb_ant;
 2366:   std::string prev_ant, ant, dep;
 2367: 
 2368:   const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
 2369: 
 2370:   while (we.next(wo)) {
 2371:     prev_ant = ant;
 2372:     // Find next 'USB_CONTROLLER, DEVICE' pair
 2373:     ant = wo.get_str("Antecedent");
 2374:     dep = wo.get_str("Dependent");
 2375: 
 2376:     if (debug && ant != prev_ant)
 2377:       pout(" %s:\n", ant.c_str());
 2378: 
 2379:     // Extract DeviceID
 2380:     regmatch_t match[2];
 2381:     if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
 2382:       if (debug)
 2383:         pout("  | (\"%s\")\n", dep.c_str());
 2384:       continue;
 2385:     }
 2386: 
 2387:     std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
 2388: 
 2389:     if (str_starts_with(devid, "USB\\\\VID_")) {
 2390:       // USB bridge entry, save CONTROLLER, ID
 2391:       int nc = -1;
 2392:       if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
 2393:             &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
 2394:         prev_usb_venid = prev_usb_proid = 0;
 2395:       }
 2396:       prev_usb_ant = ant;
 2397:       if (debug)
 2398:         pout("  +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
 2399:       continue;
 2400:     }
 2401:     else if (str_starts_with(devid, "USBSTOR\\\\")) {
 2402:       // USBSTOR device found
 2403:       if (debug)
 2404:         pout("  +--> \"%s\"\n", devid.c_str());
 2405: 
 2406:       // Retrieve name
 2407:       wbem_object wo2;
 2408:       if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
 2409:         continue;
 2410:       std::string name2 = wo2.get_str("Name");
 2411: 
 2412:       // Continue if not name of physical disk drive
 2413:       if (name2 != name) {
 2414:         if (debug)
 2415:           pout("  +---> (\"%s\")\n", name2.c_str());
 2416:         continue;
 2417:       }
 2418: 
 2419:       // Fail if previos USB bridge is associated to other controller or ID is unknown
 2420:       if (!(ant == prev_usb_ant && prev_usb_venid)) {
 2421:         if (debug)
 2422:           pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
 2423:         return false;
 2424:       }
 2425: 
 2426:       // Handle multiple devices with same name
 2427:       if (usb_venid) {
 2428:         // Fail if multiple devices with same name have different USB bridge types
 2429:         if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
 2430:           if (debug)
 2431:             pout("  +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
 2432:           return false;
 2433:         }
 2434:       }
 2435: 
 2436:       // Found
 2437:       usb_venid = prev_usb_venid;
 2438:       usb_proid = prev_usb_proid;
 2439:       if (debug)
 2440:         pout("  +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
 2441: 
 2442:       // Continue to check for duplicate names ...
 2443:     }
 2444:     else {
 2445:       if (debug)
 2446:         pout("  |   \"%s\"\n", devid.c_str());
 2447:     }
 2448:   }
 2449: 
 2450:   if (!usb_venid)
 2451:     return false;
 2452: 
 2453:   vendor_id = usb_venid;
 2454:   product_id = usb_proid;
 2455: 
 2456:   return true;
 2457: }
 2458: 
 2459: 
 2460: /////////////////////////////////////////////////////////////////////////////
 2461: 
 2462: // Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
 2463: // returns: 1=active, 0=standby, -1=error
 2464: // (This would also work for SCSI drives)
 2465: 
 2466: static int get_device_power_state(HANDLE hdevice)
 2467: {
 2468:   static bool unsupported = false;
 2469:   if (unsupported) {
 2470:     errno = ENOSYS;
 2471:     return -1;
 2472:   }
 2473: 
 2474: #ifdef __CYGWIN__
 2475:   static DWORD kernel_dll_pid = 0;
 2476: #endif
 2477:   static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
 2478: 
 2479:   if (!GetDevicePowerState_p
 2480: #ifdef __CYGWIN__
 2481:       || kernel_dll_pid != GetCurrentProcessId() // detect fork()
 2482: #endif
 2483:      ) {
 2484:     if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *))
 2485:           GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) {
 2486:       if (ata_debugmode)
 2487:         pout("  GetDevicePowerState() not found, Error=%ld\n", GetLastError());
 2488:       unsupported = true;
 2489:       errno = ENOSYS;
 2490:       return -1;
 2491:     }
 2492: #ifdef __CYGWIN__
 2493:     kernel_dll_pid = GetCurrentProcessId();
 2494: #endif
 2495:   }
 2496: 
 2497:   BOOL state = TRUE;
 2498:   if (!GetDevicePowerState_p(hdevice, &state)) {
 2499:     long err = GetLastError();
 2500:     if (ata_debugmode)
 2501:       pout("  GetDevicePowerState() failed, Error=%ld\n", err);
 2502:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 2503:     // TODO: This may not work as expected on transient errors,
 2504:     // because smartd interprets -1 as SLEEP mode regardless of errno.
 2505:     return -1;
 2506:   }
 2507: 
 2508:   if (ata_debugmode > 1)
 2509:     pout("  GetDevicePowerState() succeeded, state=%d\n", state);
 2510:   return state;
 2511: }
 2512: 
 2513: 
 2514: /////////////////////////////////////////////////////////////////////////////
 2515: 
 2516: #if WIN9X_SUPPORT
 2517: // Print SMARTVSD error message, return errno
 2518: 
 2519: static int smartvsd_error()
 2520: {
 2521:   char path[MAX_PATH];
 2522:   unsigned len;
 2523:   if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
 2524:     return ENOENT;
 2525:   // SMARTVSD.VXD present?
 2526:   strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
 2527:   if (!access(path, 0)) {
 2528:     // Yes, standard IDE driver used?
 2529:     HANDLE h;
 2530:     if (   (h = CreateFileA("\\\\.\\ESDI_506",
 2531:                  GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
 2532:                  NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
 2533:         && GetLastError() == ERROR_FILE_NOT_FOUND                             ) {
 2534:       pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
 2535:       return ENOENT;
 2536:     }
 2537:     else {
 2538:       if (h != INVALID_HANDLE_VALUE) // should not happen
 2539:         CloseHandle(h);
 2540:       pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
 2541:       return ENOSYS;
 2542:     }
 2543:   }
 2544:   else {
 2545:     strcpy(path+len, "\\SMARTVSD.VXD");
 2546:     if (!access(path, 0)) {
 2547:       // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
 2548:       // (http://support.microsoft.com/kb/265854/en-us).
 2549:       path[len] = 0;
 2550:       pout("SMART driver is not properly installed,\n"
 2551:          " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
 2552:          " and reboot Windows.\n", path, path);
 2553:     }
 2554:     else {
 2555:       // Some Windows versions do not provide SMARTVSD.VXD
 2556:       // (http://support.microsoft.com/kb/199886/en-us).
 2557:       path[len] = 0;
 2558:       pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
 2559:     }
 2560:     return ENOSYS;
 2561:   }
 2562: }
 2563: 
 2564: #endif // WIN9X_SUPPORT
 2565: 
 2566: // Get default ATA device options
 2567: 
 2568: static const char * ata_get_def_options()
 2569: {
 2570:   DWORD ver = GetVersion();
 2571:   if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME
 2572:     return "s"; // SMART_* only
 2573:   else if ((ver & 0xff) == 4) // WinNT4
 2574:     return "sc"; // SMART_*, SCSI_PASS_THROUGH
 2575:   else // WinXP, 2003, Vista
 2576:     return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
 2577:                      // STORAGE_*, SCSI_MINIPORT_*
 2578: }
 2579: 
 2580: 
 2581: // Common routines for devices with HANDLEs
 2582: 
 2583: win_smart_device::~win_smart_device() throw()
 2584: {
 2585:   if (m_fh != INVALID_HANDLE_VALUE)
 2586:     ::CloseHandle(m_fh);
 2587: }
 2588: 
 2589: bool win_smart_device::is_open() const
 2590: {
 2591:   return (m_fh != INVALID_HANDLE_VALUE);
 2592: }
 2593: 
 2594: bool win_smart_device::close()
 2595: {
 2596:   if (m_fh == INVALID_HANDLE_VALUE)
 2597:     return true;
 2598:   BOOL rc = ::CloseHandle(m_fh);
 2599:   m_fh = INVALID_HANDLE_VALUE;
 2600:   return !!rc;
 2601: }
 2602: 
 2603: // ATA
 2604: 
 2605: win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
 2606: : smart_device(intf, dev_name, "ata", req_type),
 2607:   m_usr_options(false),
 2608:   m_admin(false),
 2609:   m_id_is_cached(false),
 2610:   m_is_3ware(false),
 2611:   m_drive(0),
 2612:   m_port(-1),
 2613:   m_smartver_state(0)
 2614: {
 2615: }
 2616: 
 2617: win_ata_device::~win_ata_device() throw()
 2618: {
 2619: }
 2620: 
 2621: 
 2622: // Open ATA device
 2623: 
 2624: bool win_ata_device::open()
 2625: {
 2626:   const char * name = skipdev(get_dev_name()); int len = strlen(name);
 2627:   // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
 2628:   char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
 2629:   if (   sscanf(name, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1
 2630:       && ((n1 == len && !options[0]) || n2 == len)                                       ) {
 2631:     return open(drive[0] - 'a', -1, options, -1);
 2632:   }
 2633:   // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
 2634:   drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
 2635:   unsigned port = ~0;
 2636:   if (   sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
 2637:       && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                     ) {
 2638:     return open(drive[0] - 'a', -1, options, port);
 2639:   }
 2640:   // pd<m>,N => Physical drive <m>, RAID port N
 2641:   int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
 2642:   if (   sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
 2643:       && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
 2644:     return open(phydrive, -1, "", (int)port);
 2645:   }
 2646:   // [a-zA-Z]: => Physical drive behind logical drive 0-25
 2647:   int logdrive = drive_letter(name);
 2648:   if (logdrive >= 0) {
 2649:     return open(-1, logdrive, "", -1);
 2650:   }
 2651: 
 2652:   return set_err(EINVAL);
 2653: }
 2654: 
 2655: 
 2656: bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
 2657: {
 2658:   // path depends on Windows Version
 2659:   char devpath[30];
 2660:   if (win9x && 0 <= phydrive && phydrive <= 7)
 2661:     // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
 2662:     strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
 2663:   else if (!win9x && 0 <= phydrive && phydrive <= 255)
 2664:     snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive);
 2665:   else if (!win9x && 0 <= logdrive && logdrive <= 'Z'-'A')
 2666:     snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
 2667:   else
 2668:     return set_err(ENOENT);
 2669: 
 2670:   // Open device
 2671:   HANDLE h = INVALID_HANDLE_VALUE;
 2672:   if (win9x || !(*options && !options[strspn(options, "fp")])) {
 2673:     // Open with admin rights
 2674:     m_admin = true;
 2675:     h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
 2676:       FILE_SHARE_READ|FILE_SHARE_WRITE,
 2677:       NULL, OPEN_EXISTING, 0, 0);
 2678:   }
 2679:   if (!win9x && h == INVALID_HANDLE_VALUE) {
 2680:     // Open without admin rights
 2681:     m_admin = false;
 2682:     h = CreateFileA(devpath, 0,
 2683:       FILE_SHARE_READ|FILE_SHARE_WRITE,
 2684:       NULL, OPEN_EXISTING, 0, 0);
 2685:   }
 2686:   if (h == INVALID_HANDLE_VALUE) {
 2687:     long err = GetLastError();
 2688: #if WIN9X_SUPPORT
 2689:     if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND)
 2690:       smartvsd_error();
 2691: #endif
 2692:     if (err == ERROR_FILE_NOT_FOUND)
 2693:       set_err(ENOENT, "%s: not found", devpath);
 2694:     else if (err == ERROR_ACCESS_DENIED)
 2695:       set_err(EACCES, "%s: access denied", devpath);
 2696:     else
 2697:       set_err(EIO, "%s: Error=%ld", devpath, err);
 2698:     return false;
 2699:   }
 2700:   set_fh(h);
 2701: 
 2702:   // Warn once if admin rights are missing
 2703:   if (!m_admin) {
 2704:     static bool noadmin_warning = false;
 2705:     if (!noadmin_warning) {
 2706:       pout("Warning: Limited functionality due to missing admin rights\n");
 2707:       noadmin_warning = true;
 2708:     }
 2709:   }
 2710: 
 2711:   if (ata_debugmode > 1)
 2712:     pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
 2713: 
 2714:   m_usr_options = false;
 2715:   if (*options) {
 2716:     // Save user options
 2717:     m_options = options; m_usr_options = true;
 2718:   }
 2719:   else if (port >= 0)
 2720:     // RAID: SMART_* and SCSI_MINIPORT
 2721:     m_options = "s3";
 2722:   else {
 2723:     // Set default options according to Windows version
 2724:     static const char * def_options = ata_get_def_options();
 2725:     m_options = def_options;
 2726:   }
 2727: 
 2728:   // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
 2729:   m_drive = 0; m_port = port;
 2730:   if (!win9x && port < 0)
 2731:     return true;
 2732: 
 2733:   // Win9X/ME: Get drive map
 2734:   // RAID: Get port map
 2735:   GETVERSIONINPARAMS_EX vers_ex;
 2736:   int devmap = smart_get_version(h, &vers_ex);
 2737: 
 2738:   // 3ware RAID if vendor id present
 2739:   m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 2740: 
 2741:   unsigned long portmap = 0;
 2742:   if (port >= 0 && devmap >= 0) {
 2743:     // 3ware RAID: check vendor id
 2744:     if (!m_is_3ware) {
 2745:       pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
 2746:            "This is no 3ware 9000 controller or driver has no SMART support.\n",
 2747:            vers_ex.wIdentifier);
 2748:       devmap = -1;
 2749:     }
 2750:     else
 2751:       portmap = vers_ex.dwDeviceMapEx;
 2752:   }
 2753:   if (devmap < 0) {
 2754:     pout("%s: ATA driver has no SMART support\n", devpath);
 2755:     if (!is_permissive()) {
 2756:       close();
 2757:       return set_err(ENOSYS);
 2758:     }
 2759:     devmap = 0x0f;
 2760:   }
 2761:   m_smartver_state = 1;
 2762: 
 2763:   if (port >= 0) {
 2764:     // 3ware RAID: update devicemap first
 2765: 
 2766:     if (!update_3ware_devicemap_ioctl(h)) {
 2767:       if (   smart_get_version(h, &vers_ex) >= 0
 2768:           && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
 2769:         portmap = vers_ex.dwDeviceMapEx;
 2770:     }
 2771:     // Check port existence
 2772:     if (!(portmap & (1L << port))) {
 2773:       if (!is_permissive()) {
 2774:         close();
 2775:         return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
 2776:       }
 2777:     }
 2778:     return true;
 2779:   }
 2780: 
 2781:   // Win9x/ME: Check device presence & type
 2782:   if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) {
 2783:     unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10;
 2784:     // Win9x drive existence check may not work as expected
 2785:     // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
 2786:     // (The related KB Article Q196120 is no longer available)
 2787:     if (!is_permissive()) {
 2788:       close();
 2789:       return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
 2790:         devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap);
 2791:     }
 2792:   }
 2793:   // Drive number must be passed to ioctl
 2794:   m_drive = (phydrive & 0x3);
 2795:   return true;
 2796: }
 2797: 
 2798: 
 2799: #if WIN9X_SUPPORT
 2800: 
 2801: // Scan for ATA drives on Win9x/ME
 2802: 
 2803: bool win9x_smart_interface::ata_scan(smart_device_list & devlist)
 2804: {
 2805:   // Open device
 2806:   const char devpath[] = "\\\\.\\SMARTVSD";
 2807:   HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
 2808:     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
 2809:   if (h == INVALID_HANDLE_VALUE) {
 2810:     if (ata_debugmode > 1)
 2811:       pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
 2812:     return true; // SMARTVSD.VXD missing or no ATA devices
 2813:   }
 2814: 
 2815:   // Get drive map
 2816:   int devmap = smart_get_version(h);
 2817:   CloseHandle(h);
 2818:   if (devmap < 0)
 2819:     return true; // Should not happen
 2820: 
 2821:   // Check ATA device presence, remove ATAPI devices
 2822:   devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf);
 2823:   char name[20];
 2824:   for (int i = 0; i < 4; i++) {
 2825:     if (!(devmap & (1 << i)))
 2826:       continue;
 2827:     sprintf(name, "/dev/hd%c", 'a'+i);
 2828:     devlist.push_back( new win_ata_device(this, name, "ata") );
 2829:   }
 2830:   return true;
 2831: }
 2832: 
 2833: #endif // WIN9X_SUPPORT
 2834: 
 2835: 
 2836: /////////////////////////////////////////////////////////////////////////////
 2837: 
 2838: // Interface to ATA devices
 2839: bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 2840: {
 2841:   // No multi-sector support for now, see above
 2842:   // warning about IOCTL_ATA_PASS_THROUGH
 2843:   if (!ata_cmd_is_ok(in,
 2844:     true, // data_out_support
 2845:     false, // !multi_sector_support
 2846:     true) // ata_48bit_support
 2847:   )
 2848:     return false;
 2849: 
 2850:   // 3ware RAID: SMART DISABLE without port number disables SMART functions
 2851:   if (   m_is_3ware && m_port < 0
 2852:       && in.in_regs.command == ATA_SMART_CMD
 2853:       && in.in_regs.features == ATA_SMART_DISABLE)
 2854:     return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
 2855: 
 2856:   // Determine ioctl functions valid for this ATA cmd
 2857:   const char * valid_options = 0;
 2858: 
 2859:   switch (in.in_regs.command) {
 2860:     case ATA_IDENTIFY_DEVICE:
 2861:     case ATA_IDENTIFY_PACKET_DEVICE:
 2862:       // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
 2863:       // and SCSI_MINIPORT_* if requested by user
 2864:       valid_options = (m_usr_options ? "saicmf" : "saicf");
 2865:       break;
 2866: 
 2867:     case ATA_CHECK_POWER_MODE:
 2868:       // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
 2869:       valid_options = "pai3";
 2870:       break;
 2871: 
 2872:     case ATA_SMART_CMD:
 2873:       switch (in.in_regs.features) {
 2874:         case ATA_SMART_READ_VALUES:
 2875:         case ATA_SMART_READ_THRESHOLDS:
 2876:         case ATA_SMART_AUTOSAVE:
 2877:         case ATA_SMART_ENABLE:
 2878:         case ATA_SMART_DISABLE:
 2879:         case ATA_SMART_AUTO_OFFLINE:
 2880:           // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
 2881:           // and SCSI_MINIPORT_* if requested by user
 2882:           valid_options = (m_usr_options ? "saicmf" : "saicf");
 2883:           break;
 2884: 
 2885:         case ATA_SMART_IMMEDIATE_OFFLINE:
 2886:           // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
 2887:           valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ?
 2888:                            "saicm3" : "aicm3");
 2889:           break;
 2890: 
 2891:         case ATA_SMART_READ_LOG_SECTOR:
 2892:           // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
 2893:           // Try SCSI_MINIPORT also to skip buggy class driver
 2894:           // SMART functions do not support multi sector I/O.
 2895:           if (in.size == 512)
 2896:             valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3");
 2897:           else
 2898:             valid_options = "a";
 2899:           break;
 2900: 
 2901:         case ATA_SMART_WRITE_LOG_SECTOR:
 2902:           // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
 2903:           // but SCSI_MINIPORT_* only if requested by user and single sector.
 2904:           valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
 2905:           break;
 2906: 
 2907:         case ATA_SMART_STATUS:
 2908:           // May require lba_mid,lba_high register return
 2909:           if (in.out_needed.is_set())
 2910:             valid_options = (m_usr_options ? "saimf" : "saif");
 2911:           else
 2912:             valid_options = (m_usr_options ? "saicmf" : "saicf");
 2913:           break;
 2914: 
 2915:         default:
 2916:           // Unknown SMART command, handle below
 2917:           break;
 2918:       }
 2919:       break;
 2920: 
 2921:     default:
 2922:       // Other ATA command, handle below
 2923:       break;
 2924:   }
 2925: 
 2926:   if (!valid_options) {
 2927:     // No special ATA command found above, select a generic pass through ioctl.
 2928:     if (!(   in.direction == ata_cmd_in::no_data
 2929:           || (in.direction == ata_cmd_in::data_in && in.size == 512))
 2930:          ||  in.in_regs.is_48bit_cmd()                               )
 2931:       // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
 2932:       valid_options = "a";
 2933:     else if (in.out_needed.is_set())
 2934:       // Need output registers: ATA/IDE_PASS_THROUGH
 2935:       valid_options = "ai";
 2936:     else
 2937:       valid_options = "aic";
 2938:   }
 2939: 
 2940:   if (!m_admin) {
 2941:     // Restrict to IOCTL_STORAGE_*
 2942:     if (strchr(valid_options, 'f'))
 2943:       valid_options = "f";
 2944:     else if (strchr(valid_options, 'p'))
 2945:       valid_options = "p";
 2946:     else
 2947:       return set_err(ENOSYS, "Function requires admin rights");
 2948:   }
 2949: 
 2950:   // Set IDEREGS
 2951:   IDEREGS regs, prev_regs;
 2952:   {
 2953:     const ata_in_regs & lo = in.in_regs;
 2954:     regs.bFeaturesReg     = lo.features;
 2955:     regs.bSectorCountReg  = lo.sector_count;
 2956:     regs.bSectorNumberReg = lo.lba_low;
 2957:     regs.bCylLowReg       = lo.lba_mid;
 2958:     regs.bCylHighReg      = lo.lba_high;
 2959:     regs.bDriveHeadReg    = lo.device;
 2960:     regs.bCommandReg      = lo.command;
 2961:     regs.bReserved        = 0;
 2962:   }
 2963:   if (in.in_regs.is_48bit_cmd()) {
 2964:     const ata_in_regs & hi = in.in_regs.prev;
 2965:     prev_regs.bFeaturesReg     = hi.features;
 2966:     prev_regs.bSectorCountReg  = hi.sector_count;
 2967:     prev_regs.bSectorNumberReg = hi.lba_low;
 2968:     prev_regs.bCylLowReg       = hi.lba_mid;
 2969:     prev_regs.bCylHighReg      = hi.lba_high;
 2970:     prev_regs.bDriveHeadReg    = hi.device;
 2971:     prev_regs.bCommandReg      = hi.command;
 2972:     prev_regs.bReserved        = 0;
 2973:   }
 2974: 
 2975:   // Set data direction
 2976:   int datasize = 0;
 2977:   char * data = 0;
 2978:   switch (in.direction) {
 2979:     case ata_cmd_in::no_data:
 2980:       break;
 2981:     case ata_cmd_in::data_in:
 2982:       datasize = (int)in.size;
 2983:       data = (char *)in.buffer;
 2984:       break;
 2985:     case ata_cmd_in::data_out:
 2986:       datasize = -(int)in.size;
 2987:       data = (char *)in.buffer;
 2988:       break;
 2989:     default:
 2990:       return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
 2991:           (int)in.direction);
 2992:   }
 2993: 
 2994: 
 2995:   // Try all valid ioctls in the order specified in m_options
 2996:   bool powered_up = false;
 2997:   bool out_regs_set = false;
 2998:   bool id_is_cached = false;
 2999:   const char * options = m_options.c_str();
 3000: 
 3001:   for (int i = 0; ; i++) {
 3002:     char opt = options[i];
 3003: 
 3004:     if (!opt) {
 3005:       if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
 3006:         // Power up reported by GetDevicePowerState() and no ioctl available
 3007:         // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
 3008:         regs.bSectorCountReg = 0xff;
 3009:         out_regs_set = true;
 3010:         break;
 3011:       }
 3012:       // No IOCTL found
 3013:       return set_err(ENOSYS);
 3014:     }
 3015:     if (!strchr(valid_options, opt))
 3016:       // Invalid for this command
 3017:       continue;
 3018: 
 3019:     errno = 0;
 3020:     assert(   datasize == 0 || datasize == 512
 3021:            || (datasize == -512 && strchr("am", opt))
 3022:            || (datasize > 512 && opt == 'a'));
 3023:     int rc;
 3024:     switch (opt) {
 3025:       default: assert(0);
 3026:       case 's':
 3027:         // call SMART_GET_VERSION once for each drive
 3028:         if (m_smartver_state > 1) {
 3029:           rc = -1; errno = ENOSYS;
 3030:           break;
 3031:         }
 3032:         if (!m_smartver_state) {
 3033:           assert(m_port == -1);
 3034:           GETVERSIONINPARAMS_EX vers_ex;
 3035:           if (smart_get_version(get_fh(), &vers_ex) < 0) {
 3036:             if (!failuretest_permissive) {
 3037:               m_smartver_state = 2;
 3038:               rc = -1; errno = ENOSYS;
 3039:               break;
 3040:             }
 3041:             failuretest_permissive--;
 3042:           }
 3043:           else  {
 3044:             // 3ware RAID if vendor id present
 3045:             m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 3046:           }
 3047: 
 3048:           m_smartver_state = 1;
 3049:         }
 3050:         rc = smart_ioctl(get_fh(), m_drive, &regs, data, datasize, m_port);
 3051:         out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
 3052:         id_is_cached = (m_port < 0 && !win9x); // Not cached by 3ware or Win9x/ME driver
 3053:         break;
 3054:       case 'm':
 3055:         rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
 3056:         id_is_cached = (m_port < 0 && !win9x);
 3057:         break;
 3058:       case 'a':
 3059:         rc = ata_pass_through_ioctl(get_fh(), &regs,
 3060:           (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
 3061:           data, datasize);
 3062:         out_regs_set = true;
 3063:         break;
 3064:       case 'i':
 3065:         rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
 3066:         out_regs_set = true;
 3067:         break;
 3068:       case 'c':
 3069:         rc = ata_via_scsi_pass_through_ioctl(get_fh(), &regs, data, datasize);
 3070:         break;
 3071:       case 'f':
 3072:         if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
 3073:             rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
 3074:             id_is_cached = true;
 3075:         }
 3076:         else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
 3077:           case ATA_SMART_READ_VALUES:
 3078:             rc = storage_predict_failure_ioctl(get_fh(), data);
 3079:             if (rc > 0)
 3080:               rc = 0;
 3081:             break;
 3082:           case ATA_SMART_ENABLE:
 3083:             rc = 0;
 3084:             break;
 3085:           case ATA_SMART_STATUS:
 3086:             rc = storage_predict_failure_ioctl(get_fh());
 3087:             if (rc == 0) {
 3088:               // Good SMART status
 3089:               out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
 3090:             }
 3091:             else if (rc > 0) {
 3092:               // Bad SMART status
 3093:               out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
 3094:               rc = 0;
 3095:             }
 3096:             break;
 3097:           default:
 3098:             errno = ENOSYS; rc = -1;
 3099:         }
 3100:         else {
 3101:             errno = ENOSYS; rc = -1;
 3102:         }
 3103:         break;
 3104:       case '3':
 3105:         rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
 3106:         out_regs_set = true;
 3107:         break;
 3108:       case 'p':
 3109:         assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
 3110:         rc = get_device_power_state(get_fh());
 3111:         if (rc == 0) {
 3112:           // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
 3113:           // spin up the drive => simulate ATA result STANDBY.
 3114:           regs.bSectorCountReg = 0x00;
 3115:           out_regs_set = true;
 3116:         }
 3117:         else if (rc > 0) {
 3118:           // Power up reported by GetDevicePowerState(), but this reflects the actual mode
 3119:           // only if it is selected by the device driver => try a passthrough ioctl to get the
 3120:           // actual mode, if none available simulate ACTIVE/IDLE.
 3121:           powered_up = true;
 3122:           rc = -1; errno = ENOSYS;
 3123:         }
 3124:         break;
 3125:     }
 3126: 
 3127:     if (!rc)
 3128:       // Working ioctl found
 3129:       break;
 3130: 
 3131:     if (errno != ENOSYS)
 3132:       // Abort on I/O error
 3133:       return set_err(errno);
 3134: 
 3135:     out_regs_set = false;
 3136:     // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
 3137:   }
 3138: 
 3139:   // Return IDEREGS if set
 3140:   if (out_regs_set) {
 3141:     ata_out_regs & lo = out.out_regs;
 3142:     lo.error        = regs.bFeaturesReg;
 3143:     lo.sector_count = regs.bSectorCountReg;
 3144:     lo.lba_low      = regs.bSectorNumberReg;
 3145:     lo.lba_mid      = regs.bCylLowReg;
 3146:     lo.lba_high     = regs.bCylHighReg;
 3147:     lo.device       = regs.bDriveHeadReg;
 3148:     lo.status       = regs.bCommandReg;
 3149:     if (in.in_regs.is_48bit_cmd()) {
 3150:       ata_out_regs & hi = out.out_regs.prev;
 3151:       hi.sector_count = prev_regs.bSectorCountReg;
 3152:       hi.lba_low      = prev_regs.bSectorNumberReg;
 3153:       hi.lba_mid      = prev_regs.bCylLowReg;
 3154:       hi.lba_high     = prev_regs.bCylHighReg;
 3155:     }
 3156:   }
 3157: 
 3158:   if (   in.in_regs.command == ATA_IDENTIFY_DEVICE
 3159:       || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
 3160:     // Update ata_identify_is_cached() result according to ioctl used.
 3161:     m_id_is_cached = id_is_cached;
 3162: 
 3163:   return true;
 3164: }
 3165: 
 3166: // Return true if OS caches the ATA identify sector
 3167: bool win_ata_device::ata_identify_is_cached() const
 3168: {
 3169:   return m_id_is_cached;
 3170: }
 3171: 
 3172: 
 3173: //////////////////////////////////////////////////////////////////////
 3174: // csmi_ata_device
 3175: 
 3176: bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
 3177: {
 3178:   // Get driver info to check CSMI support
 3179:   CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf;
 3180:   memset(&driver_info_buf, 0, sizeof(driver_info_buf));
 3181:   if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf)))
 3182:     return false;
 3183: 
 3184:   if (scsi_debugmode > 1) {
 3185:     const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
 3186:     pout("CSMI_SAS_DRIVER_INFO:\n");
 3187:     pout("  Name:        \"%.81s\"\n", driver_info.szName);
 3188:     pout("  Description: \"%.81s\"\n", driver_info.szDescription);
 3189:     pout("  Revision:    %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
 3190:   }
 3191: 
 3192:   // Get Phy info
 3193:   CSMI_SAS_PHY_INFO_BUFFER phy_info_buf;
 3194:   memset(&phy_info_buf, 0, sizeof(phy_info_buf));
 3195:   if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf)))
 3196:     return false;
 3197: 
 3198:   phy_info = phy_info_buf.Information;
 3199:   if (phy_info.bNumberOfPhys > sizeof(phy_info.Phy)/sizeof(phy_info.Phy[0]))
 3200:     return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
 3201: 
 3202:   if (scsi_debugmode > 1) {
 3203:     pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
 3204:     for (int i = 0; i < phy_info.bNumberOfPhys; i++) {
 3205:       const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
 3206:       const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
 3207:       pout("Phy[%d] Port:   0x%02x\n", i, pe.bPortIdentifier);
 3208:       pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
 3209:       pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
 3210:       pout("  TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
 3211:       pout("  PhyIdent:    0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier);
 3212:       const unsigned char * b = id.bSASAddress;
 3213:       pout("  SASAddress:  %02x %02x %02x %02x %02x %02x %02x %02x, ",
 3214:         b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
 3215:       b = at.bSASAddress;
 3216:       pout(               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
 3217:         b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
 3218:     }
 3219:   }
 3220: 
 3221:   return true;
 3222: }
 3223: 
 3224: bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no)
 3225: {
 3226:   // Check Phy presence
 3227:   if (phy_no >= phy_info.bNumberOfPhys)
 3228:     return set_err(ENOENT, "Port %u does not exist (#ports: %d)", phy_no,
 3229:       phy_info.bNumberOfPhys);
 3230: 
 3231:   const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[phy_no];
 3232:   if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
 3233:     return set_err(ENOENT, "No device on port %u", phy_no);
 3234: 
 3235:   switch (phy_ent.Attached.bTargetPortProtocol) {
 3236:     case CSMI_SAS_PROTOCOL_SATA:
 3237:     case CSMI_SAS_PROTOCOL_STP:
 3238:       break;
 3239:     default:
 3240:       return set_err(ENOENT, "No SATA device on port %u (protocol: %u)",
 3241:         phy_no, phy_ent.Attached.bTargetPortProtocol);
 3242:   }
 3243: 
 3244:   return true;
 3245: }
 3246: 
 3247: bool csmi_device::select_phy(unsigned phy_no)
 3248: {
 3249:   CSMI_SAS_PHY_INFO phy_info;
 3250:   if (!get_phy_info(phy_info))
 3251:     return false;
 3252: 
 3253: 
 3254:   if (!check_phy(phy_info, phy_no))
 3255:     return false;
 3256: 
 3257:   m_phy_ent = phy_info.Phy[phy_no];
 3258:   return true;
 3259: }
 3260: 
 3261: 
 3262: bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 3263: {
 3264:   if (!ata_cmd_is_ok(in,
 3265:     true, // data_out_support
 3266:     true, // multi_sector_support
 3267:     true) // ata_48bit_support
 3268:   )
 3269:     return false;
 3270: 
 3271:   // Create buffer with appropriate size
 3272:   raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
 3273:   CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
 3274: 
 3275:   // Set addresses from Phy info
 3276:   CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
 3277:   const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
 3278:   pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
 3279:   pthru.bPortIdentifier = phy_ent.bPortIdentifier;
 3280:   memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
 3281:     sizeof(pthru.bDestinationSASAddress));
 3282:   pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
 3283: 
 3284:   // Set transfer mode
 3285:   switch (in.direction) {
 3286:     case ata_cmd_in::no_data:
 3287:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
 3288:       break;
 3289:     case ata_cmd_in::data_in:
 3290:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
 3291:       pthru.uDataLength = in.size;
 3292:       break;
 3293:     case ata_cmd_in::data_out:
 3294:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
 3295:       pthru.uDataLength = in.size;
 3296:       memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
 3297:       break;
 3298:     default:
 3299:       return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
 3300:         (int)in.direction);
 3301:   }
 3302: 
 3303:   // Set host-to-device FIS
 3304:   {
 3305:     unsigned char * fis = pthru.bCommandFIS;
 3306:     const ata_in_regs & lo = in.in_regs;
 3307:     const ata_in_regs & hi = in.in_regs.prev;
 3308:     fis[ 0] = 0x27; // Type: host-to-device FIS
 3309:     fis[ 1] = 0x80; // Bit7: Update command register
 3310:     fis[ 2] = lo.command;
 3311:     fis[ 3] = lo.features;
 3312:     fis[ 4] = lo.lba_low;
 3313:     fis[ 5] = lo.lba_mid;
 3314:     fis[ 6] = lo.lba_high;
 3315:     fis[ 7] = lo.device;
 3316:     fis[ 8] = hi.lba_low;
 3317:     fis[ 9] = hi.lba_mid;
 3318:     fis[10] = hi.lba_high;
 3319:     fis[11] = hi.features;
 3320:     fis[12] = lo.sector_count;
 3321:     fis[13] = hi.sector_count;
 3322:   }
 3323: 
 3324:   // Call ioctl
 3325:   if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
 3326:     return false;
 3327:   }
 3328: 
 3329:   // Get device-to-host FIS
 3330:   {
 3331:     const unsigned char * fis = pthru_buf->Status.bStatusFIS;
 3332:     ata_out_regs & lo = out.out_regs;
 3333:     lo.status       = fis[ 2];
 3334:     lo.error        = fis[ 3];
 3335:     lo.lba_low      = fis[ 4];
 3336:     lo.lba_mid      = fis[ 5];
 3337:     lo.lba_high     = fis[ 6];
 3338:     lo.device       = fis[ 7];
 3339:     lo.sector_count = fis[12];
 3340:     if (in.in_regs.is_48bit_cmd()) {
 3341:       ata_out_regs & hi = out.out_regs.prev;
 3342:       hi.lba_low      = fis[ 8];
 3343:       hi.lba_mid      = fis[ 9];
 3344:       hi.lba_high     = fis[10];
 3345:       hi.sector_count = fis[13];
 3346:     }
 3347:   }
 3348: 
 3349:   // Get data
 3350:   if (in.direction == ata_cmd_in::data_in)
 3351:     // TODO: Check ptru_buf->Status.uDataBytes
 3352:     memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
 3353: 
 3354:   return true;
 3355: }
 3356: 
 3357: 
 3358: //////////////////////////////////////////////////////////////////////
 3359: // win_csmi_device
 3360: 
 3361: win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name,
 3362:   const char * req_type)
 3363: : smart_device(intf, dev_name, "ata", req_type),
 3364:   m_fh(INVALID_HANDLE_VALUE), m_phy_no(0)
 3365: {
 3366: }
 3367: 
 3368: win_csmi_device::~win_csmi_device() throw()
 3369: {
 3370:   if (m_fh != INVALID_HANDLE_VALUE)
 3371:     CloseHandle(m_fh);
 3372: }
 3373: 
 3374: bool win_csmi_device::is_open() const
 3375: {
 3376:   return (m_fh != INVALID_HANDLE_VALUE);
 3377: }
 3378: 
 3379: bool win_csmi_device::close()
 3380: {
 3381:   if (m_fh == INVALID_HANDLE_VALUE)
 3382:     return true;
 3383:   BOOL rc = CloseHandle(m_fh);
 3384:   m_fh = INVALID_HANDLE_VALUE;
 3385:   return !!rc;
 3386: }
 3387: 
 3388: 
 3389: bool win_csmi_device::open_scsi()
 3390: {
 3391:   // Parse name
 3392:   unsigned contr_no = ~0, phy_no = ~0; int nc = -1;
 3393:   const char * name = skipdev(get_dev_name());
 3394:   if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &phy_no, &nc) >= 0
 3395:         && nc == (int)strlen(name) && contr_no <= 9 && phy_no < 32)  )
 3396:     return set_err(EINVAL);
 3397: 
 3398:   // Open controller handle
 3399:   char devpath[30];
 3400:   snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
 3401: 
 3402:   HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
 3403:     FILE_SHARE_READ|FILE_SHARE_WRITE,
 3404:     (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
 3405: 
 3406:   if (h == INVALID_HANDLE_VALUE) {
 3407:     long err = GetLastError();
 3408:     if (err == ERROR_FILE_NOT_FOUND)
 3409:       set_err(ENOENT, "%s: not found", devpath);
 3410:     else if (err == ERROR_ACCESS_DENIED)
 3411:       set_err(EACCES, "%s: access denied", devpath);
 3412:     else
 3413:       set_err(EIO, "%s: Error=%ld", devpath, err);
 3414:     return false;
 3415:   }
 3416: 
 3417:   if (scsi_debugmode > 1)
 3418:     pout(" %s: successfully opened\n", devpath);
 3419: 
 3420:   m_fh = h;
 3421:   m_phy_no = phy_no;
 3422:   return true;
 3423: }
 3424: 
 3425: 
 3426: bool win_csmi_device::open()
 3427: {
 3428:   if (!open_scsi())
 3429:     return false;
 3430: 
 3431:   // Get Phy info for this drive
 3432:   if (!select_phy(m_phy_no)) {
 3433:     close();
 3434:     return false;
 3435:   }
 3436: 
 3437:   return true;
 3438: }
 3439: 
 3440: 
 3441: bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
 3442:   unsigned csmi_bufsiz)
 3443: {
 3444:   // Determine signature
 3445:   const char * sig;
 3446:   switch (code) {
 3447:     case CC_CSMI_SAS_GET_DRIVER_INFO:
 3448:       sig = CSMI_ALL_SIGNATURE; break;
 3449:     case CC_CSMI_SAS_GET_PHY_INFO:
 3450:     case CC_CSMI_SAS_STP_PASSTHRU:
 3451:       sig = CSMI_SAS_SIGNATURE; break;
 3452:     default:
 3453:       return set_err(ENOSYS, "Unknown CSMI code=%u", code);
 3454:   }
 3455: 
 3456:   // Set header
 3457:   csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
 3458:   strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
 3459:   csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
 3460:   csmi_buffer->ControlCode = code;
 3461:   csmi_buffer->ReturnCode = 0;
 3462:   csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
 3463: 
 3464:   // Call function
 3465:   DWORD num_out = 0;
 3466:   if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
 3467:     csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
 3468:     long err = GetLastError();
 3469:     if (scsi_debugmode)
 3470:       pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
 3471:     if (   err == ERROR_INVALID_FUNCTION
 3472:         || err == ERROR_NOT_SUPPORTED
 3473:         || err == ERROR_DEV_NOT_EXIST)
 3474:       return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
 3475:     else
 3476:       return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
 3477:   }
 3478: 
 3479:   // Check result
 3480:   if (csmi_buffer->ReturnCode) {
 3481:     if (scsi_debugmode) {
 3482:       pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",
 3483:         code, csmi_buffer->ReturnCode);
 3484:     }
 3485:     return set_err(EIO, "CSMI(%u) failed with ReturnCode=%lu", code, csmi_buffer->ReturnCode);
 3486:   }
 3487: 
 3488:   if (scsi_debugmode > 1)
 3489:     pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code, num_out);
 3490: 
 3491:   return true;
 3492: }
 3493: 
 3494: 
 3495: /////////////////////////////////////////////////////////////////////////////
 3496: // ASPI Interface (for SCSI devices on 9x/ME)
 3497: /////////////////////////////////////////////////////////////////////////////
 3498: 
 3499: #if WIN9X_SUPPORT
 3500: 
 3501: #pragma pack(1)
 3502: 
 3503: #define ASPI_SENSE_SIZE 18
 3504: 
 3505: // ASPI SCSI Request block header
 3506: 
 3507: typedef struct {
 3508:   unsigned char cmd;             // 00: Command code
 3509:   unsigned char status;          // 01: ASPI status
 3510:   unsigned char adapter;         // 02: Host adapter number
 3511:   unsigned char flags;           // 03: Request flags
 3512:   unsigned char reserved[4];     // 04: 0
 3513: } ASPI_SRB_HEAD;
 3514: 
 3515: // SRB for host adapter inquiry
 3516: 
 3517: typedef struct {
 3518:   ASPI_SRB_HEAD h;               // 00: Header
 3519:   unsigned char adapters;        // 08: Number of adapters
 3520:   unsigned char target_id;       // 09: Target ID ?
 3521:   char manager_id[16];           // 10: SCSI manager ID
 3522:   char adapter_id[16];           // 26: Host adapter ID
 3523:   unsigned char parameters[16];  // 42: Host adapter unique parmameters
 3524: } ASPI_SRB_INQUIRY;
 3525: 
 3526: // SRB for get device type
 3527: 
 3528: typedef struct {
 3529:   ASPI_SRB_HEAD h;               // 00: Header
 3530:   unsigned char target_id;       // 08: Target ID
 3531:   unsigned char lun;             // 09: LUN
 3532:   unsigned char devtype;         // 10: Device type
 3533:   unsigned char reserved;        // 11: Reserved
 3534: } ASPI_SRB_DEVTYPE;
 3535: 
 3536: // SRB for SCSI I/O
 3537: 
 3538: typedef struct {
 3539:   ASPI_SRB_HEAD h;               // 00: Header
 3540:   unsigned char target_id;       // 08: Target ID
 3541:   unsigned char lun;             // 09: LUN
 3542:   unsigned char reserved[2];     // 10: Reserved
 3543:   unsigned long data_size;       // 12: Data alloc. lenght
 3544:   void * data_addr;              // 16: Data buffer pointer
 3545:   unsigned char sense_size;      // 20: Sense alloc. length
 3546:   unsigned char cdb_size;        // 21: CDB length
 3547:   unsigned char host_status;     // 22: Host status
 3548:   unsigned char target_status;   // 23: Target status
 3549:   void * event_handle;           // 24: Event handle
 3550:   unsigned char workspace[20];   // 28: ASPI workspace
 3551:   unsigned char cdb[16+ASPI_SENSE_SIZE];
 3552: } ASPI_SRB_IO;
 3553: 
 3554: // Macro to retrieve start of sense information
 3555: #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
 3556: 
 3557: // SRB union
 3558: 
 3559: typedef union {
 3560:   ASPI_SRB_HEAD h;       // Common header
 3561:   ASPI_SRB_INQUIRY q;    // Inquiry
 3562:   ASPI_SRB_DEVTYPE t;    // Device type
 3563:   ASPI_SRB_IO i;         // I/O
 3564: } ASPI_SRB;
 3565: 
 3566: #pragma pack()
 3567: 
 3568: // ASPI commands
 3569: #define ASPI_CMD_ADAPTER_INQUIRE        0x00
 3570: #define ASPI_CMD_GET_DEVICE_TYPE        0x01
 3571: #define ASPI_CMD_EXECUTE_IO             0x02
 3572: #define ASPI_CMD_ABORT_IO               0x03
 3573: 
 3574: // Request flags
 3575: #define ASPI_REQFLAG_DIR_TO_HOST        0x08
 3576: #define ASPI_REQFLAG_DIR_TO_TARGET      0x10
 3577: #define ASPI_REQFLAG_DIR_NO_XFER        0x18
 3578: #define ASPI_REQFLAG_EVENT_NOTIFY       0x40
 3579: 
 3580: // ASPI status
 3581: #define ASPI_STATUS_IN_PROGRESS         0x00
 3582: #define ASPI_STATUS_NO_ERROR            0x01
 3583: #define ASPI_STATUS_ABORTED             0x02
 3584: #define ASPI_STATUS_ABORT_ERR           0x03
 3585: #define ASPI_STATUS_ERROR               0x04
 3586: #define ASPI_STATUS_INVALID_COMMAND     0x80
 3587: #define ASPI_STATUS_INVALID_ADAPTER     0x81
 3588: #define ASPI_STATUS_INVALID_TARGET      0x82
 3589: #define ASPI_STATUS_NO_ADAPTERS         0xE8
 3590: 
 3591: // Adapter (host) status
 3592: #define ASPI_HSTATUS_NO_ERROR           0x00
 3593: #define ASPI_HSTATUS_SELECTION_TIMEOUT  0x11
 3594: #define ASPI_HSTATUS_DATA_OVERRUN       0x12
 3595: #define ASPI_HSTATUS_BUS_FREE           0x13
 3596: #define ASPI_HSTATUS_BUS_PHASE_ERROR    0x14
 3597: #define ASPI_HSTATUS_BAD_SGLIST         0x1A
 3598: 
 3599: // Target status
 3600: #define ASPI_TSTATUS_NO_ERROR           0x00
 3601: #define ASPI_TSTATUS_CHECK_CONDITION    0x02
 3602: #define ASPI_TSTATUS_BUSY               0x08
 3603: #define ASPI_TSTATUS_RESERV_CONFLICT    0x18
 3604: 
 3605: 
 3606: static HINSTANCE h_aspi_dll; // DLL handle
 3607: static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
 3608: static unsigned num_aspi_adapters;
 3609: 
 3610: #ifdef __CYGWIN__
 3611: // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
 3612: static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
 3613: #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
 3614: #else
 3615: #define aspi_entry_valid() (!!aspi_entry)
 3616: #endif
 3617: 
 3618: 
 3619: static int aspi_call(ASPI_SRB * srb)
 3620: {
 3621:   int i;
 3622:   aspi_entry(srb);
 3623:   i = 0;
 3624:   while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
 3625:     if (++i > 100/*10sek*/) {
 3626:       pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
 3627:       aspi_entry = 0;
 3628:       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
 3629:       errno = EIO;
 3630:       return -1;
 3631:     }
 3632:     if (scsi_debugmode > 1)
 3633:       pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
 3634:     Sleep(100);
 3635:   }
 3636:   return 0;
 3637: }
 3638: 
 3639: 
 3640: // Get ASPI entrypoint from wnaspi32.dll
 3641: 
 3642: static FARPROC aspi_get_address(const char * name, int verbose)
 3643: {
 3644:   FARPROC addr;
 3645:   assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
 3646: 
 3647:   if (!(addr = GetProcAddress(h_aspi_dll, name))) {
 3648:     if (verbose)
 3649:       pout("Missing %s() in WNASPI32.DLL\n", name);
 3650:     aspi_entry = 0;
 3651:     FreeLibrary(h_aspi_dll);
 3652:     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
 3653:     errno = ENOSYS;
 3654:     return 0;
 3655:   }
 3656:   return addr;
 3657: }
 3658: 
 3659: 
 3660: static int aspi_open_dll(int verbose)
 3661: {
 3662:   UINT (*aspi_info)(void);
 3663:   UINT info, rc;
 3664: 
 3665:   assert(!aspi_entry_valid());
 3666: 
 3667:   // Check structure layout
 3668:   assert(sizeof(ASPI_SRB_HEAD) == 8);
 3669:   assert(sizeof(ASPI_SRB_INQUIRY) == 58);
 3670:   assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
 3671:   assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
 3672:   assert(offsetof(ASPI_SRB,h.cmd) == 0);
 3673:   assert(offsetof(ASPI_SRB,h.flags) == 3);
 3674:   assert(offsetof(ASPI_SRB_IO,lun) == 9);
 3675:   assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
 3676:   assert(offsetof(ASPI_SRB_IO,workspace) == 28);
 3677:   assert(offsetof(ASPI_SRB_IO,cdb) == 48);
 3678: 
 3679:   if (h_aspi_dll == INVALID_HANDLE_VALUE) {
 3680:     // do not retry
 3681:     errno = ENOENT;
 3682:     return -1;
 3683:   }
 3684: 
 3685:   // Load ASPI DLL
 3686:   if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
 3687:     if (verbose)
 3688:       pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
 3689:     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
 3690:     errno = ENOENT;
 3691:     return -1;
 3692:   }
 3693:   if (scsi_debugmode > 1) {
 3694:     // Print full path of WNASPI32.DLL
 3695:     char path[MAX_PATH];
 3696:     if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
 3697:       strcpy(path, "*unknown*");
 3698:     pout("Using ASPI interface \"%s\"\n", path);
 3699:   }
 3700: 
 3701:   // Get ASPI entrypoints
 3702:   if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
 3703:     return -1;
 3704:   if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
 3705:     return -1;
 3706: 
 3707:   // Init ASPI manager and get number of adapters
 3708:   info = (aspi_info)();
 3709:   if (scsi_debugmode > 1)
 3710:     pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
 3711:   rc = (info >> 8) & 0xff;
 3712:   if (rc == ASPI_STATUS_NO_ADAPTERS) {
 3713:     num_aspi_adapters = 0;
 3714:   }
 3715:   else if (rc == ASPI_STATUS_NO_ERROR) {
 3716:     num_aspi_adapters = info & 0xff;
 3717:   }
 3718:   else {
 3719:     if (verbose)
 3720:       pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
 3721:     aspi_entry = 0;
 3722:     FreeLibrary(h_aspi_dll);
 3723:     h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
 3724:     errno = ENOENT;
 3725:     return -1;
 3726:   }
 3727: 
 3728:   if (scsi_debugmode)
 3729:     pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
 3730: 
 3731: #ifdef __CYGWIN__
 3732:   // save PID to detect fork() in aspi_entry_valid()
 3733:   aspi_dll_pid = GetCurrentProcessId();
 3734: #endif
 3735:   assert(aspi_entry_valid());
 3736:   return 0;
 3737: }
 3738: 
 3739: 
 3740: static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
 3741: {
 3742:   HANDLE event;
 3743:   // Create event
 3744:   if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
 3745:     pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
 3746:   }
 3747:   srb->i.event_handle = event;
 3748:   srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
 3749:   // Start ASPI request
 3750:   aspi_entry(srb);
 3751:   if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
 3752:     // Wait for event
 3753:     DWORD rc = WaitForSingleObject(event, timeout*1000L);
 3754:     if (rc != WAIT_OBJECT_0) {
 3755:       if (rc == WAIT_TIMEOUT) {
 3756:         pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
 3757:           srb->h.adapter, srb->i.target_id, timeout);
 3758:       }
 3759:       else {
 3760:         pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
 3761:           (unsigned long)(ULONG_PTR)event, rc, rc, GetLastError());
 3762:       }
 3763:       // TODO: ASPI_ABORT_IO command
 3764:       aspi_entry = 0;
 3765:       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
 3766:       return -EIO;
 3767:     }
 3768:   }
 3769:   CloseHandle(event);
 3770:   return 0;
 3771: }
 3772: 
 3773: 
 3774: win_aspi_device::win_aspi_device(smart_interface * intf,
 3775:   const char * dev_name, const char * req_type)
 3776: : smart_device(intf, dev_name, "scsi", req_type),
 3777:   m_adapter(-1), m_id(0)
 3778: {
 3779: }
 3780: 
 3781: bool win_aspi_device::is_open() const
 3782: {
 3783:   return (m_adapter >= 0);
 3784: }
 3785: 
 3786: bool win_aspi_device::open()
 3787: {
 3788:   // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
 3789:   unsigned adapter = ~0, id = ~0; int n1 = -1;
 3790:   const char * name = skipdev(get_dev_name());
 3791:   if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name)
 3792:         && adapter <= 9 && id < 16))
 3793:     return set_err(EINVAL);
 3794: 
 3795:   if (!aspi_entry_valid()) {
 3796:     if (aspi_open_dll(1/*verbose*/))
 3797:       return set_err(ENOENT);
 3798:   }
 3799: 
 3800:   // Adapter OK?
 3801:   if (adapter >= num_aspi_adapters) {
 3802:     pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
 3803:       adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
 3804:     if (!is_permissive())
 3805:       return set_err(ENOENT);
 3806:   }
 3807: 
 3808:   // Device present ?
 3809:   ASPI_SRB srb;
 3810:   memset(&srb, 0, sizeof(srb));
 3811:   srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
 3812:   srb.h.adapter = adapter; srb.i.target_id = id;
 3813:   if (aspi_call(&srb))
 3814:     return set_err(EIO);
 3815:   if (srb.h.status != ASPI_STATUS_NO_ERROR) {
 3816:     pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
 3817:     if (!is_permissive())
 3818:       return set_err(srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
 3819:   }
 3820:   else if (scsi_debugmode)
 3821:     pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
 3822: 
 3823:   m_adapter = (int)adapter; m_id = (unsigned char)id;
 3824:   return true;
 3825: }
 3826: 
 3827: 
 3828: bool win_aspi_device::close()
 3829: {
 3830:   // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
 3831:   return true;
 3832: }
 3833: 
 3834: 
 3835: // Scan for ASPI drives
 3836: 
 3837: bool win9x_smart_interface::scsi_scan(smart_device_list & devlist)
 3838: {
 3839:   if (!aspi_entry_valid()) {
 3840:     if (aspi_open_dll(scsi_debugmode/*default is quiet*/))
 3841:       return true;
 3842:   }
 3843: 
 3844:   for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {
 3845:     ASPI_SRB srb;
 3846: 
 3847:     if (ad > 9) {
 3848:       if (scsi_debugmode)
 3849:         pout(" ASPI Adapter %u: Ignored\n", ad);
 3850:       continue;
 3851:     }
 3852: 
 3853:     // Get adapter name
 3854:     memset(&srb, 0, sizeof(srb));
 3855:     srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
 3856:     srb.h.adapter = ad;
 3857:     if (aspi_call(&srb))
 3858:       break;
 3859: 
 3860:     if (srb.h.status != ASPI_STATUS_NO_ERROR) {
 3861:       if (scsi_debugmode)
 3862:         pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
 3863:       continue;
 3864:     }
 3865: 
 3866:     if (scsi_debugmode) {
 3867:       for (int i = 1; i < 16 && srb.q.adapter_id[i]; i++)
 3868:         if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
 3869:           srb.q.adapter_id[i] = '?';
 3870:       pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
 3871:     }
 3872: 
 3873:     bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5);
 3874: 
 3875:     for (unsigned id = 0; id <= 7; id++) {
 3876:       // Get device type
 3877:       memset(&srb, 0, sizeof(srb));
 3878:       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
 3879:       srb.h.adapter = ad; srb.i.target_id = id;
 3880:       if (aspi_call(&srb))
 3881:         return 0;
 3882:       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
 3883:         if (scsi_debugmode > 1)
 3884:           pout("  ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
 3885:         continue;
 3886:       }
 3887: 
 3888:       if (!ignore && srb.t.devtype == 0x00/*HDD*/) {
 3889:         if (scsi_debugmode)
 3890:           pout("  ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
 3891:         char name[20];
 3892:         sprintf(name, "/dev/scsi%u%u", ad, id);
 3893:         devlist.push_back( new win_aspi_device(this, name, "scsi") );
 3894:       }
 3895:       else if (scsi_debugmode)
 3896:         pout("  ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);
 3897:     }
 3898:   }
 3899:   return true;
 3900: }
 3901: 
 3902: 
 3903: // Interface to ASPI SCSI devices
 3904: bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)
 3905: {
 3906:   int report = scsi_debugmode; // TODO
 3907: 
 3908:   if (m_adapter < 0) {
 3909:     set_err(EBADF);
 3910:     return false;
 3911:   }
 3912: 
 3913:   if (!aspi_entry_valid()) {
 3914:     set_err(EBADF);
 3915:     return false;
 3916:   }
 3917: 
 3918:   if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
 3919:     set_err(EINVAL, "bad CDB length");
 3920:     return false;
 3921:   }
 3922: 
 3923:   if (report > 0) {
 3924:     // From os_linux.c
 3925:     int k, j;
 3926:     const unsigned char * ucp = iop->cmnd;
 3927:     const char * np;
 3928:     char buff[256];
 3929:     const int sz = (int)sizeof(buff);
 3930: 
 3931:     np = scsi_get_opcode_name(ucp[0]);
 3932:     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
 3933:     for (k = 0; k < (int)iop->cmnd_len; ++k)
 3934:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
 3935:     if ((report > 1) &&
 3936:       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
 3937:       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 3938: 
 3939:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
 3940:               "data, len=%d%s:\n", (int)iop->dxfer_len,
 3941:               (trunc ? " [only first 256 bytes shown]" : ""));
 3942:       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 3943:     }
 3944:     else
 3945:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 3946:     pout("%s", buff);
 3947:   }
 3948: 
 3949:   ASPI_SRB srb;
 3950:   memset(&srb, 0, sizeof(srb));
 3951:   srb.h.cmd = ASPI_CMD_EXECUTE_IO;
 3952:   srb.h.adapter = m_adapter;
 3953:   srb.i.target_id = m_id;
 3954:   //srb.i.lun = 0;
 3955:   srb.i.sense_size = ASPI_SENSE_SIZE;
 3956:   srb.i.cdb_size = iop->cmnd_len;
 3957:   memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
 3958: 
 3959:   switch (iop->dxfer_dir) {
 3960:     case DXFER_NONE:
 3961:       srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
 3962:       break;
 3963:     case DXFER_FROM_DEVICE:
 3964:       srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
 3965:       srb.i.data_size = iop->dxfer_len;
 3966:       srb.i.data_addr = iop->dxferp;
 3967:       break;
 3968:     case DXFER_TO_DEVICE:
 3969:       srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
 3970:       srb.i.data_size = iop->dxfer_len;
 3971:       srb.i.data_addr = iop->dxferp;
 3972:       break;
 3973:     default:
 3974:       set_err(EINVAL, "bad dxfer_dir");
 3975:       return false;
 3976:   }
 3977: 
 3978:   iop->resp_sense_len = 0;
 3979:   iop->scsi_status = 0;
 3980:   iop->resid = 0;
 3981: 
 3982:   if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
 3983:     // Timeout
 3984:     set_err(EIO, "ASPI Timeout"); return false;
 3985:   }
 3986: 
 3987:   if (srb.h.status != ASPI_STATUS_NO_ERROR) {
 3988:     if (   srb.h.status        == ASPI_STATUS_ERROR
 3989:         && srb.i.host_status   == ASPI_HSTATUS_NO_ERROR
 3990:         && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
 3991:       // Sense valid
 3992:       const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
 3993:       int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
 3994:       iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
 3995:       if (len > 0 && iop->sensep) {
 3996:         memcpy(iop->sensep, sense, len);
 3997:         iop->resp_sense_len = len;
 3998:         if (report > 1) {
 3999:           pout("  >>> Sense buffer, len=%d:\n", (int)len);
 4000:           dStrHex(iop->sensep, len , 1);
 4001:         }
 4002:       }
 4003:       if (report) {
 4004:         pout("  sense_key=%x asc=%x ascq=%x\n",
 4005:          sense[2] & 0xf, sense[12], sense[13]);
 4006:       }
 4007:       return true;
 4008:     }
 4009:     else {
 4010:       if (report)
 4011:         pout("  ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
 4012:       set_err(EIO);
 4013:       return false;
 4014:     }
 4015:   }
 4016: 
 4017:   if (report > 0)
 4018:     pout("  OK\n");
 4019: 
 4020:   if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
 4021:      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 4022:      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
 4023:         (trunc ? " [only first 256 bytes shown]" : ""));
 4024:         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 4025:   }
 4026: 
 4027:   return true;
 4028: }
 4029: 
 4030: #endif // WIN9X_SUPPORT
 4031: 
 4032: /////////////////////////////////////////////////////////////////////////////
 4033: // SPT Interface (for SCSI devices and ATA devices behind SATLs)
 4034: // Only supported in NT and later
 4035: /////////////////////////////////////////////////////////////////////////////
 4036: 
 4037: win_scsi_device::win_scsi_device(smart_interface * intf,
 4038:   const char * dev_name, const char * req_type)
 4039: : smart_device(intf, dev_name, "scsi", req_type)
 4040: {
 4041: }
 4042: 
 4043: bool win_scsi_device::open()
 4044: {
 4045:   const char * name = skipdev(get_dev_name()); int len = strlen(name);
 4046:   // sd[a-z],N => Physical drive 0-26, RAID port N
 4047:   char drive[1+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
 4048:   if (   sscanf(name, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
 4049:       && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
 4050:     return open(drive[0] - 'a', -1, -1, sub_addr);
 4051:   }
 4052:   // pd<m>,N => Physical drive <m>, RAID port N
 4053:   int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
 4054:   if (   sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
 4055:       && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
 4056:     return open(pd_num, -1, -1, sub_addr);
 4057:   }
 4058:   // [a-zA-Z]: => Physical drive behind logical drive 0-25
 4059:   int logdrive = drive_letter(name);
 4060:   if (logdrive >= 0) {
 4061:     return open(-1, logdrive, -1, -1);
 4062:   }
 4063:   // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
 4064:   int tape_num = -1; n1 = -1;
 4065:   if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 4066:     return open(-1, -1, tape_num, -1);
 4067:   }
 4068:   tape_num = -1; n1 = -1;
 4069:   if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 4070:     return open(-1, -1, tape_num, -1);
 4071:   }
 4072:   // tape<m> => tape drive <m>
 4073:   tape_num = -1; n1 = -1;
 4074:   if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 4075:     return open(-1, -1, tape_num, -1);
 4076:   }
 4077: 
 4078:   return set_err(EINVAL);
 4079: }
 4080: 
 4081: bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
 4082: {
 4083:   char b[128];
 4084:   b[sizeof(b) - 1] = '\0';
 4085:   if (pd_num >= 0)
 4086:     snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
 4087:   else if (ld_num >= 0)
 4088:     snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
 4089:   else if (tape_num >= 0)
 4090:     snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
 4091:   else {
 4092:     set_err(EINVAL);
 4093:     return false;
 4094:   }
 4095: 
 4096:   // Open device
 4097:   HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
 4098:            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
 4099:            OPEN_EXISTING, 0, 0);
 4100:   if (h == INVALID_HANDLE_VALUE) {
 4101:     set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());
 4102:     return false;
 4103:   }
 4104:   set_fh(h);
 4105:   return true;
 4106: }
 4107: 
 4108: 
 4109: typedef struct {
 4110:   SCSI_PASS_THROUGH_DIRECT spt;
 4111:   ULONG           Filler;
 4112:   UCHAR           ucSenseBuf[64];
 4113: } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
 4114: 
 4115: 
 4116: // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
 4117: // Used if DataTransferLength not supported by *_DIRECT.
 4118: static long scsi_pass_through_indirect(HANDLE h,
 4119:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
 4120: {
 4121:   struct SCSI_PASS_THROUGH_WITH_BUFFERS {
 4122:     SCSI_PASS_THROUGH spt;
 4123:     ULONG Filler;
 4124:     UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
 4125:     UCHAR ucDataBuf[512];
 4126:   };
 4127: 
 4128:   SCSI_PASS_THROUGH_WITH_BUFFERS sb;
 4129:   memset(&sb, 0, sizeof(sb));
 4130: 
 4131:   // DATA_OUT not implemented yet
 4132:   if (!(   sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
 4133:         && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
 4134:     return ERROR_INVALID_PARAMETER;
 4135: 
 4136:   sb.spt.Length = sizeof(sb.spt);
 4137:   sb.spt.CdbLength = sbd->spt.CdbLength;
 4138:   memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
 4139:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 4140:   sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
 4141:   sb.spt.DataIn = sbd->spt.DataIn;
 4142:   sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
 4143:   sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
 4144:   sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
 4145: 
 4146:   DWORD num_out;
 4147:   if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
 4148:          &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 4149:     return GetLastError();
 4150: 
 4151:   sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
 4152:   if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
 4153:     memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
 4154: 
 4155:   sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
 4156:   if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
 4157:     memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
 4158:   return 0;
 4159: }
 4160: 
 4161: 
 4162: // Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
 4163: bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
 4164: {
 4165:   int report = scsi_debugmode; // TODO
 4166: 
 4167:   if (report > 0) {
 4168:     int k, j;
 4169:     const unsigned char * ucp = iop->cmnd;
 4170:     const char * np;
 4171:     char buff[256];
 4172:     const int sz = (int)sizeof(buff);
 4173: 
 4174:     np = scsi_get_opcode_name(ucp[0]);
 4175:     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
 4176:     for (k = 0; k < (int)iop->cmnd_len; ++k)
 4177:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
 4178:     if ((report > 1) &&
 4179:       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
 4180:       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 4181: 
 4182:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
 4183:               "data, len=%d%s:\n", (int)iop->dxfer_len,
 4184:               (trunc ? " [only first 256 bytes shown]" : ""));
 4185:       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 4186:     }
 4187:     else
 4188:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 4189:     pout("%s", buff);
 4190:   }
 4191: 
 4192:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
 4193:   if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
 4194:     set_err(EINVAL, "cmnd_len too large");
 4195:     return false;
 4196:   }
 4197: 
 4198:   memset(&sb, 0, sizeof(sb));
 4199:   sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
 4200:   sb.spt.CdbLength = iop->cmnd_len;
 4201:   memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
 4202:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 4203:   sb.spt.SenseInfoOffset =
 4204:     offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
 4205:   sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
 4206: 
 4207:   bool direct = true;
 4208:   switch (iop->dxfer_dir) {
 4209:     case DXFER_NONE:
 4210:       sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
 4211:       break;
 4212:     case DXFER_FROM_DEVICE:
 4213:       sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
 4214:       sb.spt.DataTransferLength = iop->dxfer_len;
 4215:       sb.spt.DataBuffer = iop->dxferp;
 4216:       // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
 4217:       // transfers (needed for SMART STATUS check of JMicron USB bridges)
 4218:       if (sb.spt.DataTransferLength == 1)
 4219:         direct = false;
 4220:       break;
 4221:     case DXFER_TO_DEVICE:
 4222:       sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
 4223:       sb.spt.DataTransferLength = iop->dxfer_len;
 4224:       sb.spt.DataBuffer = iop->dxferp;
 4225:       break;
 4226:     default:
 4227:       set_err(EINVAL, "bad dxfer_dir");
 4228:       return false;
 4229:   }
 4230: 
 4231:   long err = 0;
 4232:   if (direct) {
 4233:     DWORD num_out;
 4234:     if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
 4235:            &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 4236:       err = GetLastError();
 4237:   }
 4238:   else
 4239:     err = scsi_pass_through_indirect(get_fh(), &sb);
 4240: 
 4241:   if (err)
 4242:     return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
 4243:       "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
 4244:       (direct ? "_DIRECT" : ""), err);
 4245: 
 4246:   iop->scsi_status = sb.spt.ScsiStatus;
 4247:   if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
 4248:     int slen = sb.ucSenseBuf[7] + 8;
 4249: 
 4250:     if (slen > (int)sizeof(sb.ucSenseBuf))
 4251:       slen = sizeof(sb.ucSenseBuf);
 4252:     if (slen > (int)iop->max_sense_len)
 4253:       slen = iop->max_sense_len;
 4254:     memcpy(iop->sensep, sb.ucSenseBuf, slen);
 4255:     iop->resp_sense_len = slen;
 4256:     if (report) {
 4257:       if (report > 1) {
 4258:         pout("  >>> Sense buffer, len=%d:\n", slen);
 4259:         dStrHex(iop->sensep, slen , 1);
 4260:       }
 4261:       if ((iop->sensep[0] & 0x7f) > 0x71)
 4262:         pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
 4263:              iop->scsi_status, iop->sensep[1] & 0xf,
 4264:              iop->sensep[2], iop->sensep[3]);
 4265:       else
 4266:         pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
 4267:              iop->scsi_status, iop->sensep[2] & 0xf,
 4268:              iop->sensep[12], iop->sensep[13]);
 4269:     }
 4270:   } else
 4271:     iop->resp_sense_len = 0;
 4272: 
 4273:   if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
 4274:     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
 4275:   else
 4276:     iop->resid = 0;
 4277: 
 4278:   if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
 4279:      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 4280:      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
 4281:         (trunc ? " [only first 256 bytes shown]" : ""));
 4282:         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 4283:   }
 4284:   return true;
 4285: }
 4286: 
 4287: // Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
 4288: static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
 4289: {
 4290:   int report = scsi_debugmode; // TODO
 4291: 
 4292:   if (report > 0) {
 4293:     int k, j;
 4294:     const unsigned char * ucp = iop->cmnd;
 4295:     const char * np;
 4296:     char buff[256];
 4297:     const int sz = (int)sizeof(buff);
 4298: 
 4299:     np = scsi_get_opcode_name(ucp[0]);
 4300:     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
 4301:     for (k = 0; k < (int)iop->cmnd_len; ++k)
 4302:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
 4303:     if ((report > 1) &&
 4304:       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
 4305:       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 4306: 
 4307:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
 4308:               "data, len=%d%s:\n", (int)iop->dxfer_len,
 4309:               (trunc ? " [only first 256 bytes shown]" : ""));
 4310:       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 4311:     }
 4312:     else
 4313:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 4314:     pout("%s", buff);
 4315:   }
 4316: 
 4317:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
 4318:   if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
 4319:     return EINVAL;
 4320:   }
 4321: 
 4322:   memset(&sb, 0, sizeof(sb));
 4323:   sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
 4324:   //sb.spt.PathId = 0;
 4325:   sb.spt.TargetId = targetid;
 4326:   //sb.spt.Lun = 0;
 4327:   sb.spt.CdbLength = iop->cmnd_len;
 4328:   memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
 4329:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 4330:   sb.spt.SenseInfoOffset =
 4331:     offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
 4332:   sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
 4333: 
 4334:   bool direct = true;
 4335:   switch (iop->dxfer_dir) {
 4336:     case DXFER_NONE:
 4337:       sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
 4338:       break;
 4339:     case DXFER_FROM_DEVICE:
 4340:       sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
 4341:       sb.spt.DataTransferLength = iop->dxfer_len;
 4342:       sb.spt.DataBuffer = iop->dxferp;
 4343:       // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
 4344:       // transfers (needed for SMART STATUS check of JMicron USB bridges)
 4345:       if (sb.spt.DataTransferLength == 1)
 4346:         direct = false;
 4347:       break;
 4348:     case DXFER_TO_DEVICE:
 4349:       sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
 4350:       sb.spt.DataTransferLength = iop->dxfer_len;
 4351:       sb.spt.DataBuffer = iop->dxferp;
 4352:       break;
 4353:     default:
 4354:       return EINVAL;
 4355:   }
 4356: 
 4357:   long err = 0;
 4358:   if (direct) {
 4359:     DWORD num_out;
 4360:     if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
 4361:            &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 4362:       err = GetLastError();
 4363:   }
 4364:   else
 4365:     err = scsi_pass_through_indirect(fd, &sb);
 4366: 
 4367:   if (err)
 4368:   {
 4369:     return err;
 4370:   }
 4371: 
 4372:   iop->scsi_status = sb.spt.ScsiStatus;
 4373:   if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
 4374:     int slen = sb.ucSenseBuf[7] + 8;
 4375: 
 4376:     if (slen > (int)sizeof(sb.ucSenseBuf))
 4377:       slen = sizeof(sb.ucSenseBuf);
 4378:     if (slen > (int)iop->max_sense_len)
 4379:       slen = iop->max_sense_len;
 4380:     memcpy(iop->sensep, sb.ucSenseBuf, slen);
 4381:     iop->resp_sense_len = slen;
 4382:     if (report) {
 4383:       if (report > 1) {
 4384:         pout("  >>> Sense buffer, len=%d:\n", slen);
 4385:         dStrHex(iop->sensep, slen , 1);
 4386:       }
 4387:       if ((iop->sensep[0] & 0x7f) > 0x71)
 4388:         pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
 4389:              iop->scsi_status, iop->sensep[1] & 0xf,
 4390:              iop->sensep[2], iop->sensep[3]);
 4391:       else
 4392:         pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
 4393:              iop->scsi_status, iop->sensep[2] & 0xf,
 4394:              iop->sensep[12], iop->sensep[13]);
 4395:     }
 4396:   } else
 4397:     iop->resp_sense_len = 0;
 4398: 
 4399:   if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
 4400:     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
 4401:   else
 4402:     iop->resid = 0;
 4403: 
 4404:   if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
 4405:      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 4406:      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
 4407:         (trunc ? " [only first 256 bytes shown]" : ""));
 4408:         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 4409:   }
 4410: 
 4411:   return 0;
 4412: }
 4413: 
 4414: 
 4415: #if 0 // For debugging areca code
 4416: 
 4417: static void dumpdata(unsigned char *block, int len)
 4418: {
 4419:   int ln = (len / 16) + 1;   // total line#
 4420:   unsigned char c;
 4421:   int pos = 0;
 4422: 
 4423:   printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);
 4424:   printf("      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      ASCII      \n");
 4425:   printf("=====================================================================\n");
 4426: 
 4427:   for ( int l = 0; l < ln && len; l++ )
 4428:   {
 4429:     // printf the line# and the HEX data
 4430:     // if a line data length < 16 then append the space to the tail of line to reach 16 chars
 4431:     printf("%02X | ", l);
 4432:     for ( pos = 0; pos < 16 && len; pos++, len-- )
 4433:     {
 4434:       c = block[l*16+pos];
 4435:       printf("%02X ", c);
 4436:     }
 4437: 
 4438:     if ( pos < 16 )
 4439:     {
 4440:       for ( int loop = pos; loop < 16; loop++ )
 4441:       {
 4442:         printf("   ");
 4443:       }
 4444:     }
 4445: 
 4446:     // print ASCII char
 4447:     for ( int loop = 0; loop < pos; loop++ )
 4448:     {
 4449:       c = block[l*16+loop];
 4450:       if ( c >= 0x20 && c <= 0x7F )
 4451:       {
 4452:         printf("%c", c);
 4453:       }
 4454:       else
 4455:       {
 4456:         printf(".");
 4457:       }
 4458:     }
 4459:     printf("\n");
 4460:   }
 4461:   printf("=====================================================================\n");
 4462: }
 4463: 
 4464: #endif
 4465: 
 4466: // PURPOSE
 4467: //   This is an interface routine meant to isolate the OS dependent
 4468: //   parts of the code, and to provide a debugging interface.  Each
 4469: //   different port and OS needs to provide it's own interface.  This
 4470: //   is the Windows interface to the Areca "arcmsr" driver.  It allows ATA
 4471: //   commands to be passed through the SCSI driver.
 4472: // DETAILED DESCRIPTION OF ARGUMENTS
 4473: //   fd: is the file descriptor provided by open()
 4474: //   disknum is the disk number (0 to 127) in the RAID array
 4475: //   command: defines the different operations.
 4476: //   select: additional input data if needed (which log, which type of
 4477: //           self-test).
 4478: //   data:   location to write output data, if needed (512 bytes).
 4479: //   Note: not all commands use all arguments.
 4480: // RETURN VALUES
 4481: //  -1 if the command failed
 4482: //   0 if the command succeeded,
 4483: //   STATUS_CHECK routine:
 4484: //  -1 if the command failed
 4485: //   0 if the command succeeded and disk SMART status is "OK"
 4486: //   1 if the command succeeded and disk SMART status is "FAILING"
 4487: int win_areca_device::arcmsr_command_handler(HANDLE fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len)
 4488: {
 4489:   int ioctlreturn = 0;
 4490:   sSRB_BUFFER sBuf;
 4491:   struct scsi_cmnd_io io_hdr;
 4492:   int dir = DXFER_TO_DEVICE;
 4493: 
 4494:   UINT8 cdb[10];
 4495:   UINT8 sense[32];
 4496: 
 4497:   unsigned char *areca_return_packet;
 4498:   int total = 0;
 4499:   int expected = -1;
 4500:   unsigned char return_buff[2048];
 4501:   unsigned char *ptr = &return_buff[0];
 4502:   memset(return_buff, 0, sizeof(return_buff));
 4503: 
 4504:   memset((unsigned char *)&sBuf, 0, sizeof(sBuf));
 4505:   memset(&io_hdr, 0, sizeof(io_hdr));
 4506:   memset(cdb, 0, sizeof(cdb));
 4507:   memset(sense, 0, sizeof(sense));
 4508: 
 4509: 
 4510:   sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL);
 4511:   memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));
 4512:   sBuf.srbioctl.Timeout = 10000;
 4513:   sBuf.srbioctl.ControlCode = arcmsr_cmd;
 4514: 
 4515:   switch ( arcmsr_cmd )
 4516:   {
 4517:   // command for writing data to driver
 4518:   case ARCMSR_IOCTL_WRITE_WQBUFFER:
 4519:     if ( data && data_len )
 4520:     {
 4521:       sBuf.srbioctl.Length = data_len;
 4522:       memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len);
 4523:     }
 4524:     // commands for clearing related buffer of driver
 4525:   case ARCMSR_IOCTL_CLEAR_RQBUFFER:
 4526:   case ARCMSR_IOCTL_CLEAR_WQBUFFER:
 4527:     cdb[0] = 0x3B; //SCSI_WRITE_BUF command;
 4528:     break;
 4529:   // command for reading data from driver
 4530:   case ARCMSR_IOCTL_READ_RQBUFFER:
 4531:   // command for identifying driver
 4532:   case ARCMSR_IOCTL_RETURN_CODE_3F:
 4533:     cdb[0] = 0x3C; //SCSI_READ_BUF command;
 4534:     dir = DXFER_FROM_DEVICE;
 4535:     break;
 4536:   default:
 4537:     // unknown arcmsr commands
 4538:     return -1;
 4539:   }
 4540: 
 4541:   cdb[1] = 0x01;
 4542:   cdb[2] = 0xf0;
 4543: 
 4544:   io_hdr.dxfer_dir = dir;
 4545:   io_hdr.dxfer_len = sizeof(sBuf);
 4546:   io_hdr.dxferp = (unsigned char *)&sBuf;
 4547:   io_hdr.cmnd = cdb;
 4548:   io_hdr.cmnd_len = sizeof(cdb);
 4549:   io_hdr.sensep = sense;
 4550:   io_hdr.max_sense_len = sizeof(sense);
 4551:   io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
 4552: 
 4553:   while ( 1 )
 4554:   {
 4555:     ioctlreturn = scsi_pass_through_direct(fd, 16, &io_hdr);
 4556:     if ( ioctlreturn || io_hdr.scsi_status )
 4557:     {
 4558:       ioctlreturn = scsi_pass_through_direct(fd, 127, &io_hdr);
 4559:       if ( ioctlreturn || io_hdr.scsi_status )
 4560:       {
 4561:         // errors found
 4562:         break;
 4563:       }
 4564:     }
 4565: 
 4566:     if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER )
 4567:     {
 4568:       // if succeeded, just returns the length of outgoing data
 4569:       return data_len;
 4570:     }
 4571: 
 4572:     if ( sBuf.srbioctl.Length )
 4573:     {
 4574:       //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
 4575:       memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
 4576:       ptr += sBuf.srbioctl.Length;
 4577:       total += sBuf.srbioctl.Length;
 4578:       // the returned bytes enough to compute payload length ?
 4579:       if ( expected < 0 && total >= 5 )
 4580:       {
 4581:         areca_return_packet = (unsigned char *)&return_buff[0];
 4582:         if ( areca_return_packet[0] == 0x5E &&
 4583:            areca_return_packet[1] == 0x01 &&
 4584:            areca_return_packet[2] == 0x61 )
 4585:         {
 4586:           // valid header, let's compute the returned payload length,
 4587:           // we expected the total length is
 4588:           // payload + 3 bytes header + 2 bytes length + 1 byte checksum
 4589:           expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6;
 4590:         }
 4591:       }
 4592: 
 4593:       if ( total >= 7 && total >= expected )
 4594:       {
 4595:         //printf("total bytes received = %d, expected length = %d\n", total, expected);
 4596: 
 4597:         // ------ Okay! we received enough --------
 4598:         break;
 4599:       }
 4600:     }
 4601:   }
 4602: 
 4603:   // Deal with the different error cases
 4604:   if ( arcmsr_cmd == ARCMSR_IOCTL_RETURN_CODE_3F )
 4605:   {
 4606:     // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...)
 4607:     return -4;
 4608:   }
 4609: 
 4610:   if ( ioctlreturn )
 4611:   {
 4612:     pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn);
 4613:     return -2;
 4614:   }
 4615: 
 4616:   if ( io_hdr.scsi_status )
 4617:   {
 4618:     pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status);
 4619:     return -3;
 4620:   }
 4621: 
 4622:   if ( data )
 4623:   {
 4624:     memcpy(data, return_buff, total);
 4625:   }
 4626: 
 4627:   return total;
 4628: }
 4629: 
 4630: 
 4631: win_areca_device::win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum)
 4632: : smart_device(intf, dev_name, "areca", "areca"),
 4633:   m_disknum(disknum),
 4634:   m_encnum(encnum)
 4635: {
 4636:   set_fh(fh);
 4637:   set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 4638: }
 4639: 
 4640: bool win_areca_device::open()
 4641: {
 4642:   HANDLE hFh;
 4643: 
 4644:   if( is_open() )
 4645:   {
 4646:     return true;
 4647:   }
 4648: 
 4649:   hFh = CreateFile( get_dev_name(),
 4650:                     GENERIC_READ|GENERIC_WRITE,
 4651:                     FILE_SHARE_READ|FILE_SHARE_WRITE,
 4652:                     NULL,
 4653:                     OPEN_EXISTING,
 4654:                     0,
 4655:                     NULL );
 4656:   if(hFh == INVALID_HANDLE_VALUE)
 4657:   {
 4658:     return false;
 4659:   }
 4660: 
 4661:   set_fh(hFh);
 4662:   return true;
 4663: }
 4664: 
 4665: // Areca RAID Controller
 4666: bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 4667: {
 4668:   // ATA input registers
 4669:   typedef struct _ATA_INPUT_REGISTERS
 4670:   {
 4671:     unsigned char features;
 4672:     unsigned char sector_count;
 4673:     unsigned char sector_number;
 4674:     unsigned char cylinder_low;
 4675:     unsigned char cylinder_high;
 4676:     unsigned char device_head;
 4677:     unsigned char command;
 4678:     unsigned char reserved[8];
 4679:     unsigned char data[512]; // [in/out] buffer for outgoing/incoming data
 4680:   } sATA_INPUT_REGISTERS;
 4681: 
 4682:   // ATA output registers
 4683:   // Note: The output registers is re-sorted for areca internal use only
 4684:   typedef struct _ATA_OUTPUT_REGISTERS
 4685:   {
 4686:     unsigned char error;
 4687:     unsigned char status;
 4688:     unsigned char sector_count;
 4689:     unsigned char sector_number;
 4690:     unsigned char cylinder_low;
 4691:     unsigned char cylinder_high;
 4692:   } sATA_OUTPUT_REGISTERS;
 4693: 
 4694:   // Areca packet format for outgoing:
 4695:   // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
 4696:   // B[3~4] : 2 bytes command length + variant data length, little endian
 4697:   // B[5]   : 1 bytes areca defined command code, ATA passthrough command code is 0x1c
 4698:   // B[6~last-1] : variant bytes payload data
 4699:   // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
 4700:   //
 4701:   //
 4702:   //   header 3 bytes  length 2 bytes   cmd 1 byte    payload data x bytes  cs 1 byte
 4703:   // +--------------------------------------------------------------------------------+
 4704:   // + 0x5E 0x01 0x61 |   0x00 0x00   |     0x1c   | .................... |   0x00    |
 4705:   // +--------------------------------------------------------------------------------+
 4706:   //
 4707: 
 4708:   //Areca packet format for incoming:
 4709:   // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
 4710:   // B[3~4] : 2 bytes payload length, little endian
 4711:   // B[5~last-1] : variant bytes returned payload data
 4712:   // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
 4713:   //
 4714:   //
 4715:   //   header 3 bytes  length 2 bytes   payload data x bytes  cs 1 byte
 4716:   // +-------------------------------------------------------------------+
 4717:   // + 0x5E 0x01 0x61 |   0x00 0x00   | .................... |   0x00    |
 4718:   // +-------------------------------------------------------------------+
 4719:   unsigned char    areca_packet[640];
 4720:   int areca_packet_len = sizeof(areca_packet);
 4721:   unsigned char cs = 0;
 4722: 
 4723:   sATA_INPUT_REGISTERS *ata_cmd;
 4724: 
 4725:   // For debugging
 4726: #if 0
 4727:   memset(sInq, 0, sizeof(sInq));
 4728:   scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq));
 4729:   dumpdata((unsigned char *)sInq, sizeof(sInq));
 4730: #endif
 4731:   memset(areca_packet, 0, areca_packet_len);
 4732: 
 4733:   // ----- BEGIN TO SETUP HEADERS -------
 4734:   areca_packet[0] = 0x5E;
 4735:   areca_packet[1] = 0x01;
 4736:   areca_packet[2] = 0x61;
 4737:   areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff);
 4738:   areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
 4739:   areca_packet[5] = 0x1c;  // areca defined code for ATA passthrough command
 4740: 
 4741:   // ----- BEGIN TO SETUP PAYLOAD DATA -----
 4742:   memcpy(&areca_packet[7], "SmrT", 4);  // areca defined password
 4743:   ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
 4744: 
 4745:   // Set registers
 4746:   {
 4747:     const ata_in_regs & r = in.in_regs;
 4748:     ata_cmd->features      = r.features;
 4749:     ata_cmd->sector_count  = r.sector_count;
 4750:     ata_cmd->sector_number = r.lba_low;
 4751:     ata_cmd->cylinder_low  = r.lba_mid;
 4752:     ata_cmd->cylinder_high = r.lba_high;
 4753:     ata_cmd->device_head   = r.device;
 4754:     ata_cmd->command       = r.command;
 4755:   }
 4756:   bool readdata = false;
 4757:   if (in.direction == ata_cmd_in::data_in) {
 4758:       readdata = true;
 4759:       // the command will read data
 4760:       areca_packet[6] = 0x13;
 4761:   }
 4762:   else if ( in.direction == ata_cmd_in::no_data )
 4763:   {
 4764:     // the commands will return no data
 4765:     areca_packet[6] = 0x15;
 4766:   }
 4767:   else if (in.direction == ata_cmd_in::data_out)
 4768:   {
 4769:     // the commands will write data
 4770:     memcpy(ata_cmd->data, in.buffer, in.size);
 4771:     areca_packet[6] = 0x14;
 4772:   }
 4773:   else {
 4774:       // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
 4775:       return set_err(ENOSYS);
 4776:   }
 4777: 
 4778:   areca_packet[11] = m_disknum - 1;  // disk#
 4779:   areca_packet[19] = m_encnum - 1;   // enc#
 4780: 
 4781:   // ----- BEGIN TO SETUP CHECKSUM -----
 4782:   for ( int loop = 3; loop < areca_packet_len - 1; loop++ )
 4783:   {
 4784:     cs += areca_packet[loop];
 4785:   }
 4786:   areca_packet[areca_packet_len-1] = cs;
 4787: 
 4788:   // ----- BEGIN TO SEND TO ARECA DRIVER ------
 4789:   int expected = 0;
 4790:   unsigned char return_buff[2048];
 4791:   memset(return_buff, 0, sizeof(return_buff));
 4792: 
 4793:   expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0);
 4794:   if (expected==-3) {
 4795:       return set_err(EIO);
 4796:   }
 4797: 
 4798:   expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0);
 4799:   expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len);
 4800:   if ( expected > 0 )
 4801:   {
 4802:     expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff));
 4803:   }
 4804:   if ( expected < 0 )
 4805:   {
 4806:     return set_err(EIO);
 4807:   }
 4808: 
 4809:   // ----- VERIFY THE CHECKSUM -----
 4810:   cs = 0;
 4811:   for ( int loop = 3; loop < expected - 1; loop++ )
 4812:   {
 4813:     cs += return_buff[loop];
 4814:   }
 4815: 
 4816:   if ( return_buff[expected - 1] != cs )
 4817:   {
 4818:     return set_err(EIO);
 4819:   }
 4820: 
 4821:   sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ;
 4822:   if ( ata_out->status )
 4823:   {
 4824:     if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
 4825:      && !nonempty((unsigned char *)in.buffer, in.size))
 4826:      {
 4827:         return set_err(ENODEV, "No drive on port %d", m_disknum);
 4828:      }
 4829:   }
 4830: 
 4831:   // returns with data
 4832:   if (readdata)
 4833:   {
 4834:     memcpy(in.buffer, &return_buff[7], in.size);
 4835:   }
 4836: 
 4837:   // Return register values
 4838:   {
 4839:     ata_out_regs & r = out.out_regs;
 4840:     r.error          = ata_out->error;
 4841:     r.sector_count   = ata_out->sector_count;
 4842:     r.lba_low        = ata_out->sector_number;
 4843:     r.lba_mid        = ata_out->cylinder_low;
 4844:     r.lba_high       = ata_out->cylinder_high;
 4845:     r.status         = ata_out->status;
 4846:   }
 4847:   return true;
 4848: }
 4849: 
 4850: 
 4851: bool win_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 4852: {
 4853: #define    SYNCOBJNAME "Global\\SynIoctlMutex"
 4854:   int ctlrnum = -1;
 4855:   char mutexstr[64];
 4856:   SECURITY_ATTRIBUTES sa;
 4857:   PSECURITY_DESCRIPTOR pSD;
 4858:   HANDLE hmutex;
 4859: 
 4860:   if (!ata_cmd_is_ok(in,
 4861:     true, // data_out_support
 4862:     false, // TODO: multi_sector_support
 4863:     true) // ata_48bit_support
 4864:   )
 4865:     return false;
 4866: 
 4867:   // Support 48-bit commands with zero high bytes
 4868:   if (in.in_regs.is_real_48bit_cmd())
 4869:     return set_err(ENOSYS, "48-bit ATA commands not fully supported by Areca");
 4870: 
 4871:   if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
 4872:     return set_err(EINVAL, "unable to parse device name");
 4873: 
 4874:   memset(mutexstr, 0, sizeof(mutexstr));
 4875:   sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum);
 4876:   pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
 4877:   if ( !InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) )
 4878:   {
 4879:     LocalFree((HLOCAL)pSD);
 4880:     return set_err(EIO, "InitializeSecurityDescriptor failed");
 4881:   }
 4882: 
 4883:   if ( !SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE) )
 4884:   {
 4885:     LocalFree((HLOCAL)pSD);
 4886:     return set_err(EIO, "SetSecurityDescriptor failed");
 4887:   }
 4888: 
 4889:   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
 4890:   sa.lpSecurityDescriptor = pSD;
 4891:   sa.bInheritHandle = TRUE;
 4892:   hmutex = CreateMutex(&sa, FALSE, mutexstr);
 4893:   if ( hmutex == NULL )
 4894:   {
 4895:     LocalFree((HLOCAL)pSD);
 4896:     return set_err(EIO, "CreateMutex failed");
 4897:   }
 4898: 
 4899:   // atomic access to driver
 4900:   WaitForSingleObject(hmutex, INFINITE);
 4901:   bool ok = arcmsr_ata_pass_through(in,out);
 4902:   ReleaseMutex(hmutex);
 4903: 
 4904:   if(hmutex)
 4905:   {
 4906:     CloseHandle(hmutex);
 4907:   }
 4908: 
 4909:   if ( (HLOCAL)pSD )
 4910:   {
 4911:     LocalFree((HLOCAL)pSD);
 4912:   }
 4913: 
 4914:   return ok;
 4915: }
 4916: 
 4917: 
 4918: //////////////////////////////////////////////////////////////////////////////////////////////////
 4919: 
 4920: 
 4921: } // namespace
 4922: 
 4923: /////////////////////////////////////////////////////////////////////////////
 4924: 
 4925: // Initialize platform interface and register with smi()
 4926: void smart_interface::init()
 4927: {
 4928:   {
 4929:     // Remove "." from DLL search path if supported
 4930:     // to prevent DLL preloading attacks
 4931:     BOOL (WINAPI * SetDllDirectoryA_p)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR))
 4932:       GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
 4933:     if (SetDllDirectoryA_p)
 4934:       SetDllDirectoryA_p("");
 4935:   }
 4936: 
 4937:   // Select interface for Windows flavor
 4938:   if (GetVersion() & 0x80000000) {
 4939: #if WIN9X_SUPPORT
 4940:     static os_win32::win9x_smart_interface the_win9x_interface;
 4941:     smart_interface::set(&the_win9x_interface);
 4942: #else
 4943:     throw std::runtime_error("Win9x/ME not supported");
 4944: #endif
 4945:   }
 4946:   else {
 4947:     static os_win32::winnt_smart_interface the_winnt_interface;
 4948:     smart_interface::set(&the_winnt_interface);
 4949:   }
 4950: }
 4951: 
 4952: 
 4953: #ifndef __CYGWIN__
 4954: 
 4955: // Get exe directory
 4956: // (prototype in utiliy.h)
 4957: std::string get_exe_dir()
 4958: {
 4959:   char path[MAX_PATH];
 4960:   // Get path of this exe
 4961:   if (!GetModuleFileNameA(GetModuleHandleA(0), path, sizeof(path)))
 4962:     throw std::runtime_error("GetModuleFileName() failed");
 4963:   // Replace backslash by slash
 4964:   int sl = -1;
 4965:   for (int i = 0; path[i]; i++)
 4966:     if (path[i] == '\\') {
 4967:       path[i] = '/'; sl = i;
 4968:     }
 4969:   // Remove filename
 4970:   if (sl >= 0)
 4971:     path[sl] = 0;
 4972:   return path;
 4973: }
 4974: 
 4975: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>