Annotation of embedaddon/smartmontools/os_win32.cpp, revision 1.1.1.4

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

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