File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / os_win32.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:17:35 2013 UTC (10 years, 11 months ago) by misho
Branches: smartmontools, elwix, MAIN
CVS tags: v6_1p0, v6_1, HEAD
6.1

    1: /*
    2:  * os_win32.cpp
    3:  *
    4:  * Home page of code is: http://smartmontools.sourceforge.net
    5:  *
    6:  * Copyright (C) 2004-13 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: #include "dev_areca.h"
   32: 
   33: #include "os_win32/wmiquery.h"
   34: 
   35: #include <errno.h>
   36: 
   37: #ifdef _DEBUG
   38: #include <assert.h>
   39: #else
   40: #undef assert
   41: #define assert(x) /* */
   42: #endif
   43: 
   44: #include <stddef.h> // offsetof()
   45: #include <io.h> // access()
   46: 
   47: // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
   48: #define WIN32_LEAN_AND_MEAN
   49: #include <windows.h>
   50: 
   51: #if HAVE_NTDDDISK_H
   52: // i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32
   53: // (Missing: FILE_DEVICE_SCSI)
   54: #include <devioctl.h>
   55: #include <ntdddisk.h>
   56: #include <ntddscsi.h>
   57: #include <ntddstor.h>
   58: #elif HAVE_DDK_NTDDDISK_H
   59: // older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
   60: // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
   61: #include <ddk/ntdddisk.h>
   62: #include <ddk/ntddscsi.h>
   63: #include <ddk/ntddstor.h>
   64: #else
   65: // MSVC10, older MinGW
   66: // (Missing: IOCTL_SCSI_MINIPORT_*)
   67: #include <ntddscsi.h>
   68: #include <winioctl.h>
   69: #endif
   70: 
   71: #ifndef _WIN32
   72: // csmisas.h requires _WIN32 but w32api-headers no longer define it on Cygwin
   73: #define _WIN32
   74: #endif
   75: 
   76: // CSMI support
   77: #include "csmisas.h"
   78: 
   79: // Macro to check constants at compile time using a dummy typedef
   80: #define ASSERT_CONST(c, n) \
   81:   typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
   82: #define ASSERT_SIZEOF(t, n) \
   83:   typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
   84: 
   85: #ifndef _WIN64
   86: #define SELECT_WIN_32_64(x32, x64) (x32)
   87: #else
   88: #define SELECT_WIN_32_64(x32, x64) (x64)
   89: #endif
   90: 
   91: const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp,v 1.1.1.3 2013/07/22 01:17:35 misho Exp $";
   92: 
   93: /////////////////////////////////////////////////////////////////////////////
   94: // Windows I/O-controls, some declarations are missing in the include files
   95: 
   96: extern "C" {
   97: 
   98: // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
   99: 
  100: ASSERT_CONST(SMART_GET_VERSION, 0x074080);
  101: ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
  102: ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088);
  103: ASSERT_SIZEOF(GETVERSIONINPARAMS, 24);
  104: ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
  105: ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
  106: 
  107: 
  108: // IDE PASS THROUGH (2000, XP, undocumented)
  109: 
  110: #ifndef IOCTL_IDE_PASS_THROUGH
  111: 
  112: #define IOCTL_IDE_PASS_THROUGH \
  113:   CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  114: 
  115: #endif // IOCTL_IDE_PASS_THROUGH
  116: 
  117: #pragma pack(1)
  118: 
  119: typedef struct {
  120:   IDEREGS IdeReg;
  121:   ULONG DataBufferSize;
  122:   UCHAR DataBuffer[1];
  123: } ATA_PASS_THROUGH;
  124: 
  125: #pragma pack()
  126: 
  127: ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
  128: ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
  129: 
  130: 
  131: // ATA PASS THROUGH (Win2003, XP SP2)
  132: 
  133: #ifndef IOCTL_ATA_PASS_THROUGH
  134: 
  135: #define IOCTL_ATA_PASS_THROUGH \
  136:   CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
  137: 
  138: typedef struct _ATA_PASS_THROUGH_EX {
  139:   USHORT Length;
  140:   USHORT AtaFlags;
  141:   UCHAR PathId;
  142:   UCHAR TargetId;
  143:   UCHAR Lun;
  144:   UCHAR ReservedAsUchar;
  145:   ULONG DataTransferLength;
  146:   ULONG TimeOutValue;
  147:   ULONG ReservedAsUlong;
  148:   ULONG_PTR DataBufferOffset;
  149:   UCHAR PreviousTaskFile[8];
  150:   UCHAR CurrentTaskFile[8];
  151: } ATA_PASS_THROUGH_EX;
  152: 
  153: #define ATA_FLAGS_DRDY_REQUIRED 0x01
  154: #define ATA_FLAGS_DATA_IN       0x02
  155: #define ATA_FLAGS_DATA_OUT      0x04
  156: #define ATA_FLAGS_48BIT_COMMAND 0x08
  157: #define ATA_FLAGS_USE_DMA       0x10
  158: #define ATA_FLAGS_NO_MULTIPLE   0x20 // Vista
  159: 
  160: #endif // IOCTL_ATA_PASS_THROUGH
  161: 
  162: ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
  163: ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48));
  164: 
  165: 
  166: // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
  167: 
  168: ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
  169: ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014);
  170: ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56));
  171: ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT, SELECT_WIN_32_64(44, 56));
  172: 
  173: 
  174: // SMART IOCTL via SCSI MINIPORT ioctl
  175: 
  176: #ifndef FILE_DEVICE_SCSI
  177: #define FILE_DEVICE_SCSI 0x001b
  178: #endif
  179: 
  180: #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
  181: 
  182: #define IOCTL_SCSI_MINIPORT_SMART_VERSION               ((FILE_DEVICE_SCSI << 16) + 0x0500)
  183: #define IOCTL_SCSI_MINIPORT_IDENTIFY                    ((FILE_DEVICE_SCSI << 16) + 0x0501)
  184: #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS          ((FILE_DEVICE_SCSI << 16) + 0x0502)
  185: #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS       ((FILE_DEVICE_SCSI << 16) + 0x0503)
  186: #define IOCTL_SCSI_MINIPORT_ENABLE_SMART                ((FILE_DEVICE_SCSI << 16) + 0x0504)
  187: #define IOCTL_SCSI_MINIPORT_DISABLE_SMART               ((FILE_DEVICE_SCSI << 16) + 0x0505)
  188: #define IOCTL_SCSI_MINIPORT_RETURN_STATUS               ((FILE_DEVICE_SCSI << 16) + 0x0506)
  189: #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE     ((FILE_DEVICE_SCSI << 16) + 0x0507)
  190: #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES       ((FILE_DEVICE_SCSI << 16) + 0x0508)
  191: #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS       ((FILE_DEVICE_SCSI << 16) + 0x0509)
  192: #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
  193: #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG              ((FILE_DEVICE_SCSI << 16) + 0x050b)
  194: #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG             ((FILE_DEVICE_SCSI << 16) + 0x050c)
  195: 
  196: #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
  197: 
  198: ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
  199: ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
  200: 
  201: 
  202: // IOCTL_STORAGE_QUERY_PROPERTY
  203: 
  204: #ifndef IOCTL_STORAGE_QUERY_PROPERTY
  205: 
  206: #define IOCTL_STORAGE_QUERY_PROPERTY \
  207:   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
  208: 
  209: typedef struct _STORAGE_DEVICE_DESCRIPTOR {
  210:   ULONG Version;
  211:   ULONG Size;
  212:   UCHAR DeviceType;
  213:   UCHAR DeviceTypeModifier;
  214:   BOOLEAN RemovableMedia;
  215:   BOOLEAN CommandQueueing;
  216:   ULONG VendorIdOffset;
  217:   ULONG ProductIdOffset;
  218:   ULONG ProductRevisionOffset;
  219:   ULONG SerialNumberOffset;
  220:   STORAGE_BUS_TYPE BusType;
  221:   ULONG RawPropertiesLength;
  222:   UCHAR RawDeviceProperties[1];
  223: } STORAGE_DEVICE_DESCRIPTOR;
  224: 
  225: typedef enum _STORAGE_QUERY_TYPE {
  226:   PropertyStandardQuery = 0,
  227:   PropertyExistsQuery,
  228:   PropertyMaskQuery,
  229:   PropertyQueryMaxDefined
  230: } STORAGE_QUERY_TYPE;
  231: 
  232: typedef enum _STORAGE_PROPERTY_ID {
  233:   StorageDeviceProperty = 0,
  234:   StorageAdapterProperty,
  235:   StorageDeviceIdProperty,
  236:   StorageDeviceUniqueIdProperty,
  237:   StorageDeviceWriteCacheProperty,
  238:   StorageMiniportProperty,
  239:   StorageAccessAlignmentProperty
  240: } STORAGE_PROPERTY_ID;
  241: 
  242: typedef struct _STORAGE_PROPERTY_QUERY {
  243:   STORAGE_PROPERTY_ID PropertyId;
  244:   STORAGE_QUERY_TYPE QueryType;
  245:   UCHAR AdditionalParameters[1];
  246: } STORAGE_PROPERTY_QUERY;
  247: 
  248: #endif // IOCTL_STORAGE_QUERY_PROPERTY
  249: 
  250: ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400);
  251: ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3);
  252: ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3);
  253: 
  254: 
  255: // IOCTL_STORAGE_PREDICT_FAILURE
  256: 
  257: ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100);
  258: ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
  259: 
  260: 
  261: // 3ware specific versions of SMART ioctl structs
  262: 
  263: #define SMART_VENDOR_3WARE      0x13C1  // identifies 3ware specific parameters
  264: 
  265: #pragma pack(1)
  266: 
  267: typedef struct _GETVERSIONINPARAMS_EX {
  268:   BYTE bVersion;
  269:   BYTE bRevision;
  270:   BYTE bReserved;
  271:   BYTE bIDEDeviceMap;
  272:   DWORD fCapabilities;
  273:   DWORD dwDeviceMapEx;  // 3ware specific: RAID drive bit map
  274:   WORD wIdentifier;     // Vendor specific identifier
  275:   WORD wControllerId;   // 3ware specific: Controller ID (0,1,...)
  276:   ULONG dwReserved[2];
  277: } GETVERSIONINPARAMS_EX;
  278: 
  279: typedef struct _SENDCMDINPARAMS_EX {
  280:   DWORD cBufferSize;
  281:   IDEREGS irDriveRegs;
  282:   BYTE bDriveNumber;
  283:   BYTE bPortNumber;     // 3ware specific: port number
  284:   WORD wIdentifier;     // Vendor specific identifier
  285:   DWORD dwReserved[4];
  286:   BYTE bBuffer[1];
  287: } SENDCMDINPARAMS_EX;
  288: 
  289: #pragma pack()
  290: 
  291: ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS));
  292: ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
  293: 
  294: 
  295: // CSMI structs
  296: 
  297: ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL));
  298: ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204);
  299: ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080);
  300: ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168);
  301: 
  302: } // extern "C"
  303: 
  304: /////////////////////////////////////////////////////////////////////////////
  305: 
  306: namespace os_win32 { // no need to publish anything, name provided for Doxygen
  307: 
  308: #ifdef _MSC_VER
  309: #pragma warning(disable:4250)
  310: #endif
  311: 
  312: class win_smart_device
  313: : virtual public /*implements*/ smart_device
  314: {
  315: public:
  316:   win_smart_device()
  317:     : smart_device(never_called),
  318:       m_fh(INVALID_HANDLE_VALUE)
  319:     { }
  320: 
  321:   virtual ~win_smart_device() throw();
  322: 
  323:   virtual bool is_open() const;
  324: 
  325:   virtual bool close();
  326: 
  327: protected:
  328:   /// Set handle for open() in derived classes.
  329:   void set_fh(HANDLE fh)
  330:     { m_fh = fh; }
  331: 
  332:   /// Return handle for derived classes.
  333:   HANDLE get_fh() const
  334:     { return m_fh; }
  335: 
  336: private:
  337:   HANDLE m_fh; ///< File handle
  338: };
  339: 
  340: 
  341: /////////////////////////////////////////////////////////////////////////////
  342: 
  343: class win_ata_device
  344: : public /*implements*/ ata_device,
  345:   public /*extends*/ win_smart_device
  346: {
  347: public:
  348:   win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
  349: 
  350:   virtual ~win_ata_device() throw();
  351: 
  352:   virtual bool open();
  353: 
  354:   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  355: 
  356:   virtual bool ata_identify_is_cached() const;
  357: 
  358: private:
  359:   bool open(int phydrive, int logdrive, const char * options, int port);
  360: 
  361:   std::string m_options;
  362:   bool m_usr_options; // options set by user?
  363:   bool m_admin; // open with admin access?
  364:   int m_phydrive; // PhysicalDriveN or -1
  365:   bool m_id_is_cached; // ata_identify_is_cached() return value.
  366:   bool m_is_3ware; // LSI/3ware controller detected?
  367:   int m_port; // LSI/3ware port
  368:   int m_smartver_state;
  369: };
  370: 
  371: 
  372: /////////////////////////////////////////////////////////////////////////////
  373: 
  374: class win_scsi_device
  375: : public /*implements*/ scsi_device,
  376:   virtual public /*extends*/ win_smart_device
  377: {
  378: public:
  379:   win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
  380: 
  381:   virtual bool open();
  382: 
  383:   virtual bool scsi_pass_through(scsi_cmnd_io * iop);
  384: 
  385: private:
  386:   bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
  387: };
  388: 
  389: 
  390: /////////////////////////////////////////////////////////////////////////////
  391: 
  392: class csmi_device
  393: : virtual public /*extends*/ smart_device
  394: {
  395: public:
  396:   /// Get phy info
  397:   bool get_phy_info(CSMI_SAS_PHY_INFO & phy_info);
  398: 
  399:   /// Check physical drive existence
  400:   bool check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no);
  401: 
  402: protected:
  403:   csmi_device()
  404:     : smart_device(never_called)
  405:     { memset(&m_phy_ent, 0, sizeof(m_phy_ent)); }
  406: 
  407:   /// Select physical drive
  408:   bool select_phy(unsigned phy_no);
  409: 
  410:   /// Get info for selected physical drive
  411:   const CSMI_SAS_PHY_ENTITY & get_phy_ent() const
  412:     { return m_phy_ent; }
  413: 
  414:   /// Call platform-specific CSMI ioctl
  415:   virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
  416:     unsigned csmi_bufsiz) = 0;
  417: 
  418: private:
  419:   CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
  420: };
  421: 
  422: 
  423: class csmi_ata_device
  424: : virtual public /*extends*/ csmi_device,
  425:   virtual public /*implements*/ ata_device
  426: {
  427: public:
  428:   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
  429: 
  430: protected:
  431:   csmi_ata_device()
  432:     : smart_device(never_called) { }
  433: };
  434: 
  435: 
  436: //////////////////////////////////////////////////////////////////////
  437: 
  438: class win_csmi_device
  439: : public /*implements*/ csmi_ata_device
  440: {
  441: public:
  442:   win_csmi_device(smart_interface * intf, const char * dev_name,
  443:     const char * req_type);
  444: 
  445:   virtual ~win_csmi_device() throw();
  446: 
  447:   virtual bool open();
  448: 
  449:   virtual bool close();
  450: 
  451:   virtual bool is_open() const;
  452: 
  453:   bool open_scsi();
  454: 
  455: protected:
  456:   virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
  457:     unsigned csmi_bufsiz);
  458: 
  459: private:
  460:   HANDLE m_fh; ///< Controller device handle
  461:   unsigned m_phy_no; ///< Physical drive number
  462: };
  463: 
  464: 
  465: //////////////////////////////////////////////////////////////////////
  466: 
  467: class win_tw_cli_device
  468: : public /*implements*/ ata_device_with_command_set
  469: {
  470: public:
  471:   win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
  472: 
  473:   virtual bool is_open() const;
  474: 
  475:   virtual bool open();
  476: 
  477:   virtual bool close();
  478: 
  479: protected:
  480:   virtual int ata_command_interface(smart_command_set command, int select, char * data);
  481: 
  482: private:
  483:   bool m_ident_valid, m_smart_valid;
  484:   ata_identify_device m_ident_buf;
  485:   ata_smart_values m_smart_buf;
  486: };
  487: 
  488: 
  489: /////////////////////////////////////////////////////////////////////////////
  490: /// Areca RAID support
  491: 
  492: ///////////////////////////////////////////////////////////////////
  493: // SATA(ATA) device behind Areca RAID Controller
  494: class win_areca_ata_device
  495: : public /*implements*/ areca_ata_device,
  496:   public /*extends*/ win_smart_device
  497: {
  498: public:
  499:   win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
  500:   virtual bool open();
  501:   virtual smart_device * autodetect_open();
  502:   virtual bool arcmsr_lock();
  503:   virtual bool arcmsr_unlock();
  504:   virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
  505: 
  506: private:
  507:   HANDLE m_mutex;
  508: };
  509: 
  510: ///////////////////////////////////////////////////////////////////
  511: // SAS(SCSI) device behind Areca RAID Controller
  512: class win_areca_scsi_device
  513: : public /*implements*/ areca_scsi_device,
  514:   public /*extends*/ win_smart_device
  515: {
  516: public:
  517:   win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
  518:   virtual bool open();
  519:   virtual smart_device * autodetect_open();
  520:   virtual bool arcmsr_lock();
  521:   virtual bool arcmsr_unlock();
  522:   virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
  523: 
  524: private:
  525:   HANDLE m_mutex;
  526: };
  527: 
  528: 
  529: //////////////////////////////////////////////////////////////////////
  530: // Platform specific interface
  531: 
  532: class win_smart_interface
  533: : public /*implements part of*/ smart_interface
  534: {
  535: public:
  536:   virtual std::string get_os_version_str();
  537: 
  538:   virtual std::string get_app_examples(const char * appname);
  539: 
  540: #ifndef __CYGWIN__
  541:   virtual int64_t get_timer_usec();
  542: #endif
  543: 
  544:   virtual bool disable_system_auto_standby(bool disable);
  545: 
  546:   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
  547:     const char * pattern = 0);
  548: 
  549: protected:
  550:   virtual ata_device * get_ata_device(const char * name, const char * type);
  551: 
  552:   virtual scsi_device * get_scsi_device(const char * name, const char * type);
  553: 
  554:   virtual smart_device * autodetect_smart_device(const char * name);
  555: 
  556:   virtual smart_device * get_custom_smart_device(const char * name, const char * type);
  557: 
  558:   virtual std::string get_valid_custom_dev_types_str();
  559: };
  560: 
  561: 
  562: //////////////////////////////////////////////////////////////////////
  563: 
  564: #ifndef _WIN64
  565: // Running on 64-bit Windows as 32-bit app ?
  566: static bool is_wow64()
  567: {
  568:   BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
  569:     (BOOL (WINAPI *)(HANDLE, PBOOL))
  570:     GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
  571:   if (!IsWow64Process_p)
  572:     return false;
  573:   BOOL w64 = FALSE;
  574:   if (!IsWow64Process_p(GetCurrentProcess(), &w64))
  575:     return false;
  576:   return !!w64;
  577: }
  578: #endif // _WIN64
  579: 
  580: // Return info string about build host and OS version
  581: std::string win_smart_interface::get_os_version_str()
  582: {
  583:   char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
  584:     = SMARTMONTOOLS_BUILD_HOST;
  585:   if (vstr[1] < '6')
  586:     vstr[1] = '6';
  587:   char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
  588:   const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
  589:   assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
  590: 
  591:   OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
  592:   vi.dwOSVersionInfoSize = sizeof(vi);
  593:   if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
  594:     memset(&vi, 0, sizeof(vi));
  595:     vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
  596:     if (!GetVersionExA((OSVERSIONINFOA *)&vi))
  597:       return vstr;
  598:   }
  599: 
  600:   if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
  601:     return vstr;
  602: 
  603:   const char * w;
  604:   switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
  605:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
  606:       w = (vi.szCSDVersion[1] == 'B' ||
  607:            vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
  608:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
  609:       w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
  610:     case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
  611:   //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
  612:     case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
  613:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
  614:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
  615:       w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
  616:                                                    :   "xp-mc"); break;
  617:     case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
  618:       w = (!GetSystemMetrics(89/*SM_SERVERR2*/)    ?   "2003"
  619:                                                    :   "2003r2"); break;
  620:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0:
  621:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "vista"
  622:                                                    :   "2008" );  break;
  623:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 1:
  624:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win7"
  625:                                                    :   "2008r2"); break;
  626:     case VER_PLATFORM_WIN32_NT     <<16|0x0600| 2:
  627:       w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win8"
  628:                                                    :   "2012"); break;
  629:     default: w = 0; break;
  630:   }
  631: 
  632:   const char * w64 = "";
  633: #ifndef _WIN64
  634:   if (is_wow64())
  635:     w64 = "(64)";
  636: #endif
  637: 
  638:   if (!w)
  639:     snprintf(vptr, vlen, "-%s%lu.%lu%s",
  640:       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
  641:       vi.dwMajorVersion, vi.dwMinorVersion, w64);
  642:   else if (vi.wServicePackMinor)
  643:     snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
  644:   else if (vi.wServicePackMajor)
  645:     snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
  646:   else
  647:     snprintf(vptr, vlen, "-%s%s", w, w64);
  648:   return vstr;
  649: }
  650: 
  651: #ifndef __CYGWIN__
  652: // MSVCRT only provides ftime() which uses GetSystemTime()
  653: // This provides only ~15ms resolution by default.
  654: // Use QueryPerformanceCounter instead (~300ns).
  655: // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
  656: int64_t win_smart_interface::get_timer_usec()
  657: {
  658:   static int64_t freq = 0;
  659: 
  660:   LARGE_INTEGER t;
  661:   if (freq == 0)
  662:     freq = (QueryPerformanceFrequency(&t) ? t.QuadPart : -1);
  663:   if (freq <= 0)
  664:     return smart_interface::get_timer_usec();
  665: 
  666:   if (!QueryPerformanceCounter(&t))
  667:     return -1;
  668:   if (!(0 <= t.QuadPart && t.QuadPart <= (int64_t)(~(uint64_t)0 >> 1)/1000000))
  669:     return -1;
  670: 
  671:   return (t.QuadPart * 1000000LL) / freq;
  672: }
  673: #endif // __CYGWIN__
  674: 
  675: 
  676: // Return value for device detection functions
  677: enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
  678: 
  679: static win_dev_type get_phy_drive_type(int drive);
  680: static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex);
  681: static win_dev_type get_log_drive_type(int drive);
  682: static bool get_usb_id(int drive, unsigned short & vendor_id,
  683:                        unsigned short & product_id);
  684: 
  685: static const char * ata_get_def_options(void);
  686: 
  687: 
  688: static int is_permissive()
  689: {
  690:   if (!failuretest_permissive) {
  691:     pout("To continue, add one or more '-T permissive' options.\n");
  692:     return 0;
  693:   }
  694:   failuretest_permissive--;
  695:   return 1;
  696: }
  697: 
  698: // return number for drive letter, -1 on error
  699: // "[A-Za-z]:([/\\][.]?)?" => 0-25
  700: // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
  701: static int drive_letter(const char * s)
  702: {
  703:   return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
  704:           && s[1] == ':'
  705:           && (!s[2] || (   strchr("/\\\"", s[2])
  706:                         && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
  707:           (s[0] & 0x1f) - 1 : -1);
  708: }
  709: 
  710: // Skip trailing "/dev/", do not allow "/dev/X:"
  711: static const char * skipdev(const char * s)
  712: {
  713:   return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
  714: }
  715: 
  716: ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
  717: {
  718:   const char * testname = skipdev(name);
  719:   if (!strncmp(testname, "csmi", 4))
  720:     return new win_csmi_device(this, name, type);
  721:   if (!strncmp(testname, "tw_cli", 6))
  722:     return new win_tw_cli_device(this, name, type);
  723:   return new win_ata_device(this, name, type);
  724: }
  725: 
  726: scsi_device * win_smart_interface::get_scsi_device(const char * name, const char * type)
  727: {
  728:   return new win_scsi_device(this, name, type);
  729: }
  730: 
  731: static int sdxy_to_phydrive(const char (& xy)[2+1])
  732: {
  733:   int phydrive = xy[0] - 'a';
  734:   if (xy[1])
  735:     phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
  736:   return phydrive;
  737: }
  738: 
  739: static win_dev_type get_dev_type(const char * name, int & phydrive)
  740: {
  741:   phydrive = -1;
  742:   name = skipdev(name);
  743:   if (!strncmp(name, "st", 2))
  744:     return DEV_SCSI;
  745:   if (!strncmp(name, "nst", 3))
  746:     return DEV_SCSI;
  747:   if (!strncmp(name, "tape", 4))
  748:     return DEV_SCSI;
  749: 
  750:   int logdrive = drive_letter(name);
  751:   if (logdrive >= 0) {
  752:     win_dev_type type = get_log_drive_type(logdrive);
  753:     return (type != DEV_UNKNOWN ? type : DEV_SCSI);
  754:   }
  755: 
  756:   char drive[2+1] = "";
  757:   if (sscanf(name, "sd%2[a-z]", drive) == 1) {
  758:     phydrive = sdxy_to_phydrive(drive);
  759:     return get_phy_drive_type(phydrive);
  760:   }
  761: 
  762:   phydrive = -1;
  763:   if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
  764:     return get_phy_drive_type(phydrive);
  765:   return DEV_UNKNOWN;
  766: }
  767: 
  768: smart_device * win_smart_interface::get_custom_smart_device(const char * name, const char * type)
  769: {
  770:   // Areca?
  771:   int disknum = -1, n1 = -1, n2 = -1;
  772:   int encnum = 1;
  773:   char devpath[32];
  774: 
  775:   if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
  776:     if (!(1 <= disknum && disknum <= 128)) {
  777:       set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
  778:       return 0;
  779:     }
  780:     if (!(1 <= encnum && encnum <= 8)) {
  781:       set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
  782:       return 0;
  783:     }
  784: 
  785:     name = skipdev(name);
  786: #define ARECA_MAX_CTLR_NUM  16
  787:     n1 = -1;
  788:     int ctlrindex = 0;
  789:     if (sscanf(name, "arcmsr%d%n", &ctlrindex, &n1) >= 1 && n1 == (int)strlen(name)) {
  790:       /*
  791:        1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
  792:        2. map arcmsrX into "\\\\.\\scsiX"
  793:       */
  794:      for (int idx = 0; idx < ARECA_MAX_CTLR_NUM; idx++) {
  795:         memset(devpath, 0, sizeof(devpath));
  796:         snprintf(devpath, sizeof(devpath), "\\\\.\\scsi%d:", idx);
  797:         win_areca_ata_device *arcdev = new win_areca_ata_device(this, devpath, disknum, encnum);
  798:         if(arcdev->arcmsr_probe()) {
  799:           if(ctlrindex-- == 0) {
  800:             return arcdev;
  801:           }
  802:         }
  803:         delete arcdev;
  804:       }
  805:       set_err(ENOENT, "No Areca controller found");
  806:     }
  807:     else
  808:       set_err(EINVAL, "Option -d areca,N/E requires device name /dev/arcmsrX");
  809:   }
  810: 
  811:   return 0;
  812: }
  813: 
  814: std::string win_smart_interface::get_valid_custom_dev_types_str()
  815: {
  816:   return "areca,N[/E]";
  817: }
  818: 
  819: 
  820: smart_device * win_smart_interface::autodetect_smart_device(const char * name)
  821: {
  822:   const char * testname = skipdev(name);
  823:   if (str_starts_with(testname, "hd"))
  824:     return new win_ata_device(this, name, "");
  825: 
  826:   if (str_starts_with(testname, "tw_cli"))
  827:     return new win_tw_cli_device(this, name, "");
  828: 
  829:   if (str_starts_with(testname, "csmi"))
  830:     return new win_csmi_device(this, name, "");
  831: 
  832:   int phydrive = -1;
  833:   win_dev_type type = get_dev_type(name, phydrive);
  834: 
  835:   if (type == DEV_ATA)
  836:     return new win_ata_device(this, name, "");
  837:   if (type == DEV_SCSI)
  838:     return new win_scsi_device(this, name, "");
  839: 
  840:   if (type == DEV_USB) {
  841:     // Get USB bridge ID
  842:     unsigned short vendor_id = 0, product_id = 0;
  843:     if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
  844:       set_err(EINVAL, "Unable to read USB device ID");
  845:       return 0;
  846:     }
  847:     // Get type name for this ID
  848:     const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
  849:     if (!usbtype)
  850:       return 0;
  851:     // Return SAT/USB device for this type
  852:     return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
  853:   }
  854: 
  855:   return 0;
  856: }
  857: 
  858: 
  859: // Scan for devices
  860: 
  861: bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
  862:   const char * type, const char * pattern /* = 0*/)
  863: {
  864:   if (pattern) {
  865:     set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
  866:     return false;
  867:   }
  868: 
  869:   // Check for "[*,]pd" type
  870:   bool pd = false;
  871:   char type2[16+1] = "";
  872:   if (type) {
  873:     int nc = -1;
  874:     if (!strcmp(type, "pd")) {
  875:       pd = true;
  876:       type = 0;
  877:     }
  878:     else if (sscanf(type, "%16[^,],pd%n", type2, &nc) == 1 &&
  879:              nc == (int)strlen(type)) {
  880:       pd = true;
  881:       type = type2;
  882:     }
  883:   }
  884: 
  885:   // Set valid types
  886:   bool ata, scsi, usb, csmi;
  887:   if (!type) {
  888:     ata = scsi = usb = csmi = true;
  889:   }
  890:   else {
  891:     ata = scsi = usb = csmi = false;
  892:     if (!strcmp(type, "ata"))
  893:       ata = true;
  894:     else if (!strcmp(type, "scsi"))
  895:       scsi = true;
  896:     else if (!strcmp(type, "usb"))
  897:       usb = true;
  898:     else if (!strcmp(type, "csmi"))
  899:       csmi = true;
  900:     else {
  901:       set_err(EINVAL, "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], usb[,pd], csmi, pd", type);
  902:       return false;
  903:     }
  904:   }
  905: 
  906:   char name[20];
  907: 
  908:   if (ata || scsi || usb) {
  909:     // Scan up to 128 drives and 2 3ware controllers
  910:     const int max_raid = 2;
  911:     bool raid_seen[max_raid] = {false, false};
  912: 
  913:     for (int i = 0; i < 128; i++) {
  914:       if (pd)
  915:         snprintf(name, sizeof(name), "/dev/pd%d", i);
  916:       else if (i + 'a' <= 'z')
  917:         snprintf(name, sizeof(name), "/dev/sd%c", i + 'a');
  918:       else
  919:         snprintf(name, sizeof(name), "/dev/sd%c%c",
  920:                  i / ('z'-'a'+1) - 1 + 'a',
  921:                  i % ('z'-'a'+1)     + 'a');
  922: 
  923:       GETVERSIONINPARAMS_EX vers_ex;
  924: 
  925:       switch (get_phy_drive_type(i, (ata ? &vers_ex : 0))) {
  926:         case DEV_ATA:
  927:           // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
  928:           if (!ata)
  929:             continue;
  930: 
  931:           // Interpret RAID drive map if present
  932:           if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
  933:             // Skip if too many controllers or logical drive from this controller already seen
  934:             if (!(vers_ex.wControllerId < max_raid && !raid_seen[vers_ex.wControllerId]))
  935:               continue;
  936:             raid_seen[vers_ex.wControllerId] = true;
  937:             // Add physical drives
  938:             int len = strlen(name);
  939:             for (int pi = 0; pi < 32; pi++) {
  940:               if (vers_ex.dwDeviceMapEx & (1L << pi)) {
  941:                 snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
  942:                 devlist.push_back( new win_ata_device(this, name, "ata") );
  943:               }
  944:             }
  945:           }
  946:           else {
  947:             devlist.push_back( new win_ata_device(this, name, "ata") );
  948:           }
  949:           break;
  950: 
  951:         case DEV_SCSI:
  952:           // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
  953:           if (!scsi)
  954:             continue;
  955:           devlist.push_back( new win_scsi_device(this, name, "scsi") );
  956:           break;
  957: 
  958:         case DEV_USB:
  959:           // STORAGE_QUERY_PROPERTY returned USB
  960:           if (!usb)
  961:             continue;
  962:           {
  963:             // TODO: Use common function for this and autodetect_smart_device()
  964:             // Get USB bridge ID
  965:             unsigned short vendor_id = 0, product_id = 0;
  966:             if (!get_usb_id(i, vendor_id, product_id))
  967:               continue;
  968:             // Get type name for this ID
  969:             const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
  970:             if (!usbtype)
  971:               continue;
  972:             // Return SAT/USB device for this type
  973:             ata_device * dev = get_sat_device(usbtype, new win_scsi_device(this, name, ""));
  974:             if (!dev)
  975:               continue;
  976:             devlist.push_back(dev);
  977:           }
  978:           break;
  979: 
  980:         default:
  981:           // Unknown type
  982:           break;
  983:       }
  984:     }
  985:   }
  986: 
  987:   if (csmi) {
  988:     // Scan CSMI devices
  989:     for (int i = 0; i <= 9; i++) {
  990:       snprintf(name, sizeof(name)-1, "/dev/csmi%d,0", i);
  991:       win_csmi_device test_dev(this, name, "");
  992:       if (!test_dev.open_scsi())
  993:         continue;
  994:       CSMI_SAS_PHY_INFO phy_info;
  995:       if (!test_dev.get_phy_info(phy_info))
  996:         continue;
  997: 
  998:       for (int pi = 0; pi < phy_info.bNumberOfPhys; pi++) {
  999:         if (!test_dev.check_phy(phy_info, pi))
 1000:           continue;
 1001:         snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
 1002:         devlist.push_back( new win_csmi_device(this, name, "ata") );
 1003:       }
 1004:     }
 1005:   }
 1006:   return true;
 1007: }
 1008: 
 1009: 
 1010: // get examples for smartctl
 1011: std::string win_smart_interface::get_app_examples(const char * appname)
 1012: {
 1013:   if (strcmp(appname, "smartctl"))
 1014:     return "";
 1015:   return "=================================================== SMARTCTL EXAMPLES =====\n\n"
 1016:          "  smartctl -a /dev/sda                       (Prints all SMART information)\n\n"
 1017:          "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
 1018:          "                                              (Enables SMART on first disk)\n\n"
 1019:          "  smartctl -t long /dev/sda              (Executes extended disk self-test)\n\n"
 1020:          "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
 1021:          "                                      (Prints Self-Test & Attribute errors)\n"
 1022:          "  smartctl -a /dev/sda\n"
 1023:          "             (Prints all information for disk on PhysicalDrive 0)\n"
 1024:          "  smartctl -a /dev/pd3\n"
 1025:          "             (Prints all information for disk on PhysicalDrive 3)\n"
 1026:          "  smartctl -a /dev/tape1\n"
 1027:          "             (Prints all information for SCSI tape on Tape 1)\n"
 1028:          "  smartctl -A /dev/hdb,3\n"
 1029:          "                (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
 1030:          "  smartctl -A /dev/tw_cli/c0/p1\n"
 1031:          "            (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
 1032:          "  smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
 1033:          "           (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
 1034:          "            on 1st Areca RAID controller)\n"
 1035:          "\n"
 1036:          "  ATA SMART access methods and ordering may be specified by modifiers\n"
 1037:          "  following the device name: /dev/hdX:[saicm], where\n"
 1038:          "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
 1039:          "  'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
 1040:          "  'm': IOCTL_SCSI_MINIPORT_*.\n"
 1041:       + strprintf(
 1042:          "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
 1043:         );
 1044: }
 1045: 
 1046: 
 1047: bool win_smart_interface::disable_system_auto_standby(bool disable)
 1048: {
 1049:   if (disable) {
 1050:     SYSTEM_POWER_STATUS ps;
 1051:     if (!GetSystemPowerStatus(&ps))
 1052:       return set_err(ENOSYS, "Unknown power status");
 1053:     if (ps.ACLineStatus != 1) {
 1054:       SetThreadExecutionState(ES_CONTINUOUS);
 1055:       if (ps.ACLineStatus == 0)
 1056:         set_err(EIO, "AC offline");
 1057:       else
 1058:         set_err(EIO, "Unknown AC line status");
 1059:       return false;
 1060:     }
 1061:   }
 1062: 
 1063:   if (!SetThreadExecutionState(ES_CONTINUOUS | (disable ? ES_SYSTEM_REQUIRED : 0)))
 1064:     return set_err(ENOSYS);
 1065:   return true;
 1066: }
 1067: 
 1068: 
 1069: /////////////////////////////////////////////////////////////////////////////
 1070: // ATA Interface
 1071: /////////////////////////////////////////////////////////////////////////////
 1072: 
 1073: #define SMART_CYL_LOW  0x4F
 1074: #define SMART_CYL_HI   0xC2
 1075: 
 1076: static void print_ide_regs(const IDEREGS * r, int out)
 1077: {
 1078:   pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
 1079:     (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
 1080:     r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
 1081: }
 1082: 
 1083: static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
 1084: {
 1085:   pout("    Input : "); print_ide_regs(ri, 0);
 1086:   if (ro) {
 1087:     pout("    Output: "); print_ide_regs(ro, 1);
 1088:   }
 1089: }
 1090: 
 1091: /////////////////////////////////////////////////////////////////////////////
 1092: 
 1093: // call SMART_GET_VERSION, return device map or -1 on error
 1094: 
 1095: static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 1096: {
 1097:   GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
 1098:   const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
 1099:   DWORD num_out;
 1100: 
 1101:   if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
 1102:     NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
 1103:     if (ata_debugmode)
 1104:       pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
 1105:     errno = ENOSYS;
 1106:     return -1;
 1107:   }
 1108:   assert(num_out == sizeof(GETVERSIONINPARAMS));
 1109: 
 1110:   if (ata_debugmode > 1) {
 1111:     pout("  SMART_GET_VERSION suceeded, bytes returned: %lu\n"
 1112:          "    Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
 1113:       num_out, vers.bVersion, vers.bRevision,
 1114:       vers.fCapabilities, vers.bIDEDeviceMap);
 1115:     if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
 1116:       pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
 1117:       vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
 1118:   }
 1119: 
 1120:   if (ata_version_ex)
 1121:     *ata_version_ex = vers_ex;
 1122: 
 1123:   // TODO: Check vers.fCapabilities here?
 1124:   return vers.bIDEDeviceMap;
 1125: }
 1126: 
 1127: 
 1128: // call SMART_* ioctl
 1129: 
 1130: static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
 1131: {
 1132:   SENDCMDINPARAMS inpar;
 1133:   SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
 1134: 
 1135:   unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
 1136:   const SENDCMDOUTPARAMS * outpar;
 1137:   DWORD code, num_out;
 1138:   unsigned int size_out;
 1139:   const char * name;
 1140: 
 1141:   memset(&inpar, 0, sizeof(inpar));
 1142:   inpar.irDriveRegs = *regs;
 1143: 
 1144:   // Older drivers may require bits 5 and 7 set
 1145:   // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
 1146:   inpar.irDriveRegs.bDriveHeadReg |= 0xa0;
 1147: 
 1148:   // Drive number 0-3 was required on Win9x/ME only
 1149:   //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
 1150:   //inpar.bDriveNumber = drive;
 1151: 
 1152:   if (port >= 0) {
 1153:     // Set RAID port
 1154:     inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
 1155:     inpar_ex.bPortNumber = port;
 1156:   }
 1157: 
 1158:   if (datasize == 512) {
 1159:     code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
 1160:     inpar.cBufferSize = size_out = 512;
 1161:   }
 1162:   else if (datasize == 0) {
 1163:     code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
 1164:     if (regs->bFeaturesReg == ATA_SMART_STATUS)
 1165:       size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
 1166:       // Note: cBufferSize must be 0 on Win9x
 1167:     else
 1168:       size_out = 0;
 1169:   }
 1170:   else {
 1171:     errno = EINVAL;
 1172:     return -1;
 1173:   }
 1174: 
 1175:   memset(&outbuf, 0, sizeof(outbuf));
 1176: 
 1177:   if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
 1178:     outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
 1179:     // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
 1180:     long err = GetLastError();
 1181:     if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
 1182:       pout("  %s failed, Error=%ld\n", name, err);
 1183:       print_ide_regs_io(regs, NULL);
 1184:     }
 1185:     errno = (   err == ERROR_INVALID_FUNCTION/*9x*/
 1186:              || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
 1187:              || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1188:     return -1;
 1189:   }
 1190:   // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
 1191: 
 1192:   outpar = (const SENDCMDOUTPARAMS *)outbuf;
 1193: 
 1194:   if (outpar->DriverStatus.bDriverError) {
 1195:     if (ata_debugmode) {
 1196:       pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
 1197:         outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
 1198:       print_ide_regs_io(regs, NULL);
 1199:     }
 1200:     errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
 1201:     return -1;
 1202:   }
 1203: 
 1204:   if (ata_debugmode > 1) {
 1205:     pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
 1206:       num_out, outpar->cBufferSize);
 1207:     print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
 1208:       (const IDEREGS *)(outpar->bBuffer) : NULL));
 1209:   }
 1210: 
 1211:   if (datasize)
 1212:     memcpy(data, outpar->bBuffer, 512);
 1213:   else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
 1214:     if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
 1215:       memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
 1216:     else {  // Workaround for driver not returning regs
 1217:       if (ata_debugmode)
 1218:         pout("  WARNING: driver does not return ATA registers in output buffer!\n");
 1219:       *regs = inpar.irDriveRegs;
 1220:     }
 1221:   }
 1222: 
 1223:   return 0;
 1224: }
 1225: 
 1226: 
 1227: /////////////////////////////////////////////////////////////////////////////
 1228: // IDE PASS THROUGH (2000, XP, undocumented)
 1229: //
 1230: // Based on WinATA.cpp, 2002 c't/Matthias Withopf
 1231: // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
 1232: 
 1233: static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
 1234: {
 1235:   if (datasize > 512) {
 1236:     errno = EINVAL;
 1237:     return -1;
 1238:   }
 1239:   unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
 1240:   ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
 1241:   DWORD num_out;
 1242:   const unsigned char magic = 0xcf;
 1243: 
 1244:   if (!buf) {
 1245:     errno = ENOMEM;
 1246:     return -1;
 1247:   }
 1248: 
 1249:   buf->IdeReg = *regs;
 1250:   buf->DataBufferSize = datasize;
 1251:   if (datasize)
 1252:     buf->DataBuffer[0] = magic;
 1253: 
 1254:   if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
 1255:     buf, size, buf, size, &num_out, NULL)) {
 1256:     long err = GetLastError();
 1257:     if (ata_debugmode) {
 1258:       pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
 1259:       print_ide_regs_io(regs, NULL);
 1260:     }
 1261:     VirtualFree(buf, 0, MEM_RELEASE);
 1262:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1263:     return -1;
 1264:   }
 1265: 
 1266:   // Check ATA status
 1267:   if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
 1268:     if (ata_debugmode) {
 1269:       pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
 1270:       print_ide_regs_io(regs, &buf->IdeReg);
 1271:     }
 1272:     VirtualFree(buf, 0, MEM_RELEASE);
 1273:     errno = EIO;
 1274:     return -1;
 1275:   }
 1276: 
 1277:   // Check and copy data
 1278:   if (datasize) {
 1279:     if (   num_out != size
 1280:         || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
 1281:       if (ata_debugmode) {
 1282:         pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
 1283:           num_out, buf->DataBufferSize);
 1284:         print_ide_regs_io(regs, &buf->IdeReg);
 1285:       }
 1286:       VirtualFree(buf, 0, MEM_RELEASE);
 1287:       errno = EIO;
 1288:       return -1;
 1289:     }
 1290:     memcpy(data, buf->DataBuffer, datasize);
 1291:   }
 1292: 
 1293:   if (ata_debugmode > 1) {
 1294:     pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
 1295:       num_out, buf->DataBufferSize);
 1296:     print_ide_regs_io(regs, &buf->IdeReg);
 1297:   }
 1298:   *regs = buf->IdeReg;
 1299: 
 1300:   // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
 1301:   VirtualFree(buf, 0, MEM_RELEASE);
 1302:   return 0;
 1303: }
 1304: 
 1305: 
 1306: /////////////////////////////////////////////////////////////////////////////
 1307: // ATA PASS THROUGH (Win2003, XP SP2)
 1308: 
 1309: // Warning:
 1310: // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
 1311: // transfer per command. Therefore, multi-sector transfers are only supported
 1312: // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
 1313: // or READ/WRITE LOG EXT work only with single sector transfers.
 1314: // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
 1315: // See:
 1316: // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
 1317: 
 1318: static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
 1319: {
 1320:   const int max_sectors = 32; // TODO: Allocate dynamic buffer
 1321: 
 1322:   typedef struct {
 1323:     ATA_PASS_THROUGH_EX apt;
 1324:     ULONG Filler;
 1325:     UCHAR ucDataBuf[max_sectors * 512];
 1326:   } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
 1327: 
 1328:   const unsigned char magic = 0xcf;
 1329: 
 1330:   ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
 1331:   ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
 1332:   //ab.apt.PathId = 0;
 1333:   //ab.apt.TargetId = 0;
 1334:   //ab.apt.Lun = 0;
 1335:   ab.apt.TimeOutValue = 10;
 1336:   unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
 1337:   ab.apt.DataBufferOffset = size;
 1338: 
 1339:   if (datasize > 0) {
 1340:     if (datasize > (int)sizeof(ab.ucDataBuf)) {
 1341:       errno = EINVAL;
 1342:       return -1;
 1343:     }
 1344:     ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
 1345:     ab.apt.DataTransferLength = datasize;
 1346:     size += datasize;
 1347:     ab.ucDataBuf[0] = magic;
 1348:   }
 1349:   else if (datasize < 0) {
 1350:     if (-datasize > (int)sizeof(ab.ucDataBuf)) {
 1351:       errno = EINVAL;
 1352:       return -1;
 1353:     }
 1354:     ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
 1355:     ab.apt.DataTransferLength = -datasize;
 1356:     size += -datasize;
 1357:     memcpy(ab.ucDataBuf, data, -datasize);
 1358:   }
 1359:   else {
 1360:     assert(ab.apt.AtaFlags == 0);
 1361:     assert(ab.apt.DataTransferLength == 0);
 1362:   }
 1363: 
 1364:   assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
 1365:   IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
 1366:   IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
 1367:   *ctfregs = *regs;
 1368: 
 1369:   if (prev_regs) {
 1370:     *ptfregs = *prev_regs;
 1371:     ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
 1372:   }
 1373: 
 1374:   DWORD num_out;
 1375:   if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
 1376:     &ab, size, &ab, size, &num_out, NULL)) {
 1377:     long err = GetLastError();
 1378:     if (ata_debugmode) {
 1379:       pout("  IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
 1380:       print_ide_regs_io(regs, NULL);
 1381:     }
 1382:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1383:     return -1;
 1384:   }
 1385: 
 1386:   // Check ATA status
 1387:   if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
 1388:     if (ata_debugmode) {
 1389:       pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
 1390:       print_ide_regs_io(regs, ctfregs);
 1391:     }
 1392:     errno = EIO;
 1393:     return -1;
 1394:   }
 1395: 
 1396:   // Check and copy data
 1397:   if (datasize > 0) {
 1398:     if (   num_out != size
 1399:         || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
 1400:       if (ata_debugmode) {
 1401:         pout("  IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);
 1402:         print_ide_regs_io(regs, ctfregs);
 1403:       }
 1404:       errno = EIO;
 1405:       return -1;
 1406:     }
 1407:     memcpy(data, ab.ucDataBuf, datasize);
 1408:   }
 1409: 
 1410:   if (ata_debugmode > 1) {
 1411:     pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
 1412:     print_ide_regs_io(regs, ctfregs);
 1413:   }
 1414:   *regs = *ctfregs;
 1415:   if (prev_regs)
 1416:     *prev_regs = *ptfregs;
 1417: 
 1418:   return 0;
 1419: }
 1420: 
 1421: 
 1422: /////////////////////////////////////////////////////////////////////////////
 1423: // SMART IOCTL via SCSI MINIPORT ioctl
 1424: 
 1425: // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
 1426: // miniport driver (via SCSI port driver scsiport.sys).
 1427: // It can be used to skip the missing or broken handling of some SMART
 1428: // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
 1429: 
 1430: static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
 1431: {
 1432:   // Select code
 1433:   DWORD code = 0; const char * name = 0;
 1434:   if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
 1435:     code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
 1436:   }
 1437:   else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
 1438:     case ATA_SMART_READ_VALUES:
 1439:       code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
 1440:     case ATA_SMART_READ_THRESHOLDS:
 1441:       code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
 1442:     case ATA_SMART_ENABLE:
 1443:       code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
 1444:     case ATA_SMART_DISABLE:
 1445:       code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
 1446:     case ATA_SMART_STATUS:
 1447:       code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
 1448:     case ATA_SMART_AUTOSAVE:
 1449:       code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
 1450:   //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
 1451:   //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
 1452:     case ATA_SMART_IMMEDIATE_OFFLINE:
 1453:       code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
 1454:     case ATA_SMART_AUTO_OFFLINE:
 1455:       code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
 1456:     case ATA_SMART_READ_LOG_SECTOR:
 1457:       code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
 1458:     case ATA_SMART_WRITE_LOG_SECTOR:
 1459:       code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
 1460:   }
 1461:   if (!code) {
 1462:     errno = ENOSYS;
 1463:     return -1;
 1464:   }
 1465: 
 1466:   // Set SRB
 1467:   struct {
 1468:     SRB_IO_CONTROL srbc;
 1469:     union {
 1470:       SENDCMDINPARAMS in;
 1471:       SENDCMDOUTPARAMS out;
 1472:     } params;
 1473:     char space[512-1];
 1474:   } sb;
 1475:   ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
 1476:   memset(&sb, 0, sizeof(sb));
 1477: 
 1478:   unsigned size;
 1479:   if (datasize > 0) {
 1480:     if (datasize > (int)sizeof(sb.space)+1) {
 1481:       errno = EINVAL;
 1482:       return -1;
 1483:     }
 1484:     size = datasize;
 1485:   }
 1486:   else if (datasize < 0) {
 1487:     if (-datasize > (int)sizeof(sb.space)+1) {
 1488:       errno = EINVAL;
 1489:       return -1;
 1490:     }
 1491:     size = -datasize;
 1492:     memcpy(sb.params.in.bBuffer, data, size);
 1493:   }
 1494:   else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
 1495:     size = sizeof(IDEREGS);
 1496:   else
 1497:     size = 0;
 1498:   sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1499:   memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
 1500:   sb.srbc.Timeout = 60; // seconds
 1501:   sb.srbc.ControlCode = code;
 1502:   //sb.srbc.ReturnCode = 0;
 1503:   sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
 1504:   sb.params.in.irDriveRegs = *regs;
 1505:   sb.params.in.cBufferSize = size;
 1506: 
 1507:   // Call miniport ioctl
 1508:   size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
 1509:   DWORD num_out;
 1510:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1511:     &sb, size, &sb, size, &num_out, NULL)) {
 1512:     long err = GetLastError();
 1513:     if (ata_debugmode) {
 1514:       pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
 1515:       print_ide_regs_io(regs, NULL);
 1516:     }
 1517:     errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
 1518:     return -1;
 1519:   }
 1520: 
 1521:   // Check result
 1522:   if (sb.srbc.ReturnCode) {
 1523:     if (ata_debugmode) {
 1524:       pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
 1525:       print_ide_regs_io(regs, NULL);
 1526:     }
 1527:     errno = EIO;
 1528:     return -1;
 1529:   }
 1530: 
 1531:   if (sb.params.out.DriverStatus.bDriverError) {
 1532:     if (ata_debugmode) {
 1533:       pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
 1534:         sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
 1535:       print_ide_regs_io(regs, NULL);
 1536:     }
 1537:     errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
 1538:     return -1;
 1539:   }
 1540: 
 1541:   if (ata_debugmode > 1) {
 1542:     pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
 1543:       num_out, sb.params.out.cBufferSize);
 1544:     print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
 1545:                              (const IDEREGS *)(sb.params.out.bBuffer) : 0));
 1546:   }
 1547: 
 1548:   if (datasize > 0)
 1549:     memcpy(data, sb.params.out.bBuffer, datasize);
 1550:   else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
 1551:     memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
 1552: 
 1553:   return 0;
 1554: }
 1555: 
 1556: 
 1557: /////////////////////////////////////////////////////////////////////////////
 1558: 
 1559: // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
 1560: 
 1561: static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
 1562: {
 1563:   struct {
 1564:     SRB_IO_CONTROL srbc;
 1565:     IDEREGS regs;
 1566:     UCHAR buffer[512];
 1567:   } sb;
 1568:   ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
 1569: 
 1570:   if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
 1571:     errno = EINVAL;
 1572:     return -1;
 1573:   }
 1574:   memset(&sb, 0, sizeof(sb));
 1575:   strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
 1576:   sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1577:   sb.srbc.Timeout = 60; // seconds
 1578:   sb.srbc.ControlCode = 0xA0000000;
 1579:   sb.srbc.ReturnCode = 0;
 1580:   sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
 1581:   sb.regs = *regs;
 1582:   sb.regs.bReserved = port;
 1583: 
 1584:   DWORD num_out;
 1585:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1586:     &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
 1587:     long err = GetLastError();
 1588:     if (ata_debugmode) {
 1589:       pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
 1590:       print_ide_regs_io(regs, NULL);
 1591:     }
 1592:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 1593:     return -1;
 1594:   }
 1595: 
 1596:   if (sb.srbc.ReturnCode) {
 1597:     if (ata_debugmode) {
 1598:       pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
 1599:       print_ide_regs_io(regs, NULL);
 1600:     }
 1601:     errno = EIO;
 1602:     return -1;
 1603:   }
 1604: 
 1605:   // Copy data
 1606:   if (datasize > 0)
 1607:     memcpy(data, sb.buffer, datasize);
 1608: 
 1609:   if (ata_debugmode > 1) {
 1610:     pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
 1611:     print_ide_regs_io(regs, &sb.regs);
 1612:   }
 1613:   *regs = sb.regs;
 1614: 
 1615:   return 0;
 1616: }
 1617: 
 1618: 
 1619: /////////////////////////////////////////////////////////////////////////////
 1620: 
 1621: // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
 1622: // 3DM/CLI "Rescan Controller" function does not to always update it.
 1623: 
 1624: static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 1625: {
 1626:   SRB_IO_CONTROL srbc;
 1627:   memset(&srbc, 0, sizeof(srbc));
 1628:   strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
 1629:   srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
 1630:   srbc.Timeout = 60; // seconds
 1631:   srbc.ControlCode = 0xCC010014;
 1632:   srbc.ReturnCode = 0;
 1633:   srbc.Length = 0;
 1634: 
 1635:   DWORD num_out;
 1636:   if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
 1637:     &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
 1638:     long err = GetLastError();
 1639:     if (ata_debugmode)
 1640:       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
 1641:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 1642:     return -1;
 1643:   }
 1644:   if (srbc.ReturnCode) {
 1645:     if (ata_debugmode)
 1646:       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
 1647:     errno = EIO;
 1648:     return -1;
 1649:   }
 1650:   if (ata_debugmode > 1)
 1651:     pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
 1652:   return 0;
 1653: }
 1654: 
 1655: 
 1656: 
 1657: /////////////////////////////////////////////////////////////////////////////
 1658: 
 1659: // Routines for pseudo device /dev/tw_cli/*
 1660: // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
 1661: 
 1662: 
 1663: // Get clipboard data
 1664: 
 1665: static int get_clipboard(char * data, int datasize)
 1666: {
 1667:   if (!OpenClipboard(NULL))
 1668:     return -1;
 1669:   HANDLE h = GetClipboardData(CF_TEXT);
 1670:   if (!h) {
 1671:     CloseClipboard();
 1672:     return 0;
 1673:   }
 1674:   const void * p = GlobalLock(h);
 1675:   int n = GlobalSize(h);
 1676:   if (n > datasize)
 1677:     n = datasize;
 1678:   memcpy(data, p, n);
 1679:   GlobalFree(h);
 1680:   CloseClipboard();
 1681:   return n;
 1682: }
 1683: 
 1684: 
 1685: // Run a command, write stdout to dataout
 1686: // TODO: Combine with daemon_win32.cpp:daemon_spawn()
 1687: 
 1688: static int run_cmd(const char * cmd, char * dataout, int outsize)
 1689: {
 1690:   // Create stdout pipe
 1691:   SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
 1692:   HANDLE pipe_out_w, h;
 1693:   if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
 1694:     return -1;
 1695:   HANDLE self = GetCurrentProcess();
 1696:   HANDLE pipe_out_r;
 1697:   if (!DuplicateHandle(self, h, self, &pipe_out_r,
 1698:     GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
 1699:     CloseHandle(pipe_out_w);
 1700:     return -1;
 1701:   }
 1702:   HANDLE pipe_err_w;
 1703:   if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
 1704:     0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
 1705:     CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
 1706:     return -1;
 1707:   }
 1708: 
 1709:   // Create process
 1710:   STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
 1711:   si.hStdInput  = INVALID_HANDLE_VALUE;
 1712:   si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
 1713:   si.dwFlags = STARTF_USESTDHANDLES;
 1714:   PROCESS_INFORMATION pi;
 1715:   if (!CreateProcess(
 1716:     NULL, const_cast<char *>(cmd),
 1717:     NULL, NULL, TRUE/*inherit*/,
 1718:     CREATE_NO_WINDOW/*do not create a new console window*/,
 1719:     NULL, NULL, &si, &pi)) {
 1720:     CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
 1721:     return -1;
 1722:   }
 1723:   CloseHandle(pi.hThread);
 1724:   CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
 1725: 
 1726:   // Copy stdout to output buffer
 1727:   int i = 0;
 1728:   while (i < outsize) {
 1729:     DWORD num_read;
 1730:     if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
 1731:       break;
 1732:     i += num_read;
 1733:   }
 1734:   CloseHandle(pipe_out_r);
 1735:   // Wait for process
 1736:   WaitForSingleObject(pi.hProcess, INFINITE);
 1737:   CloseHandle(pi.hProcess);
 1738:   return i;
 1739: }
 1740: 
 1741: 
 1742: static const char * findstr(const char * str, const char * sub)
 1743: {
 1744:   const char * s = strstr(str, sub);
 1745:   return (s ? s+strlen(sub) : "");
 1746: }
 1747: 
 1748: 
 1749: static void copy_swapped(unsigned char * dest, const char * src, int destsize)
 1750: {
 1751:   int srclen = strcspn(src, "\r\n");
 1752:   int i;
 1753:   for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
 1754:     dest[i] = src[i+1]; dest[i+1] = src[i];
 1755:   }
 1756:   if (i < destsize-1 && i < srclen)
 1757:     dest[i+1] = src[i];
 1758: }
 1759: 
 1760: 
 1761: // TODO: This is OS independent
 1762: 
 1763: win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
 1764: : smart_device(intf, dev_name, "tw_cli", req_type),
 1765:   m_ident_valid(false), m_smart_valid(false)
 1766: {
 1767:   memset(&m_ident_buf, 0, sizeof(m_ident_buf));
 1768:   memset(&m_smart_buf, 0, sizeof(m_smart_buf));
 1769: }
 1770: 
 1771: 
 1772: bool win_tw_cli_device::is_open() const
 1773: {
 1774:   return (m_ident_valid || m_smart_valid);
 1775: }
 1776: 
 1777: 
 1778: bool win_tw_cli_device::open()
 1779: {
 1780:   m_ident_valid = m_smart_valid = false;
 1781:   const char * name = skipdev(get_dev_name());
 1782:   // Read tw_cli or 3DM browser output into buffer
 1783:   char buffer[4096];
 1784:   int size = -1, n1 = -1, n2 = -1;
 1785:   if (!strcmp(name, "tw_cli/clip")) { // read clipboard
 1786:     size = get_clipboard(buffer, sizeof(buffer));
 1787:   }
 1788:   else if (!strcmp(name, "tw_cli/stdin")) {  // read stdin
 1789:     size = fread(buffer, 1, sizeof(buffer), stdin);
 1790:   }
 1791:   else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
 1792:     // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
 1793:     char cmd[100];
 1794:     snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
 1795:     if (ata_debugmode > 1)
 1796:       pout("%s: Run: \"%s\"\n", name, cmd);
 1797:     size = run_cmd(cmd, buffer, sizeof(buffer));
 1798:   }
 1799:   else {
 1800:     return set_err(EINVAL);
 1801:   }
 1802: 
 1803:   if (ata_debugmode > 1)
 1804:     pout("%s: Read %d bytes\n", name, size);
 1805:   if (size <= 0)
 1806:     return set_err(ENOENT);
 1807:   if (size >= (int)sizeof(buffer))
 1808:     return set_err(EIO);
 1809: 
 1810:   buffer[size] = 0;
 1811:   if (ata_debugmode > 1)
 1812:     pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
 1813: 
 1814:   // Fake identify sector
 1815:   ASSERT_SIZEOF(ata_identify_device, 512);
 1816:   ata_identify_device * id = &m_ident_buf;
 1817:   memset(id, 0, sizeof(*id));
 1818:   copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
 1819:   copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
 1820:   copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
 1821:   unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
 1822:   sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
 1823:   if (nblocks) {
 1824:     id->words047_079[49-47] = 0x0200; // size valid
 1825:     id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
 1826:     id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
 1827:   }
 1828:   id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
 1829:   id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
 1830: 
 1831:   // Parse smart data hex dump
 1832:   const char * s = findstr(buffer, "Drive Smart Data:");
 1833:   if (!*s)
 1834:     s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
 1835:   if (!*s) {
 1836:     s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
 1837:     if (*s) {
 1838:       const char * s1 = findstr(s, "<td class"); // html version
 1839:       if (*s1)
 1840:         s = s1;
 1841:       s += strcspn(s, "\r\n");
 1842:     }
 1843:     else
 1844:       s = buffer; // try raw hex dump without header
 1845:   }
 1846:   unsigned char * sd = (unsigned char *)&m_smart_buf;
 1847:   int i = 0;
 1848:   for (;;) {
 1849:     unsigned x = ~0; int n = -1;
 1850:     if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
 1851:       break;
 1852:     sd[i] = (unsigned char)x;
 1853:     if (!(++i < 512 && n > 0))
 1854:       break;
 1855:     s += n;
 1856:     if (*s == '<') // "<br>"
 1857:       s += strcspn(s, "\r\n");
 1858:   }
 1859:   if (i < 512) {
 1860:     if (!id->model[1]) {
 1861:       // No useful data found
 1862:       char * err = strstr(buffer, "Error:");
 1863:       if (!err)
 1864:         err = strstr(buffer, "error :");
 1865:       if (err && (err = strchr(err, ':'))) {
 1866:         // Show tw_cli error message
 1867:         err++;
 1868:         err[strcspn(err, "\r\n")] = 0;
 1869:         return set_err(EIO, "%s", err);
 1870:       }
 1871:       return set_err(EIO);
 1872:     }
 1873:     sd = 0;
 1874:   }
 1875: 
 1876:   m_ident_valid = true;
 1877:   m_smart_valid = !!sd;
 1878:   return true;
 1879: }
 1880: 
 1881: 
 1882: bool win_tw_cli_device::close()
 1883: {
 1884:   m_ident_valid = m_smart_valid = false;
 1885:   return true;
 1886: }
 1887: 
 1888: 
 1889: int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
 1890: {
 1891:   switch (command) {
 1892:     case IDENTIFY:
 1893:       if (!m_ident_valid)
 1894:         break;
 1895:       memcpy(data, &m_ident_buf, 512);
 1896:       return 0;
 1897:     case READ_VALUES:
 1898:       if (!m_smart_valid)
 1899:         break;
 1900:       memcpy(data, &m_smart_buf, 512);
 1901:       return 0;
 1902:     case ENABLE:
 1903:     case STATUS:
 1904:     case STATUS_CHECK: // Fake "good" SMART status
 1905:       return 0;
 1906:     default:
 1907:       break;
 1908:   }
 1909:   // Arrive here for all unsupported commands
 1910:   set_err(ENOSYS);
 1911:   return -1;
 1912: }
 1913: 
 1914: 
 1915: /////////////////////////////////////////////////////////////////////////////
 1916: // IOCTL_STORAGE_QUERY_PROPERTY
 1917: 
 1918: union STORAGE_DEVICE_DESCRIPTOR_DATA {
 1919:   STORAGE_DEVICE_DESCRIPTOR desc;
 1920:   char raw[256];
 1921: };
 1922: 
 1923: // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
 1924: // (This works without admin rights)
 1925: 
 1926: static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 1927: {
 1928:   STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
 1929:   memset(data, 0, sizeof(*data));
 1930: 
 1931:   DWORD num_out;
 1932:   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
 1933:     &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
 1934:     if (ata_debugmode > 1 || scsi_debugmode > 1)
 1935:       pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
 1936:     errno = ENOSYS;
 1937:     return -1;
 1938:   }
 1939: 
 1940:   if (ata_debugmode > 1 || scsi_debugmode > 1) {
 1941:     pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
 1942:          "    Vendor:   \"%s\"\n"
 1943:          "    Product:  \"%s\"\n"
 1944:          "    Revision: \"%s\"\n"
 1945:          "    Removable: %s\n"
 1946:          "    BusType:   0x%02x\n",
 1947:          (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : "(null)"),
 1948:          (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : "(null)"),
 1949:          (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : "(null)"),
 1950:          (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
 1951:     );
 1952:   }
 1953:   return 0;
 1954: }
 1955: 
 1956: 
 1957: /////////////////////////////////////////////////////////////////////////////
 1958: // IOCTL_STORAGE_PREDICT_FAILURE
 1959: 
 1960: // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
 1961: // or -1 on error, opionally return VendorSpecific data.
 1962: // (This works without admin rights)
 1963: 
 1964: static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
 1965: {
 1966:   STORAGE_PREDICT_FAILURE pred;
 1967:   memset(&pred, 0, sizeof(pred));
 1968: 
 1969:   DWORD num_out;
 1970:   if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
 1971:     0, 0, &pred, sizeof(pred), &num_out, NULL)) {
 1972:     if (ata_debugmode > 1)
 1973:       pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
 1974:     errno = ENOSYS;
 1975:     return -1;
 1976:   }
 1977: 
 1978:   if (ata_debugmode > 1) {
 1979:     pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
 1980:          "    PredictFailure: 0x%08lx\n"
 1981:          "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
 1982:          pred.PredictFailure,
 1983:          pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
 1984:          pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
 1985:     );
 1986:   }
 1987:   if (data)
 1988:     memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
 1989:   return (!pred.PredictFailure ? 0 : 1);
 1990: }
 1991: 
 1992: 
 1993: /////////////////////////////////////////////////////////////////////////////
 1994: 
 1995: // Return true if Intel ICHxR RAID volume
 1996: static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 1997: {
 1998:   if (!(data->desc.VendorIdOffset && data->desc.ProductIdOffset))
 1999:     return false;
 2000:   const char * vendor = data->raw + data->desc.VendorIdOffset;
 2001:   if (!(!strnicmp(vendor, "Intel", 5) && strspn(vendor+5, " ") == strlen(vendor+5)))
 2002:     return false;
 2003:   if (strnicmp(data->raw + data->desc.ProductIdOffset, "Raid ", 5))
 2004:     return false;
 2005:   return true;
 2006: }
 2007: 
 2008: // get DEV_* for open handle
 2009: static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
 2010: {
 2011:   // Get BusType from device descriptor
 2012:   STORAGE_DEVICE_DESCRIPTOR_DATA data;
 2013:   if (storage_query_property_ioctl(hdevice, &data))
 2014:     return DEV_UNKNOWN;
 2015: 
 2016:   // Newer BusType* values are missing in older includes
 2017:   switch ((int)data.desc.BusType) {
 2018:     case BusTypeAta:
 2019:     case 0x0b: // BusTypeSata
 2020:       if (ata_version_ex)
 2021:         memset(ata_version_ex, 0, sizeof(*ata_version_ex));
 2022:       return DEV_ATA;
 2023: 
 2024:     case BusTypeScsi:
 2025:     case BusTypeRAID:
 2026:       // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
 2027:       if (is_intel_raid_volume(&data))
 2028:         return DEV_SCSI;
 2029:       // LSI/3ware RAID volume: supports SMART_*
 2030:       if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
 2031:         return DEV_ATA;
 2032:       return DEV_SCSI;
 2033: 
 2034:     case 0x09: // BusTypeiScsi
 2035:     case 0x0a: // BusTypeSas
 2036:       return DEV_SCSI;
 2037: 
 2038:     case BusTypeUsb:
 2039:       return DEV_USB;
 2040: 
 2041:     default:
 2042:       return DEV_UNKNOWN;
 2043:   }
 2044:   /*NOTREACHED*/
 2045: }
 2046: 
 2047: // get DEV_* for device path
 2048: static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 2049: {
 2050:   bool admin = true;
 2051:   HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
 2052:     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 2053:   if (h == INVALID_HANDLE_VALUE) {
 2054:     admin = false;
 2055:     h = CreateFileA(path, 0,
 2056:       FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 2057:     if (h == INVALID_HANDLE_VALUE)
 2058:       return DEV_UNKNOWN;
 2059:   }
 2060:   if (ata_debugmode > 1 || scsi_debugmode > 1)
 2061:     pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
 2062:   win_dev_type type = get_controller_type(h, admin, ata_version_ex);
 2063:   CloseHandle(h);
 2064:   return type;
 2065: }
 2066: 
 2067: // get DEV_* for physical drive number
 2068: static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
 2069: {
 2070:   char path[30];
 2071:   snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
 2072:   return get_controller_type(path, ata_version_ex);
 2073: }
 2074: 
 2075: static win_dev_type get_phy_drive_type(int drive)
 2076: {
 2077:   return get_phy_drive_type(drive, 0);
 2078: }
 2079: 
 2080: // get DEV_* for logical drive number
 2081: static win_dev_type get_log_drive_type(int drive)
 2082: {
 2083:   char path[30];
 2084:   snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
 2085:   return get_controller_type(path);
 2086: }
 2087: 
 2088: // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
 2089: static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
 2090: {
 2091:   STORAGE_DEVICE_DESCRIPTOR_DATA data;
 2092:   if (storage_query_property_ioctl(hdevice, &data))
 2093:     return -1;
 2094: 
 2095:   memset(id, 0, sizeof(*id));
 2096: 
 2097:   // Some drivers split ATA model string into VendorId and ProductId,
 2098:   // others return it as ProductId only.
 2099:   char model[sizeof(id->model) + 1] = "";
 2100: 
 2101:   unsigned i = 0;
 2102:   if (data.desc.VendorIdOffset) {
 2103:     for ( ;i < sizeof(model)-1 && data.raw[data.desc.VendorIdOffset+i]; i++)
 2104:       model[i] = data.raw[data.desc.VendorIdOffset+i];
 2105:   }
 2106: 
 2107:   if (data.desc.ProductIdOffset) {
 2108:     while (i > 1 && model[i-2] == ' ') // Keep last blank from VendorId
 2109:       i--;
 2110:     // Ignore VendorId "ATA"
 2111:     if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
 2112:       i = 0;
 2113:     for (unsigned j = 0; i < sizeof(model)-1 && data.raw[data.desc.ProductIdOffset+j]; i++, j++)
 2114:       model[i] = data.raw[data.desc.ProductIdOffset+j];
 2115:   }
 2116: 
 2117:   while (i > 0 && model[i-1] == ' ')
 2118:     i--;
 2119:   model[i] = 0;
 2120:   copy_swapped(id->model, model, sizeof(id->model));
 2121: 
 2122:   if (data.desc.ProductRevisionOffset)
 2123:     copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
 2124: 
 2125:   id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
 2126:   id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
 2127:   return 0;
 2128: }
 2129: 
 2130: // Get Serial Number in IDENTIFY from WMI
 2131: static bool get_serial_from_wmi(int drive, ata_identify_device * id)
 2132: {
 2133:   bool debug = (ata_debugmode > 1);
 2134: 
 2135:   wbem_services ws;
 2136:   if (!ws.connect()) {
 2137:     if (debug)
 2138:       pout("WMI connect failed\n");
 2139:     return false;
 2140:   }
 2141: 
 2142:   wbem_object wo;
 2143:   if (!ws.query1(wo, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
 2144:                      "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
 2145:     return false;
 2146: 
 2147:   std::string serial = wo.get_str("SerialNumber");
 2148:   if (debug)
 2149:     pout("  WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive, wo.get_str("Model").c_str(), serial.c_str());
 2150: 
 2151:   copy_swapped(id->serial_no, serial.c_str(), sizeof(id->serial_no));
 2152:   return true;
 2153: }
 2154: 
 2155: 
 2156: /////////////////////////////////////////////////////////////////////////////
 2157: // USB ID detection using WMI
 2158: 
 2159: // Get USB ID for a physical drive number
 2160: static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
 2161: {
 2162:   bool debug = (scsi_debugmode > 1);
 2163: 
 2164:   wbem_services ws;
 2165:   if (!ws.connect()) {
 2166:     if (debug)
 2167:       pout("WMI connect failed\n");
 2168:     return false;
 2169:   }
 2170: 
 2171:   // Get device name
 2172:   wbem_object wo;
 2173:   if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
 2174:     return false;
 2175: 
 2176:   std::string name = wo.get_str("Model");
 2177:   if (debug)
 2178:     pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str());
 2179: 
 2180:   // Get USB_CONTROLLER -> DEVICE associations
 2181:   wbem_enumerator we;
 2182:   if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
 2183:     return false;
 2184: 
 2185:   unsigned short usb_venid = 0, prev_usb_venid = 0;
 2186:   unsigned short usb_proid = 0, prev_usb_proid = 0;
 2187:   std::string prev_usb_ant;
 2188:   std::string prev_ant, ant, dep;
 2189: 
 2190:   const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
 2191: 
 2192:   while (we.next(wo)) {
 2193:     prev_ant = ant;
 2194:     // Find next 'USB_CONTROLLER, DEVICE' pair
 2195:     ant = wo.get_str("Antecedent");
 2196:     dep = wo.get_str("Dependent");
 2197: 
 2198:     if (debug && ant != prev_ant)
 2199:       pout(" %s:\n", ant.c_str());
 2200: 
 2201:     // Extract DeviceID
 2202:     regmatch_t match[2];
 2203:     if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
 2204:       if (debug)
 2205:         pout("  | (\"%s\")\n", dep.c_str());
 2206:       continue;
 2207:     }
 2208: 
 2209:     std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
 2210: 
 2211:     if (str_starts_with(devid, "USB\\\\VID_")) {
 2212:       // USB bridge entry, save CONTROLLER, ID
 2213:       int nc = -1;
 2214:       if (!(sscanf(devid.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
 2215:             &prev_usb_venid, &prev_usb_proid, &nc) == 2 && nc == 9+4+5+4)) {
 2216:         prev_usb_venid = prev_usb_proid = 0;
 2217:       }
 2218:       prev_usb_ant = ant;
 2219:       if (debug)
 2220:         pout("  +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
 2221:       continue;
 2222:     }
 2223:     else if (str_starts_with(devid, "USBSTOR\\\\")) {
 2224:       // USBSTOR device found
 2225:       if (debug)
 2226:         pout("  +--> \"%s\"\n", devid.c_str());
 2227: 
 2228:       // Retrieve name
 2229:       wbem_object wo2;
 2230:       if (!ws.query1(wo2, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid.c_str()))
 2231:         continue;
 2232:       std::string name2 = wo2.get_str("Name");
 2233: 
 2234:       // Continue if not name of physical disk drive
 2235:       if (name2 != name) {
 2236:         if (debug)
 2237:           pout("  +---> (\"%s\")\n", name2.c_str());
 2238:         continue;
 2239:       }
 2240: 
 2241:       // Fail if previous USB bridge is associated to other controller or ID is unknown
 2242:       if (!(ant == prev_usb_ant && prev_usb_venid)) {
 2243:         if (debug)
 2244:           pout("  +---> \"%s\" (Error: No USB bridge found)\n", name2.c_str());
 2245:         return false;
 2246:       }
 2247: 
 2248:       // Handle multiple devices with same name
 2249:       if (usb_venid) {
 2250:         // Fail if multiple devices with same name have different USB bridge types
 2251:         if (!(usb_venid == prev_usb_venid && usb_proid == prev_usb_proid)) {
 2252:           if (debug)
 2253:             pout("  +---> \"%s\" (Error: More than one USB ID found)\n", name2.c_str());
 2254:           return false;
 2255:         }
 2256:       }
 2257: 
 2258:       // Found
 2259:       usb_venid = prev_usb_venid;
 2260:       usb_proid = prev_usb_proid;
 2261:       if (debug)
 2262:         pout("  +===> \"%s\" [0x%04x:0x%04x]\n", name2.c_str(), usb_venid, usb_proid);
 2263: 
 2264:       // Continue to check for duplicate names ...
 2265:     }
 2266:     else {
 2267:       if (debug)
 2268:         pout("  |   \"%s\"\n", devid.c_str());
 2269:     }
 2270:   }
 2271: 
 2272:   if (!usb_venid)
 2273:     return false;
 2274: 
 2275:   vendor_id = usb_venid;
 2276:   product_id = usb_proid;
 2277: 
 2278:   return true;
 2279: }
 2280: 
 2281: 
 2282: /////////////////////////////////////////////////////////////////////////////
 2283: 
 2284: // Call GetDevicePowerState()
 2285: // returns: 1=active, 0=standby, -1=error
 2286: // (This would also work for SCSI drives)
 2287: 
 2288: static int get_device_power_state(HANDLE hdevice)
 2289: {
 2290:   BOOL state = TRUE;
 2291:   if (!GetDevicePowerState(hdevice, &state)) {
 2292:     long err = GetLastError();
 2293:     if (ata_debugmode)
 2294:       pout("  GetDevicePowerState() failed, Error=%ld\n", err);
 2295:     errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
 2296:     // TODO: This may not work as expected on transient errors,
 2297:     // because smartd interprets -1 as SLEEP mode regardless of errno.
 2298:     return -1;
 2299:   }
 2300: 
 2301:   if (ata_debugmode > 1)
 2302:     pout("  GetDevicePowerState() succeeded, state=%d\n", state);
 2303:   return state;
 2304: }
 2305: 
 2306: 
 2307: /////////////////////////////////////////////////////////////////////////////
 2308: 
 2309: // Get default ATA device options
 2310: 
 2311: static const char * ata_get_def_options()
 2312: {
 2313:   return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
 2314:                    // STORAGE_*, SCSI_MINIPORT_*
 2315: }
 2316: 
 2317: 
 2318: // Common routines for devices with HANDLEs
 2319: 
 2320: win_smart_device::~win_smart_device() throw()
 2321: {
 2322:   if (m_fh != INVALID_HANDLE_VALUE)
 2323:     ::CloseHandle(m_fh);
 2324: }
 2325: 
 2326: bool win_smart_device::is_open() const
 2327: {
 2328:   return (m_fh != INVALID_HANDLE_VALUE);
 2329: }
 2330: 
 2331: bool win_smart_device::close()
 2332: {
 2333:   if (m_fh == INVALID_HANDLE_VALUE)
 2334:     return true;
 2335:   BOOL rc = ::CloseHandle(m_fh);
 2336:   m_fh = INVALID_HANDLE_VALUE;
 2337:   return !!rc;
 2338: }
 2339: 
 2340: // ATA
 2341: 
 2342: win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
 2343: : smart_device(intf, dev_name, "ata", req_type),
 2344:   m_usr_options(false),
 2345:   m_admin(false),
 2346:   m_phydrive(-1),
 2347:   m_id_is_cached(false),
 2348:   m_is_3ware(false),
 2349:   m_port(-1),
 2350:   m_smartver_state(0)
 2351: {
 2352: }
 2353: 
 2354: win_ata_device::~win_ata_device() throw()
 2355: {
 2356: }
 2357: 
 2358: 
 2359: // Open ATA device
 2360: 
 2361: bool win_ata_device::open()
 2362: {
 2363:   const char * name = skipdev(get_dev_name()); int len = strlen(name);
 2364:   // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
 2365:   char drive[2+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
 2366:   if (   sscanf(name, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive, &n1, options, &n2) >= 1
 2367:       && ((n1 == len && !options[0]) || n2 == len)                                   ) {
 2368:     return open(sdxy_to_phydrive(drive), -1, options, -1);
 2369:   }
 2370:   // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
 2371:   drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
 2372:   unsigned port = ~0;
 2373:   if (   sscanf(name, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive, &port, &n1, options, &n2) >= 2
 2374:       && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                  ) {
 2375:     return open(sdxy_to_phydrive(drive), -1, options, port);
 2376:   }
 2377:   // pd<m>,N => Physical drive <m>, RAID port N
 2378:   int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
 2379:   if (   sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
 2380:       && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
 2381:     return open(phydrive, -1, "", (int)port);
 2382:   }
 2383:   // [a-zA-Z]: => Physical drive behind logical drive 0-25
 2384:   int logdrive = drive_letter(name);
 2385:   if (logdrive >= 0) {
 2386:     return open(-1, logdrive, "", -1);
 2387:   }
 2388: 
 2389:   return set_err(EINVAL);
 2390: }
 2391: 
 2392: 
 2393: bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
 2394: {
 2395:   m_phydrive = -1;
 2396:   char devpath[30];
 2397:   if (0 <= phydrive && phydrive <= 255)
 2398:     snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive = phydrive));
 2399:   else if (0 <= logdrive && logdrive <= 'Z'-'A')
 2400:     snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
 2401:   else
 2402:     return set_err(ENOENT);
 2403: 
 2404:   // Open device
 2405:   HANDLE h = INVALID_HANDLE_VALUE;
 2406:   if (!(*options && !options[strspn(options, "fp")])) {
 2407:     // Open with admin rights
 2408:     m_admin = true;
 2409:     h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
 2410:       FILE_SHARE_READ|FILE_SHARE_WRITE,
 2411:       NULL, OPEN_EXISTING, 0, 0);
 2412:   }
 2413:   if (h == INVALID_HANDLE_VALUE) {
 2414:     // Open without admin rights
 2415:     m_admin = false;
 2416:     h = CreateFileA(devpath, 0,
 2417:       FILE_SHARE_READ|FILE_SHARE_WRITE,
 2418:       NULL, OPEN_EXISTING, 0, 0);
 2419:   }
 2420:   if (h == INVALID_HANDLE_VALUE) {
 2421:     long err = GetLastError();
 2422:     if (err == ERROR_FILE_NOT_FOUND)
 2423:       set_err(ENOENT, "%s: not found", devpath);
 2424:     else if (err == ERROR_ACCESS_DENIED)
 2425:       set_err(EACCES, "%s: access denied", devpath);
 2426:     else
 2427:       set_err(EIO, "%s: Error=%ld", devpath, err);
 2428:     return false;
 2429:   }
 2430:   set_fh(h);
 2431: 
 2432:   // Warn once if admin rights are missing
 2433:   if (!m_admin) {
 2434:     static bool noadmin_warning = false;
 2435:     if (!noadmin_warning) {
 2436:       pout("Warning: Limited functionality due to missing admin rights\n");
 2437:       noadmin_warning = true;
 2438:     }
 2439:   }
 2440: 
 2441:   if (ata_debugmode > 1)
 2442:     pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
 2443: 
 2444:   m_usr_options = false;
 2445:   if (*options) {
 2446:     // Save user options
 2447:     m_options = options; m_usr_options = true;
 2448:   }
 2449:   else if (port >= 0)
 2450:     // RAID: SMART_* and SCSI_MINIPORT
 2451:     m_options = "s3";
 2452:   else {
 2453:     // Set default options according to Windows version
 2454:     static const char * def_options = ata_get_def_options();
 2455:     m_options = def_options;
 2456:   }
 2457: 
 2458:   // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
 2459:   m_port = port;
 2460:   if (port < 0)
 2461:     return true;
 2462: 
 2463:   // 3ware RAID: Get port map
 2464:   GETVERSIONINPARAMS_EX vers_ex;
 2465:   int devmap = smart_get_version(h, &vers_ex);
 2466: 
 2467:   // 3ware RAID if vendor id present
 2468:   m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 2469: 
 2470:   unsigned long portmap = 0;
 2471:   if (port >= 0 && devmap >= 0) {
 2472:     // 3ware RAID: check vendor id
 2473:     if (!m_is_3ware) {
 2474:       pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
 2475:            "This is no 3ware 9000 controller or driver has no SMART support.\n",
 2476:            vers_ex.wIdentifier);
 2477:       devmap = -1;
 2478:     }
 2479:     else
 2480:       portmap = vers_ex.dwDeviceMapEx;
 2481:   }
 2482:   if (devmap < 0) {
 2483:     pout("%s: ATA driver has no SMART support\n", devpath);
 2484:     if (!is_permissive()) {
 2485:       close();
 2486:       return set_err(ENOSYS);
 2487:     }
 2488:     devmap = 0x0f;
 2489:   }
 2490:   m_smartver_state = 1;
 2491: 
 2492:   {
 2493:     // 3ware RAID: update devicemap first
 2494: 
 2495:     if (!update_3ware_devicemap_ioctl(h)) {
 2496:       if (   smart_get_version(h, &vers_ex) >= 0
 2497:           && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
 2498:         portmap = vers_ex.dwDeviceMapEx;
 2499:     }
 2500:     // Check port existence
 2501:     if (!(portmap & (1L << port))) {
 2502:       if (!is_permissive()) {
 2503:         close();
 2504:         return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
 2505:       }
 2506:     }
 2507:   }
 2508: 
 2509:   return true;
 2510: }
 2511: 
 2512: 
 2513: /////////////////////////////////////////////////////////////////////////////
 2514: 
 2515: // Interface to ATA devices
 2516: bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 2517: {
 2518:   // No multi-sector support for now, see above
 2519:   // warning about IOCTL_ATA_PASS_THROUGH
 2520:   if (!ata_cmd_is_supported(in,
 2521:     ata_device::supports_data_out |
 2522:     ata_device::supports_output_regs |
 2523:     ata_device::supports_48bit)
 2524:   )
 2525:     return false;
 2526: 
 2527:   // 3ware RAID: SMART DISABLE without port number disables SMART functions
 2528:   if (   m_is_3ware && m_port < 0
 2529:       && in.in_regs.command == ATA_SMART_CMD
 2530:       && in.in_regs.features == ATA_SMART_DISABLE)
 2531:     return set_err(ENOSYS, "SMART DISABLE requires 3ware port number");
 2532: 
 2533:   // Determine ioctl functions valid for this ATA cmd
 2534:   const char * valid_options = 0;
 2535: 
 2536:   switch (in.in_regs.command) {
 2537:     case ATA_IDENTIFY_DEVICE:
 2538:     case ATA_IDENTIFY_PACKET_DEVICE:
 2539:       // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
 2540:       // and SCSI_MINIPORT_* if requested by user
 2541:       valid_options = (m_usr_options ? "saimf" : "saif");
 2542:       break;
 2543: 
 2544:     case ATA_CHECK_POWER_MODE:
 2545:       // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
 2546:       valid_options = "pai3";
 2547:       break;
 2548: 
 2549:     case ATA_SMART_CMD:
 2550:       switch (in.in_regs.features) {
 2551:         case ATA_SMART_READ_VALUES:
 2552:         case ATA_SMART_READ_THRESHOLDS:
 2553:         case ATA_SMART_AUTOSAVE:
 2554:         case ATA_SMART_ENABLE:
 2555:         case ATA_SMART_DISABLE:
 2556:         case ATA_SMART_AUTO_OFFLINE:
 2557:           // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
 2558:           // and SCSI_MINIPORT_* if requested by user
 2559:           valid_options = (m_usr_options ? "saimf" : "saif");
 2560:           break;
 2561: 
 2562:         case ATA_SMART_IMMEDIATE_OFFLINE:
 2563:           // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
 2564:           valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ ?
 2565:                            "saim3" : "aim3");
 2566:           break;
 2567: 
 2568:         case ATA_SMART_READ_LOG_SECTOR:
 2569:           // SMART_RCV_DRIVE_DATA does not support READ_LOG
 2570:           // Try SCSI_MINIPORT also to skip buggy class driver
 2571:           // SMART functions do not support multi sector I/O.
 2572:           if (in.size == 512)
 2573:             valid_options = (m_usr_options ? "saim3" : "aim3");
 2574:           else
 2575:             valid_options = "a";
 2576:           break;
 2577: 
 2578:         case ATA_SMART_WRITE_LOG_SECTOR:
 2579:           // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
 2580:           // but SCSI_MINIPORT_* only if requested by user and single sector.
 2581:           valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
 2582:           break;
 2583: 
 2584:         case ATA_SMART_STATUS:
 2585:           valid_options = (m_usr_options ? "saimf" : "saif");
 2586:           break;
 2587: 
 2588:         default:
 2589:           // Unknown SMART command, handle below
 2590:           break;
 2591:       }
 2592:       break;
 2593: 
 2594:     default:
 2595:       // Other ATA command, handle below
 2596:       break;
 2597:   }
 2598: 
 2599:   if (!valid_options) {
 2600:     // No special ATA command found above, select a generic pass through ioctl.
 2601:     if (!(   in.direction == ata_cmd_in::no_data
 2602:           || (in.direction == ata_cmd_in::data_in && in.size == 512))
 2603:          ||  in.in_regs.is_48bit_cmd()                               )
 2604:       // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
 2605:       valid_options = "a";
 2606:     else
 2607:       // ATA/IDE_PASS_THROUGH
 2608:       valid_options = "ai";
 2609:   }
 2610: 
 2611:   if (!m_admin) {
 2612:     // Restrict to IOCTL_STORAGE_*
 2613:     if (strchr(valid_options, 'f'))
 2614:       valid_options = "f";
 2615:     else if (strchr(valid_options, 'p'))
 2616:       valid_options = "p";
 2617:     else
 2618:       return set_err(ENOSYS, "Function requires admin rights");
 2619:   }
 2620: 
 2621:   // Set IDEREGS
 2622:   IDEREGS regs, prev_regs;
 2623:   {
 2624:     const ata_in_regs & lo = in.in_regs;
 2625:     regs.bFeaturesReg     = lo.features;
 2626:     regs.bSectorCountReg  = lo.sector_count;
 2627:     regs.bSectorNumberReg = lo.lba_low;
 2628:     regs.bCylLowReg       = lo.lba_mid;
 2629:     regs.bCylHighReg      = lo.lba_high;
 2630:     regs.bDriveHeadReg    = lo.device;
 2631:     regs.bCommandReg      = lo.command;
 2632:     regs.bReserved        = 0;
 2633:   }
 2634:   if (in.in_regs.is_48bit_cmd()) {
 2635:     const ata_in_regs & hi = in.in_regs.prev;
 2636:     prev_regs.bFeaturesReg     = hi.features;
 2637:     prev_regs.bSectorCountReg  = hi.sector_count;
 2638:     prev_regs.bSectorNumberReg = hi.lba_low;
 2639:     prev_regs.bCylLowReg       = hi.lba_mid;
 2640:     prev_regs.bCylHighReg      = hi.lba_high;
 2641:     prev_regs.bDriveHeadReg    = hi.device;
 2642:     prev_regs.bCommandReg      = hi.command;
 2643:     prev_regs.bReserved        = 0;
 2644:   }
 2645: 
 2646:   // Set data direction
 2647:   int datasize = 0;
 2648:   char * data = 0;
 2649:   switch (in.direction) {
 2650:     case ata_cmd_in::no_data:
 2651:       break;
 2652:     case ata_cmd_in::data_in:
 2653:       datasize = (int)in.size;
 2654:       data = (char *)in.buffer;
 2655:       break;
 2656:     case ata_cmd_in::data_out:
 2657:       datasize = -(int)in.size;
 2658:       data = (char *)in.buffer;
 2659:       break;
 2660:     default:
 2661:       return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
 2662:           (int)in.direction);
 2663:   }
 2664: 
 2665: 
 2666:   // Try all valid ioctls in the order specified in m_options
 2667:   bool powered_up = false;
 2668:   bool out_regs_set = false;
 2669:   bool id_is_cached = false;
 2670:   const char * options = m_options.c_str();
 2671: 
 2672:   for (int i = 0; ; i++) {
 2673:     char opt = options[i];
 2674: 
 2675:     if (!opt) {
 2676:       if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
 2677:         // Power up reported by GetDevicePowerState() and no ioctl available
 2678:         // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
 2679:         regs.bSectorCountReg = 0xff;
 2680:         out_regs_set = true;
 2681:         break;
 2682:       }
 2683:       // No IOCTL found
 2684:       return set_err(ENOSYS);
 2685:     }
 2686:     if (!strchr(valid_options, opt))
 2687:       // Invalid for this command
 2688:       continue;
 2689: 
 2690:     errno = 0;
 2691:     assert(   datasize == 0 || datasize == 512
 2692:            || (datasize == -512 && strchr("am", opt))
 2693:            || (datasize > 512 && opt == 'a'));
 2694:     int rc;
 2695:     switch (opt) {
 2696:       default: assert(0);
 2697:       case 's':
 2698:         // call SMART_GET_VERSION once for each drive
 2699:         if (m_smartver_state > 1) {
 2700:           rc = -1; errno = ENOSYS;
 2701:           break;
 2702:         }
 2703:         if (!m_smartver_state) {
 2704:           assert(m_port == -1);
 2705:           GETVERSIONINPARAMS_EX vers_ex;
 2706:           if (smart_get_version(get_fh(), &vers_ex) < 0) {
 2707:             if (!failuretest_permissive) {
 2708:               m_smartver_state = 2;
 2709:               rc = -1; errno = ENOSYS;
 2710:               break;
 2711:             }
 2712:             failuretest_permissive--;
 2713:           }
 2714:           else  {
 2715:             // 3ware RAID if vendor id present
 2716:             m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 2717:           }
 2718: 
 2719:           m_smartver_state = 1;
 2720:         }
 2721:         rc = smart_ioctl(get_fh(), &regs, data, datasize, m_port);
 2722:         out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
 2723:         id_is_cached = (m_port < 0); // Not cached by 3ware driver
 2724:         break;
 2725:       case 'm':
 2726:         rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
 2727:         id_is_cached = (m_port < 0);
 2728:         break;
 2729:       case 'a':
 2730:         rc = ata_pass_through_ioctl(get_fh(), &regs,
 2731:           (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
 2732:           data, datasize);
 2733:         out_regs_set = true;
 2734:         break;
 2735:       case 'i':
 2736:         rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
 2737:         out_regs_set = true;
 2738:         break;
 2739:       case 'f':
 2740:         if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
 2741:             rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
 2742:             if (rc == 0 && m_phydrive >= 0)
 2743:               get_serial_from_wmi(m_phydrive, (ata_identify_device *)data);
 2744:             id_is_cached = true;
 2745:         }
 2746:         else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
 2747:           case ATA_SMART_READ_VALUES:
 2748:             rc = storage_predict_failure_ioctl(get_fh(), data);
 2749:             if (rc > 0)
 2750:               rc = 0;
 2751:             break;
 2752:           case ATA_SMART_ENABLE:
 2753:             rc = 0;
 2754:             break;
 2755:           case ATA_SMART_STATUS:
 2756:             rc = storage_predict_failure_ioctl(get_fh());
 2757:             if (rc == 0) {
 2758:               // Good SMART status
 2759:               out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
 2760:             }
 2761:             else if (rc > 0) {
 2762:               // Bad SMART status
 2763:               out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
 2764:               rc = 0;
 2765:             }
 2766:             break;
 2767:           default:
 2768:             errno = ENOSYS; rc = -1;
 2769:         }
 2770:         else {
 2771:             errno = ENOSYS; rc = -1;
 2772:         }
 2773:         break;
 2774:       case '3':
 2775:         rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
 2776:         out_regs_set = true;
 2777:         break;
 2778:       case 'p':
 2779:         assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
 2780:         rc = get_device_power_state(get_fh());
 2781:         if (rc == 0) {
 2782:           // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
 2783:           // spin up the drive => simulate ATA result STANDBY.
 2784:           regs.bSectorCountReg = 0x00;
 2785:           out_regs_set = true;
 2786:         }
 2787:         else if (rc > 0) {
 2788:           // Power up reported by GetDevicePowerState(), but this reflects the actual mode
 2789:           // only if it is selected by the device driver => try a passthrough ioctl to get the
 2790:           // actual mode, if none available simulate ACTIVE/IDLE.
 2791:           powered_up = true;
 2792:           rc = -1; errno = ENOSYS;
 2793:         }
 2794:         break;
 2795:     }
 2796: 
 2797:     if (!rc)
 2798:       // Working ioctl found
 2799:       break;
 2800: 
 2801:     if (errno != ENOSYS)
 2802:       // Abort on I/O error
 2803:       return set_err(errno);
 2804: 
 2805:     out_regs_set = false;
 2806:     // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
 2807:   }
 2808: 
 2809:   // Return IDEREGS if set
 2810:   if (out_regs_set) {
 2811:     ata_out_regs & lo = out.out_regs;
 2812:     lo.error        = regs.bFeaturesReg;
 2813:     lo.sector_count = regs.bSectorCountReg;
 2814:     lo.lba_low      = regs.bSectorNumberReg;
 2815:     lo.lba_mid      = regs.bCylLowReg;
 2816:     lo.lba_high     = regs.bCylHighReg;
 2817:     lo.device       = regs.bDriveHeadReg;
 2818:     lo.status       = regs.bCommandReg;
 2819:     if (in.in_regs.is_48bit_cmd()) {
 2820:       ata_out_regs & hi = out.out_regs.prev;
 2821:       hi.sector_count = prev_regs.bSectorCountReg;
 2822:       hi.lba_low      = prev_regs.bSectorNumberReg;
 2823:       hi.lba_mid      = prev_regs.bCylLowReg;
 2824:       hi.lba_high     = prev_regs.bCylHighReg;
 2825:     }
 2826:   }
 2827: 
 2828:   if (   in.in_regs.command == ATA_IDENTIFY_DEVICE
 2829:       || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
 2830:     // Update ata_identify_is_cached() result according to ioctl used.
 2831:     m_id_is_cached = id_is_cached;
 2832: 
 2833:   return true;
 2834: }
 2835: 
 2836: // Return true if OS caches the ATA identify sector
 2837: bool win_ata_device::ata_identify_is_cached() const
 2838: {
 2839:   return m_id_is_cached;
 2840: }
 2841: 
 2842: 
 2843: //////////////////////////////////////////////////////////////////////
 2844: // csmi_ata_device
 2845: 
 2846: bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info)
 2847: {
 2848:   // Get driver info to check CSMI support
 2849:   CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf;
 2850:   memset(&driver_info_buf, 0, sizeof(driver_info_buf));
 2851:   if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO, &driver_info_buf.IoctlHeader, sizeof(driver_info_buf)))
 2852:     return false;
 2853: 
 2854:   if (scsi_debugmode > 1) {
 2855:     const CSMI_SAS_DRIVER_INFO & driver_info = driver_info_buf.Information;
 2856:     pout("CSMI_SAS_DRIVER_INFO:\n");
 2857:     pout("  Name:        \"%.81s\"\n", driver_info.szName);
 2858:     pout("  Description: \"%.81s\"\n", driver_info.szDescription);
 2859:     pout("  Revision:    %d.%d\n", driver_info.usMajorRevision, driver_info.usMinorRevision);
 2860:   }
 2861: 
 2862:   // Get Phy info
 2863:   CSMI_SAS_PHY_INFO_BUFFER phy_info_buf;
 2864:   memset(&phy_info_buf, 0, sizeof(phy_info_buf));
 2865:   if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO, &phy_info_buf.IoctlHeader, sizeof(phy_info_buf)))
 2866:     return false;
 2867: 
 2868:   phy_info = phy_info_buf.Information;
 2869:   if (phy_info.bNumberOfPhys > sizeof(phy_info.Phy)/sizeof(phy_info.Phy[0]))
 2870:     return set_err(EIO, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info.bNumberOfPhys);
 2871: 
 2872:   if (scsi_debugmode > 1) {
 2873:     pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info.bNumberOfPhys);
 2874:     for (int i = 0; i < phy_info.bNumberOfPhys; i++) {
 2875:       const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
 2876:       const CSMI_SAS_IDENTIFY & id = pe.Identify, & at = pe.Attached;
 2877:       pout("Phy[%d] Port:   0x%02x\n", i, pe.bPortIdentifier);
 2878:       pout("  Type:        0x%02x, 0x%02x\n", id.bDeviceType, at.bDeviceType);
 2879:       pout("  InitProto:   0x%02x, 0x%02x\n", id.bInitiatorPortProtocol, at.bInitiatorPortProtocol);
 2880:       pout("  TargetProto: 0x%02x, 0x%02x\n", id.bTargetPortProtocol, at.bTargetPortProtocol);
 2881:       pout("  PhyIdent:    0x%02x, 0x%02x\n", id.bPhyIdentifier, at.bPhyIdentifier);
 2882:       const unsigned char * b = id.bSASAddress;
 2883:       pout("  SASAddress:  %02x %02x %02x %02x %02x %02x %02x %02x, ",
 2884:         b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
 2885:       b = at.bSASAddress;
 2886:       pout(               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
 2887:         b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
 2888:     }
 2889:   }
 2890: 
 2891:   return true;
 2892: }
 2893: 
 2894: bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO & phy_info, unsigned phy_no)
 2895: {
 2896:   // Check Phy presence
 2897:   if (phy_no >= phy_info.bNumberOfPhys)
 2898:     return set_err(ENOENT, "Port %u does not exist (#ports: %d)", phy_no,
 2899:       phy_info.bNumberOfPhys);
 2900: 
 2901:   const CSMI_SAS_PHY_ENTITY & phy_ent = phy_info.Phy[phy_no];
 2902:   if (phy_ent.Attached.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
 2903:     return set_err(ENOENT, "No device on port %u", phy_no);
 2904: 
 2905:   switch (phy_ent.Attached.bTargetPortProtocol) {
 2906:     case CSMI_SAS_PROTOCOL_SATA:
 2907:     case CSMI_SAS_PROTOCOL_STP:
 2908:       break;
 2909:     default:
 2910:       return set_err(ENOENT, "No SATA device on port %u (protocol: %u)",
 2911:         phy_no, phy_ent.Attached.bTargetPortProtocol);
 2912:   }
 2913: 
 2914:   return true;
 2915: }
 2916: 
 2917: bool csmi_device::select_phy(unsigned phy_no)
 2918: {
 2919:   CSMI_SAS_PHY_INFO phy_info;
 2920:   if (!get_phy_info(phy_info))
 2921:     return false;
 2922: 
 2923: 
 2924:   if (!check_phy(phy_info, phy_no))
 2925:     return false;
 2926: 
 2927:   m_phy_ent = phy_info.Phy[phy_no];
 2928:   return true;
 2929: }
 2930: 
 2931: 
 2932: bool csmi_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 2933: {
 2934:   if (!ata_cmd_is_supported(in,
 2935:     ata_device::supports_data_out |
 2936:     ata_device::supports_output_regs |
 2937:     ata_device::supports_multi_sector |
 2938:     ata_device::supports_48bit,
 2939:     "CMSI")
 2940:   )
 2941:     return false;
 2942: 
 2943:   // Create buffer with appropriate size
 2944:   raw_buffer pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) + in.size);
 2945:   CSMI_SAS_STP_PASSTHRU_BUFFER * pthru_buf = (CSMI_SAS_STP_PASSTHRU_BUFFER *)pthru_raw_buf.data();
 2946: 
 2947:   // Set addresses from Phy info
 2948:   CSMI_SAS_STP_PASSTHRU & pthru = pthru_buf->Parameters;
 2949:   const CSMI_SAS_PHY_ENTITY & phy_ent = get_phy_ent();
 2950:   pthru.bPhyIdentifier = phy_ent.Identify.bPhyIdentifier;
 2951:   pthru.bPortIdentifier = phy_ent.bPortIdentifier;
 2952:   memcpy(pthru.bDestinationSASAddress, phy_ent.Attached.bSASAddress,
 2953:     sizeof(pthru.bDestinationSASAddress));
 2954:   pthru.bConnectionRate = CSMI_SAS_LINK_RATE_NEGOTIATED;
 2955: 
 2956:   // Set transfer mode
 2957:   switch (in.direction) {
 2958:     case ata_cmd_in::no_data:
 2959:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_UNSPECIFIED;
 2960:       break;
 2961:     case ata_cmd_in::data_in:
 2962:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_READ;
 2963:       pthru.uDataLength = in.size;
 2964:       break;
 2965:     case ata_cmd_in::data_out:
 2966:       pthru.uFlags = CSMI_SAS_STP_PIO | CSMI_SAS_STP_WRITE;
 2967:       pthru.uDataLength = in.size;
 2968:       memcpy(pthru_buf->bDataBuffer, in.buffer, in.size);
 2969:       break;
 2970:     default:
 2971:       return set_err(EINVAL, "csmi_ata_device::ata_pass_through: invalid direction=%d",
 2972:         (int)in.direction);
 2973:   }
 2974: 
 2975:   // Set host-to-device FIS
 2976:   {
 2977:     unsigned char * fis = pthru.bCommandFIS;
 2978:     const ata_in_regs & lo = in.in_regs;
 2979:     const ata_in_regs & hi = in.in_regs.prev;
 2980:     fis[ 0] = 0x27; // Type: host-to-device FIS
 2981:     fis[ 1] = 0x80; // Bit7: Update command register
 2982:     fis[ 2] = lo.command;
 2983:     fis[ 3] = lo.features;
 2984:     fis[ 4] = lo.lba_low;
 2985:     fis[ 5] = lo.lba_mid;
 2986:     fis[ 6] = lo.lba_high;
 2987:     fis[ 7] = lo.device;
 2988:     fis[ 8] = hi.lba_low;
 2989:     fis[ 9] = hi.lba_mid;
 2990:     fis[10] = hi.lba_high;
 2991:     fis[11] = hi.features;
 2992:     fis[12] = lo.sector_count;
 2993:     fis[13] = hi.sector_count;
 2994:   }
 2995: 
 2996:   // Call ioctl
 2997:   if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU, &pthru_buf->IoctlHeader, pthru_raw_buf.size())) {
 2998:     return false;
 2999:   }
 3000: 
 3001:   // Get device-to-host FIS
 3002:   {
 3003:     const unsigned char * fis = pthru_buf->Status.bStatusFIS;
 3004:     ata_out_regs & lo = out.out_regs;
 3005:     lo.status       = fis[ 2];
 3006:     lo.error        = fis[ 3];
 3007:     lo.lba_low      = fis[ 4];
 3008:     lo.lba_mid      = fis[ 5];
 3009:     lo.lba_high     = fis[ 6];
 3010:     lo.device       = fis[ 7];
 3011:     lo.sector_count = fis[12];
 3012:     if (in.in_regs.is_48bit_cmd()) {
 3013:       ata_out_regs & hi = out.out_regs.prev;
 3014:       hi.lba_low      = fis[ 8];
 3015:       hi.lba_mid      = fis[ 9];
 3016:       hi.lba_high     = fis[10];
 3017:       hi.sector_count = fis[13];
 3018:     }
 3019:   }
 3020: 
 3021:   // Get data
 3022:   if (in.direction == ata_cmd_in::data_in)
 3023:     // TODO: Check ptru_buf->Status.uDataBytes
 3024:     memcpy(in.buffer, pthru_buf->bDataBuffer, in.size);
 3025: 
 3026:   return true;
 3027: }
 3028: 
 3029: 
 3030: //////////////////////////////////////////////////////////////////////
 3031: // win_csmi_device
 3032: 
 3033: win_csmi_device::win_csmi_device(smart_interface * intf, const char * dev_name,
 3034:   const char * req_type)
 3035: : smart_device(intf, dev_name, "ata", req_type),
 3036:   m_fh(INVALID_HANDLE_VALUE), m_phy_no(0)
 3037: {
 3038: }
 3039: 
 3040: win_csmi_device::~win_csmi_device() throw()
 3041: {
 3042:   if (m_fh != INVALID_HANDLE_VALUE)
 3043:     CloseHandle(m_fh);
 3044: }
 3045: 
 3046: bool win_csmi_device::is_open() const
 3047: {
 3048:   return (m_fh != INVALID_HANDLE_VALUE);
 3049: }
 3050: 
 3051: bool win_csmi_device::close()
 3052: {
 3053:   if (m_fh == INVALID_HANDLE_VALUE)
 3054:     return true;
 3055:   BOOL rc = CloseHandle(m_fh);
 3056:   m_fh = INVALID_HANDLE_VALUE;
 3057:   return !!rc;
 3058: }
 3059: 
 3060: 
 3061: bool win_csmi_device::open_scsi()
 3062: {
 3063:   // Parse name
 3064:   unsigned contr_no = ~0, phy_no = ~0; int nc = -1;
 3065:   const char * name = skipdev(get_dev_name());
 3066:   if (!(   sscanf(name, "csmi%u,%u%n", &contr_no, &phy_no, &nc) >= 0
 3067:         && nc == (int)strlen(name) && contr_no <= 9 && phy_no < 32)  )
 3068:     return set_err(EINVAL);
 3069: 
 3070:   // Open controller handle
 3071:   char devpath[30];
 3072:   snprintf(devpath, sizeof(devpath)-1, "\\\\.\\Scsi%u:", contr_no);
 3073: 
 3074:   HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
 3075:     FILE_SHARE_READ|FILE_SHARE_WRITE,
 3076:     (SECURITY_ATTRIBUTES *)0, OPEN_EXISTING, 0, 0);
 3077: 
 3078:   if (h == INVALID_HANDLE_VALUE) {
 3079:     long err = GetLastError();
 3080:     if (err == ERROR_FILE_NOT_FOUND)
 3081:       set_err(ENOENT, "%s: not found", devpath);
 3082:     else if (err == ERROR_ACCESS_DENIED)
 3083:       set_err(EACCES, "%s: access denied", devpath);
 3084:     else
 3085:       set_err(EIO, "%s: Error=%ld", devpath, err);
 3086:     return false;
 3087:   }
 3088: 
 3089:   if (scsi_debugmode > 1)
 3090:     pout(" %s: successfully opened\n", devpath);
 3091: 
 3092:   m_fh = h;
 3093:   m_phy_no = phy_no;
 3094:   return true;
 3095: }
 3096: 
 3097: 
 3098: bool win_csmi_device::open()
 3099: {
 3100:   if (!open_scsi())
 3101:     return false;
 3102: 
 3103:   // Get Phy info for this drive
 3104:   if (!select_phy(m_phy_no)) {
 3105:     close();
 3106:     return false;
 3107:   }
 3108: 
 3109:   return true;
 3110: }
 3111: 
 3112: 
 3113: bool win_csmi_device::csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
 3114:   unsigned csmi_bufsiz)
 3115: {
 3116:   // Determine signature
 3117:   const char * sig;
 3118:   switch (code) {
 3119:     case CC_CSMI_SAS_GET_DRIVER_INFO:
 3120:       sig = CSMI_ALL_SIGNATURE; break;
 3121:     case CC_CSMI_SAS_GET_PHY_INFO:
 3122:     case CC_CSMI_SAS_STP_PASSTHRU:
 3123:       sig = CSMI_SAS_SIGNATURE; break;
 3124:     default:
 3125:       return set_err(ENOSYS, "Unknown CSMI code=%u", code);
 3126:   }
 3127: 
 3128:   // Set header
 3129:   csmi_buffer->HeaderLength = sizeof(IOCTL_HEADER);
 3130:   strncpy((char *)csmi_buffer->Signature, sig, sizeof(csmi_buffer->Signature));
 3131:   csmi_buffer->Timeout = CSMI_SAS_TIMEOUT;
 3132:   csmi_buffer->ControlCode = code;
 3133:   csmi_buffer->ReturnCode = 0;
 3134:   csmi_buffer->Length = csmi_bufsiz - sizeof(IOCTL_HEADER);
 3135: 
 3136:   // Call function
 3137:   DWORD num_out = 0;
 3138:   if (!DeviceIoControl(m_fh, IOCTL_SCSI_MINIPORT,
 3139:     csmi_buffer, csmi_bufsiz, csmi_buffer, csmi_bufsiz, &num_out, (OVERLAPPED*)0)) {
 3140:     long err = GetLastError();
 3141:     if (scsi_debugmode)
 3142:       pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code, err);
 3143:     if (   err == ERROR_INVALID_FUNCTION
 3144:         || err == ERROR_NOT_SUPPORTED
 3145:         || err == ERROR_DEV_NOT_EXIST)
 3146:       return set_err(ENOSYS, "CSMI is not supported (Error=%ld)", err);
 3147:     else
 3148:       return set_err(EIO, "CSMI(%u) failed with Error=%ld", code, err);
 3149:   }
 3150: 
 3151:   // Check result
 3152:   if (csmi_buffer->ReturnCode) {
 3153:     if (scsi_debugmode) {
 3154:       pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",
 3155:         code, csmi_buffer->ReturnCode);
 3156:     }
 3157:     return set_err(EIO, "CSMI(%u) failed with ReturnCode=%lu", code, csmi_buffer->ReturnCode);
 3158:   }
 3159: 
 3160:   if (scsi_debugmode > 1)
 3161:     pout("  IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code, num_out);
 3162: 
 3163:   return true;
 3164: }
 3165: 
 3166: 
 3167: /////////////////////////////////////////////////////////////////////////////
 3168: // SPT Interface (for SCSI devices and ATA devices behind SATLs)
 3169: // Only supported in NT and later
 3170: /////////////////////////////////////////////////////////////////////////////
 3171: 
 3172: win_scsi_device::win_scsi_device(smart_interface * intf,
 3173:   const char * dev_name, const char * req_type)
 3174: : smart_device(intf, dev_name, "scsi", req_type)
 3175: {
 3176: }
 3177: 
 3178: bool win_scsi_device::open()
 3179: {
 3180:   const char * name = skipdev(get_dev_name()); int len = strlen(name);
 3181:   // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
 3182:   char drive[2+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
 3183:   if (   sscanf(name, "sd%2[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
 3184:       && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
 3185:     return open(sdxy_to_phydrive(drive), -1, -1, sub_addr);
 3186:   }
 3187:   // pd<m>,N => Physical drive <m>, RAID port N
 3188:   int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
 3189:   if (   sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
 3190:       && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
 3191:     return open(pd_num, -1, -1, sub_addr);
 3192:   }
 3193:   // [a-zA-Z]: => Physical drive behind logical drive 0-25
 3194:   int logdrive = drive_letter(name);
 3195:   if (logdrive >= 0) {
 3196:     return open(-1, logdrive, -1, -1);
 3197:   }
 3198:   // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
 3199:   int tape_num = -1; n1 = -1;
 3200:   if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 3201:     return open(-1, -1, tape_num, -1);
 3202:   }
 3203:   tape_num = -1; n1 = -1;
 3204:   if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 3205:     return open(-1, -1, tape_num, -1);
 3206:   }
 3207:   // tape<m> => tape drive <m>
 3208:   tape_num = -1; n1 = -1;
 3209:   if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
 3210:     return open(-1, -1, tape_num, -1);
 3211:   }
 3212: 
 3213:   return set_err(EINVAL);
 3214: }
 3215: 
 3216: bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
 3217: {
 3218:   char b[128];
 3219:   b[sizeof(b) - 1] = '\0';
 3220:   if (pd_num >= 0)
 3221:     snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
 3222:   else if (ld_num >= 0)
 3223:     snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
 3224:   else if (tape_num >= 0)
 3225:     snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
 3226:   else {
 3227:     set_err(EINVAL);
 3228:     return false;
 3229:   }
 3230: 
 3231:   // Open device
 3232:   HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
 3233:            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
 3234:            OPEN_EXISTING, 0, 0);
 3235:   if (h == INVALID_HANDLE_VALUE) {
 3236:     set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());
 3237:     return false;
 3238:   }
 3239:   set_fh(h);
 3240:   return true;
 3241: }
 3242: 
 3243: 
 3244: typedef struct {
 3245:   SCSI_PASS_THROUGH_DIRECT spt;
 3246:   ULONG           Filler;
 3247:   UCHAR           ucSenseBuf[64];
 3248: } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
 3249: 
 3250: 
 3251: // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
 3252: // Used if DataTransferLength not supported by *_DIRECT.
 3253: static long scsi_pass_through_indirect(HANDLE h,
 3254:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
 3255: {
 3256:   struct SCSI_PASS_THROUGH_WITH_BUFFERS {
 3257:     SCSI_PASS_THROUGH spt;
 3258:     ULONG Filler;
 3259:     UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
 3260:     UCHAR ucDataBuf[512];
 3261:   };
 3262: 
 3263:   SCSI_PASS_THROUGH_WITH_BUFFERS sb;
 3264:   memset(&sb, 0, sizeof(sb));
 3265: 
 3266:   // DATA_OUT not implemented yet
 3267:   if (!(   sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
 3268:         && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
 3269:     return ERROR_INVALID_PARAMETER;
 3270: 
 3271:   sb.spt.Length = sizeof(sb.spt);
 3272:   sb.spt.CdbLength = sbd->spt.CdbLength;
 3273:   memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
 3274:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 3275:   sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
 3276:   sb.spt.DataIn = sbd->spt.DataIn;
 3277:   sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
 3278:   sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
 3279:   sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
 3280: 
 3281:   DWORD num_out;
 3282:   if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
 3283:          &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 3284:     return GetLastError();
 3285: 
 3286:   sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
 3287:   if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
 3288:     memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
 3289: 
 3290:   sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
 3291:   if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
 3292:     memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
 3293:   return 0;
 3294: }
 3295: 
 3296: 
 3297: // Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
 3298: bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
 3299: {
 3300:   int report = scsi_debugmode; // TODO
 3301: 
 3302:   if (report > 0) {
 3303:     int k, j;
 3304:     const unsigned char * ucp = iop->cmnd;
 3305:     const char * np;
 3306:     char buff[256];
 3307:     const int sz = (int)sizeof(buff);
 3308: 
 3309:     np = scsi_get_opcode_name(ucp[0]);
 3310:     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
 3311:     for (k = 0; k < (int)iop->cmnd_len; ++k)
 3312:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
 3313:     if ((report > 1) &&
 3314:       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
 3315:       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 3316: 
 3317:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
 3318:               "data, len=%d%s:\n", (int)iop->dxfer_len,
 3319:               (trunc ? " [only first 256 bytes shown]" : ""));
 3320:       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 3321:     }
 3322:     else
 3323:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 3324:     pout("%s", buff);
 3325:   }
 3326: 
 3327:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
 3328:   if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
 3329:     set_err(EINVAL, "cmnd_len too large");
 3330:     return false;
 3331:   }
 3332: 
 3333:   memset(&sb, 0, sizeof(sb));
 3334:   sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
 3335:   sb.spt.CdbLength = iop->cmnd_len;
 3336:   memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
 3337:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 3338:   sb.spt.SenseInfoOffset =
 3339:     offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
 3340:   sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
 3341: 
 3342:   bool direct = true;
 3343:   switch (iop->dxfer_dir) {
 3344:     case DXFER_NONE:
 3345:       sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
 3346:       break;
 3347:     case DXFER_FROM_DEVICE:
 3348:       sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
 3349:       sb.spt.DataTransferLength = iop->dxfer_len;
 3350:       sb.spt.DataBuffer = iop->dxferp;
 3351:       // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
 3352:       // transfers (needed for SMART STATUS check of JMicron USB bridges)
 3353:       if (sb.spt.DataTransferLength == 1)
 3354:         direct = false;
 3355:       break;
 3356:     case DXFER_TO_DEVICE:
 3357:       sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
 3358:       sb.spt.DataTransferLength = iop->dxfer_len;
 3359:       sb.spt.DataBuffer = iop->dxferp;
 3360:       break;
 3361:     default:
 3362:       set_err(EINVAL, "bad dxfer_dir");
 3363:       return false;
 3364:   }
 3365: 
 3366:   long err = 0;
 3367:   if (direct) {
 3368:     DWORD num_out;
 3369:     if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
 3370:            &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 3371:       err = GetLastError();
 3372:   }
 3373:   else
 3374:     err = scsi_pass_through_indirect(get_fh(), &sb);
 3375: 
 3376:   if (err)
 3377:     return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
 3378:       "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
 3379:       (direct ? "_DIRECT" : ""), err);
 3380: 
 3381:   iop->scsi_status = sb.spt.ScsiStatus;
 3382:   if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
 3383:     int slen = sb.ucSenseBuf[7] + 8;
 3384: 
 3385:     if (slen > (int)sizeof(sb.ucSenseBuf))
 3386:       slen = sizeof(sb.ucSenseBuf);
 3387:     if (slen > (int)iop->max_sense_len)
 3388:       slen = iop->max_sense_len;
 3389:     memcpy(iop->sensep, sb.ucSenseBuf, slen);
 3390:     iop->resp_sense_len = slen;
 3391:     if (report) {
 3392:       if (report > 1) {
 3393:         pout("  >>> Sense buffer, len=%d:\n", slen);
 3394:         dStrHex(iop->sensep, slen , 1);
 3395:       }
 3396:       if ((iop->sensep[0] & 0x7f) > 0x71)
 3397:         pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
 3398:              iop->scsi_status, iop->sensep[1] & 0xf,
 3399:              iop->sensep[2], iop->sensep[3]);
 3400:       else
 3401:         pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
 3402:              iop->scsi_status, iop->sensep[2] & 0xf,
 3403:              iop->sensep[12], iop->sensep[13]);
 3404:     }
 3405:   } else
 3406:     iop->resp_sense_len = 0;
 3407: 
 3408:   if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
 3409:     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
 3410:   else
 3411:     iop->resid = 0;
 3412: 
 3413:   if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
 3414:      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 3415:      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
 3416:         (trunc ? " [only first 256 bytes shown]" : ""));
 3417:         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 3418:   }
 3419:   return true;
 3420: }
 3421: 
 3422: // Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
 3423: static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
 3424: {
 3425:   int report = scsi_debugmode; // TODO
 3426: 
 3427:   if (report > 0) {
 3428:     int k, j;
 3429:     const unsigned char * ucp = iop->cmnd;
 3430:     const char * np;
 3431:     char buff[256];
 3432:     const int sz = (int)sizeof(buff);
 3433: 
 3434:     np = scsi_get_opcode_name(ucp[0]);
 3435:     j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
 3436:     for (k = 0; k < (int)iop->cmnd_len; ++k)
 3437:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
 3438:     if ((report > 1) &&
 3439:       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
 3440:       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 3441: 
 3442:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
 3443:               "data, len=%d%s:\n", (int)iop->dxfer_len,
 3444:               (trunc ? " [only first 256 bytes shown]" : ""));
 3445:       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 3446:     }
 3447:     else
 3448:       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
 3449:     pout("%s", buff);
 3450:   }
 3451: 
 3452:   SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
 3453:   if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
 3454:     return EINVAL;
 3455:   }
 3456: 
 3457:   memset(&sb, 0, sizeof(sb));
 3458:   sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
 3459:   //sb.spt.PathId = 0;
 3460:   sb.spt.TargetId = targetid;
 3461:   //sb.spt.Lun = 0;
 3462:   sb.spt.CdbLength = iop->cmnd_len;
 3463:   memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
 3464:   sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
 3465:   sb.spt.SenseInfoOffset =
 3466:     offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
 3467:   sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
 3468: 
 3469:   bool direct = true;
 3470:   switch (iop->dxfer_dir) {
 3471:     case DXFER_NONE:
 3472:       sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
 3473:       break;
 3474:     case DXFER_FROM_DEVICE:
 3475:       sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
 3476:       sb.spt.DataTransferLength = iop->dxfer_len;
 3477:       sb.spt.DataBuffer = iop->dxferp;
 3478:       // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
 3479:       // transfers (needed for SMART STATUS check of JMicron USB bridges)
 3480:       if (sb.spt.DataTransferLength == 1)
 3481:         direct = false;
 3482:       break;
 3483:     case DXFER_TO_DEVICE:
 3484:       sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
 3485:       sb.spt.DataTransferLength = iop->dxfer_len;
 3486:       sb.spt.DataBuffer = iop->dxferp;
 3487:       break;
 3488:     default:
 3489:       return EINVAL;
 3490:   }
 3491: 
 3492:   long err = 0;
 3493:   if (direct) {
 3494:     DWORD num_out;
 3495:     if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
 3496:            &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
 3497:       err = GetLastError();
 3498:   }
 3499:   else
 3500:     err = scsi_pass_through_indirect(fd, &sb);
 3501: 
 3502:   if (err)
 3503:   {
 3504:     return err;
 3505:   }
 3506: 
 3507:   iop->scsi_status = sb.spt.ScsiStatus;
 3508:   if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
 3509:     int slen = sb.ucSenseBuf[7] + 8;
 3510: 
 3511:     if (slen > (int)sizeof(sb.ucSenseBuf))
 3512:       slen = sizeof(sb.ucSenseBuf);
 3513:     if (slen > (int)iop->max_sense_len)
 3514:       slen = iop->max_sense_len;
 3515:     memcpy(iop->sensep, sb.ucSenseBuf, slen);
 3516:     iop->resp_sense_len = slen;
 3517:     if (report) {
 3518:       if (report > 1) {
 3519:         pout("  >>> Sense buffer, len=%d:\n", slen);
 3520:         dStrHex(iop->sensep, slen , 1);
 3521:       }
 3522:       if ((iop->sensep[0] & 0x7f) > 0x71)
 3523:         pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
 3524:              iop->scsi_status, iop->sensep[1] & 0xf,
 3525:              iop->sensep[2], iop->sensep[3]);
 3526:       else
 3527:         pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
 3528:              iop->scsi_status, iop->sensep[2] & 0xf,
 3529:              iop->sensep[12], iop->sensep[13]);
 3530:     }
 3531:   } else
 3532:     iop->resp_sense_len = 0;
 3533: 
 3534:   if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
 3535:     iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
 3536:   else
 3537:     iop->resid = 0;
 3538: 
 3539:   if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
 3540:      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
 3541:      pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
 3542:         (trunc ? " [only first 256 bytes shown]" : ""));
 3543:         dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
 3544:   }
 3545: 
 3546:   return 0;
 3547: }
 3548: 
 3549: // Areca RAID Controller(SAS Device)
 3550: win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
 3551: : smart_device(intf, dev_name, "areca", "areca")
 3552: {
 3553:     set_fh(INVALID_HANDLE_VALUE);
 3554:     set_disknum(disknum);
 3555:     set_encnum(encnum);
 3556:     set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 3557: }
 3558: 
 3559: bool win_areca_scsi_device::open()
 3560: {
 3561:   HANDLE hFh;
 3562: 
 3563:   if( is_open() )
 3564:   {
 3565:     return true;
 3566:   }
 3567:   hFh = CreateFile( get_dev_name(),
 3568:                     GENERIC_READ|GENERIC_WRITE,
 3569:                     FILE_SHARE_READ|FILE_SHARE_WRITE,
 3570:                     NULL,
 3571:                     OPEN_EXISTING,
 3572:                     0,
 3573:                     NULL );
 3574:   if(hFh == INVALID_HANDLE_VALUE)
 3575:   {
 3576:     return false;
 3577:   }
 3578: 
 3579:   set_fh(hFh);
 3580:   return true;
 3581: }
 3582: 
 3583: smart_device * win_areca_scsi_device::autodetect_open()
 3584: {
 3585:   return this;
 3586: }
 3587: 
 3588: int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
 3589: {
 3590:    int ioctlreturn = 0;
 3591: 
 3592:    ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
 3593:    if ( ioctlreturn || iop->scsi_status )
 3594:    {
 3595:      ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
 3596:      if ( ioctlreturn || iop->scsi_status )
 3597:      {
 3598:        // errors found
 3599:        return -1;
 3600:      }
 3601:    }
 3602: 
 3603:    return ioctlreturn;
 3604: }
 3605: 
 3606: bool win_areca_scsi_device::arcmsr_lock()
 3607: {
 3608: #define    SYNCOBJNAME "Global\\SynIoctlMutex"
 3609:   int ctlrnum = -1;
 3610:   char mutexstr[64];
 3611: 
 3612:   if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
 3613:     return set_err(EINVAL, "unable to parse device name");
 3614: 
 3615:   snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
 3616:   m_mutex = CreateMutex(NULL, FALSE, mutexstr);
 3617:   if ( m_mutex == NULL )
 3618:   {
 3619:     return set_err(EIO, "CreateMutex failed");
 3620:   }
 3621: 
 3622:   // atomic access to driver
 3623:   WaitForSingleObject(m_mutex, INFINITE);
 3624: 
 3625:   return true;
 3626: }
 3627: 
 3628: 
 3629: bool win_areca_scsi_device::arcmsr_unlock()
 3630: {
 3631:   if( m_mutex != NULL)
 3632:   {
 3633:       ReleaseMutex(m_mutex);
 3634:       CloseHandle(m_mutex);
 3635:   }
 3636: 
 3637:   return true;
 3638: }
 3639: 
 3640: 
 3641: // Areca RAID Controller(SATA Disk)
 3642: win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
 3643: : smart_device(intf, dev_name, "areca", "areca")
 3644: {
 3645:   set_fh(INVALID_HANDLE_VALUE);
 3646:   set_disknum(disknum);
 3647:   set_encnum(encnum);
 3648:   set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 3649: }
 3650: 
 3651: bool win_areca_ata_device::open()
 3652: {
 3653:   HANDLE hFh;
 3654: 
 3655:   if( is_open() )
 3656:   {
 3657:     return true;
 3658:   }
 3659:   hFh = CreateFile( get_dev_name(),
 3660:                     GENERIC_READ|GENERIC_WRITE,
 3661:                     FILE_SHARE_READ|FILE_SHARE_WRITE,
 3662:                     NULL,
 3663:                     OPEN_EXISTING,
 3664:                     0,
 3665:                     NULL );
 3666:   if(hFh == INVALID_HANDLE_VALUE)
 3667:   {
 3668:     return false;
 3669:   }
 3670: 
 3671:   set_fh(hFh);
 3672:   return true;
 3673: }
 3674: 
 3675: smart_device * win_areca_ata_device::autodetect_open()
 3676: {
 3677:   int is_ata = 1;
 3678: 
 3679:   // autodetect device type
 3680:   is_ata = arcmsr_get_dev_type();
 3681:   if(is_ata < 0)
 3682:   {
 3683:     set_err(EIO);
 3684:     return this;
 3685:   }
 3686: 
 3687:   if(is_ata == 1)
 3688:   {
 3689:     // SATA device
 3690:     return this;
 3691:   }
 3692: 
 3693:   // SAS device
 3694:   smart_device_auto_ptr newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
 3695:   close();
 3696:   delete this;
 3697:   newdev->open(); // TODO: Can possibly pass open fd
 3698: 
 3699:   return newdev.release();
 3700: }
 3701: 
 3702: int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
 3703: {
 3704:    int ioctlreturn = 0;
 3705: 
 3706:    ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
 3707:    if ( ioctlreturn || iop->scsi_status )
 3708:    {
 3709:      ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
 3710:      if ( ioctlreturn || iop->scsi_status )
 3711:      {
 3712:        // errors found
 3713:        return -1;
 3714:      }
 3715:    }
 3716: 
 3717:    return ioctlreturn;
 3718: }
 3719: 
 3720: bool win_areca_ata_device::arcmsr_lock()
 3721: {
 3722: #define    SYNCOBJNAME "Global\\SynIoctlMutex"
 3723:   int ctlrnum = -1;
 3724:   char mutexstr[64];
 3725: 
 3726:   if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
 3727:     return set_err(EINVAL, "unable to parse device name");
 3728: 
 3729:   snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
 3730:   m_mutex = CreateMutex(NULL, FALSE, mutexstr);
 3731:   if ( m_mutex == NULL )
 3732:   {
 3733:     return set_err(EIO, "CreateMutex failed");
 3734:   }
 3735: 
 3736:   // atomic access to driver
 3737:   WaitForSingleObject(m_mutex, INFINITE);
 3738: 
 3739:   return true;
 3740: }
 3741: 
 3742: 
 3743: bool win_areca_ata_device::arcmsr_unlock()
 3744: {
 3745:   if( m_mutex != NULL)
 3746:   {
 3747:       ReleaseMutex(m_mutex);
 3748:       CloseHandle(m_mutex);
 3749:   }
 3750: 
 3751:   return true;
 3752: }
 3753: 
 3754: 
 3755: //////////////////////////////////////////////////////////////////////////////////////////////////
 3756: 
 3757: 
 3758: } // namespace
 3759: 
 3760: /////////////////////////////////////////////////////////////////////////////
 3761: 
 3762: // Initialize platform interface and register with smi()
 3763: void smart_interface::init()
 3764: {
 3765:   {
 3766:     // Remove "." from DLL search path if supported
 3767:     // to prevent DLL preloading attacks
 3768:     BOOL (WINAPI * SetDllDirectoryA_p)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR))
 3769:       GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
 3770:     if (SetDllDirectoryA_p)
 3771:       SetDllDirectoryA_p("");
 3772:   }
 3773: 
 3774:   static os_win32::win_smart_interface the_win_interface;
 3775:   smart_interface::set(&the_win_interface);
 3776: }
 3777: 
 3778: 
 3779: #ifndef __CYGWIN__
 3780: 
 3781: // Get exe directory
 3782: // (prototype in utiliy.h)
 3783: std::string get_exe_dir()
 3784: {
 3785:   char path[MAX_PATH];
 3786:   // Get path of this exe
 3787:   if (!GetModuleFileNameA(GetModuleHandleA(0), path, sizeof(path)))
 3788:     throw std::runtime_error("GetModuleFileName() failed");
 3789:   // Replace backslash by slash
 3790:   int sl = -1;
 3791:   for (int i = 0; path[i]; i++)
 3792:     if (path[i] == '\\') {
 3793:       path[i] = '/'; sl = i;
 3794:     }
 3795:   // Remove filename
 3796:   if (sl >= 0)
 3797:     path[sl] = 0;
 3798:   return path;
 3799: }
 3800: 
 3801: #endif

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