4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-17 Christian Franke
8 * Original AACRaid code:
9 * Copyright (C) 2015 Nidhi Malhotra <nidhi.malhotra@pmcs.com>
11 * Original Areca code:
12 * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2, or (at your option)
19 * You should have received a copy of the GNU General Public License
20 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
26 #define _WIN32_WINNT WINVER
33 #include "smartctl.h" // TODO: Do not use smartctl only variables here
35 #include "dev_interface.h"
36 #include "dev_ata_cmd_set.h"
37 #include "dev_areca.h"
39 #include "os_win32/wmiquery.h"
47 #define assert(x) /* */
50 #include <stddef.h> // offsetof()
51 #include <io.h> // access()
53 // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
54 #define WIN32_LEAN_AND_MEAN
58 // i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32
59 // (Missing: FILE_DEVICE_SCSI)
64 #elif HAVE_DDK_NTDDDISK_H
65 // older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
66 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
67 #include <ddk/ntdddisk.h>
68 #include <ddk/ntddscsi.h>
69 #include <ddk/ntddstor.h>
71 // MSVC10, older MinGW
72 // (Missing: IOCTL_SCSI_MINIPORT_*)
78 // csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
79 // (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
89 // Silence -Wunused-local-typedefs warning from g++ >= 4.8
91 #define ATTR_UNUSED __attribute__((unused))
93 #define ATTR_UNUSED /**/
96 // Macro to check constants at compile time using a dummy typedef
97 #define ASSERT_CONST(c, n) \
98 typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED
99 #define ASSERT_SIZEOF(t, n) \
100 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED
103 #define SELECT_WIN_32_64(x32, x64) (x32)
105 #define SELECT_WIN_32_64(x32, x64) (x64)
108 // Cygwin does no longer provide strn?icmp() compatibility macros
109 // MSVCRT does not provide strn?casecmp()
110 #if defined(__CYGWIN__) && !defined(stricmp)
111 #define stricmp strcasecmp
112 #define strnicmp strncasecmp
115 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 4559 2017-10-22 16:17:50Z chrfranke $";
117 /////////////////////////////////////////////////////////////////////////////
118 // Windows I/O-controls, some declarations are missing in the include files
122 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
124 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
125 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
126 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
127 ASSERT_SIZEOF(GETVERSIONINPARAMS
, 24);
128 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
129 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
132 // IDE PASS THROUGH (2000, XP, undocumented)
134 #ifndef IOCTL_IDE_PASS_THROUGH
136 #define IOCTL_IDE_PASS_THROUGH \
137 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
139 #endif // IOCTL_IDE_PASS_THROUGH
145 ULONG DataBufferSize
;
151 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
152 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
155 // ATA PASS THROUGH (Win2003, XP SP2)
157 #ifndef IOCTL_ATA_PASS_THROUGH
159 #define IOCTL_ATA_PASS_THROUGH \
160 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
162 typedef struct _ATA_PASS_THROUGH_EX
{
168 UCHAR ReservedAsUchar
;
169 ULONG DataTransferLength
;
171 ULONG ReservedAsUlong
;
172 ULONG_PTR DataBufferOffset
;
173 UCHAR PreviousTaskFile
[8];
174 UCHAR CurrentTaskFile
[8];
175 } ATA_PASS_THROUGH_EX
;
177 #define ATA_FLAGS_DRDY_REQUIRED 0x01
178 #define ATA_FLAGS_DATA_IN 0x02
179 #define ATA_FLAGS_DATA_OUT 0x04
180 #define ATA_FLAGS_48BIT_COMMAND 0x08
181 #define ATA_FLAGS_USE_DMA 0x10
182 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
184 #endif // IOCTL_ATA_PASS_THROUGH
186 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
187 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, SELECT_WIN_32_64(40, 48));
190 // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
192 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
193 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT
, 0x04d014);
194 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, SELECT_WIN_32_64(44, 56));
195 ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT
, SELECT_WIN_32_64(44, 56));
198 // SMART IOCTL via SCSI MINIPORT ioctl
200 #ifndef FILE_DEVICE_SCSI
201 #define FILE_DEVICE_SCSI 0x001b
204 #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
206 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
207 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
208 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
209 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
210 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
211 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
212 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
213 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
214 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
215 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
216 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
217 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
218 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
220 #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
222 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
223 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
226 // IOCTL_STORAGE_QUERY_PROPERTY
228 #ifndef IOCTL_STORAGE_QUERY_PROPERTY
230 #define IOCTL_STORAGE_QUERY_PROPERTY \
231 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
233 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
237 UCHAR DeviceTypeModifier
;
238 BOOLEAN RemovableMedia
;
239 BOOLEAN CommandQueueing
;
240 ULONG VendorIdOffset
;
241 ULONG ProductIdOffset
;
242 ULONG ProductRevisionOffset
;
243 ULONG SerialNumberOffset
;
244 STORAGE_BUS_TYPE BusType
;
245 ULONG RawPropertiesLength
;
246 UCHAR RawDeviceProperties
[1];
247 } STORAGE_DEVICE_DESCRIPTOR
;
249 typedef enum _STORAGE_QUERY_TYPE
{
250 PropertyStandardQuery
= 0,
253 PropertyQueryMaxDefined
254 } STORAGE_QUERY_TYPE
;
256 typedef enum _STORAGE_PROPERTY_ID
{
257 StorageDeviceProperty
= 0,
258 StorageAdapterProperty
,
259 StorageDeviceIdProperty
,
260 StorageDeviceUniqueIdProperty
,
261 StorageDeviceWriteCacheProperty
,
262 StorageMiniportProperty
,
263 StorageAccessAlignmentProperty
264 } STORAGE_PROPERTY_ID
;
266 typedef struct _STORAGE_PROPERTY_QUERY
{
267 STORAGE_PROPERTY_ID PropertyId
;
268 STORAGE_QUERY_TYPE QueryType
;
269 UCHAR AdditionalParameters
[1];
270 } STORAGE_PROPERTY_QUERY
;
272 #endif // IOCTL_STORAGE_QUERY_PROPERTY
274 ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY
, 0x002d1400);
275 ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR
, 36+1+3);
276 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY
, 8+1+3);
279 // IOCTL_STORAGE_QUERY_PROPERTY: Windows 10 enhancements
283 // enum STORAGE_PROPERTY_ID: new values
284 const STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty
= (STORAGE_PROPERTY_ID
)49;
285 const STORAGE_PROPERTY_ID StorageDeviceProtocolSpecificProperty
= (STORAGE_PROPERTY_ID
)50;
287 typedef enum _STORAGE_PROTOCOL_TYPE
{
288 ProtocolTypeUnknown
= 0,
293 } STORAGE_PROTOCOL_TYPE
;
295 typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE
{
296 NVMeDataTypeUnknown
= 0,
297 NVMeDataTypeIdentify
,
300 } STORAGE_PROTOCOL_NVME_DATA_TYPE
;
302 typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA
{
303 STORAGE_PROTOCOL_TYPE ProtocolType
;
305 ULONG ProtocolDataRequestValue
;
306 ULONG ProtocolDataRequestSubValue
;
307 ULONG ProtocolDataOffset
;
308 ULONG ProtocolDataLength
;
309 ULONG FixedProtocolReturnData
;
311 } STORAGE_PROTOCOL_SPECIFIC_DATA
;
313 ASSERT_SIZEOF(STORAGE_PROTOCOL_SPECIFIC_DATA
, 40);
318 // IOCTL_STORAGE_PREDICT_FAILURE
320 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE
, 0x002d1100);
321 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
324 // 3ware specific versions of SMART ioctl structs
326 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
330 typedef struct _GETVERSIONINPARAMS_EX
{
336 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
337 WORD wIdentifier
; // Vendor specific identifier
338 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
340 } GETVERSIONINPARAMS_EX
;
342 typedef struct _SENDCMDINPARAMS_EX
{
346 BYTE bPortNumber
; // 3ware specific: port number
347 WORD wIdentifier
; // Vendor specific identifier
350 } SENDCMDINPARAMS_EX
;
354 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONINPARAMS
));
355 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
360 #ifndef NVME_PASS_THROUGH_SRB_IO_CODE
362 #define NVME_SIG_STR "NvmeMini"
363 #define NVME_STORPORT_DRIVER 0xe000
365 #define NVME_PASS_THROUGH_SRB_IO_CODE \
366 CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
369 typedef struct _NVME_PASS_THROUGH_IOCTL
371 SRB_IO_CONTROL SrbIoCtrl
;
372 ULONG VendorSpecific
[6];
373 ULONG NVMeCmd
[16]; // Command DW[0...15]
374 ULONG CplEntry
[4]; // Completion DW[0...3]
375 ULONG Direction
; // 0=No, 1=Out, 2=In, 3=I/O
376 ULONG QueueId
; // 0=AdminQ
377 ULONG DataBufferLen
; // sizeof(DataBuffer) if Data In
379 ULONG ReturnBufferLen
; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
381 } NVME_PASS_THROUGH_IOCTL
;
384 #endif // NVME_PASS_THROUGH_SRB_IO_CODE
386 ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE
, (int)0xe0002000);
387 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, 152+1);
388 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
)+1);
393 ASSERT_SIZEOF(IOCTL_HEADER
, sizeof(SRB_IO_CONTROL
));
394 ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER
, 204);
395 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER
, 2080);
396 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER
, 168);
400 ASSERT_SIZEOF(SCSI_REQUEST_BLOCK
, SELECT_WIN_32_64(64, 88));
404 /////////////////////////////////////////////////////////////////////////////
406 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
409 #pragma warning(disable:4250)
412 static int is_permissive()
414 if (!failuretest_permissive
) {
415 pout("To continue, add one or more '-T permissive' options.\n");
418 failuretest_permissive
--;
422 // return number for drive letter, -1 on error
423 // "[A-Za-z]:([/\\][.]?)?" => 0-25
424 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
425 static int drive_letter(const char * s
)
427 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
429 && (!s
[2] || ( strchr("/\\\"", s
[2])
430 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
431 (s
[0] & 0x1f) - 1 : -1);
434 // Skip trailing "/dev/", do not allow "/dev/X:"
435 static const char * skipdev(const char * s
)
437 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
440 // "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
441 static int sdxy_to_phydrive(const char (& xy
)[2+1])
443 int phydrive
= xy
[0] - 'a';
445 phydrive
= (phydrive
+ 1) * ('z' - 'a' + 1) + (xy
[1] - 'a');
449 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
451 int srclen
= strcspn(src
, "\r\n");
453 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
454 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
456 if (i
< destsize
-1 && i
< srclen
)
461 /////////////////////////////////////////////////////////////////////////////
464 class win_smart_device
465 : virtual public /*implements*/ smart_device
469 : smart_device(never_called
),
470 m_fh(INVALID_HANDLE_VALUE
)
473 virtual ~win_smart_device() throw();
475 virtual bool is_open() const;
477 virtual bool close();
480 /// Set handle for open() in derived classes.
481 void set_fh(HANDLE fh
)
484 /// Return handle for derived classes.
485 HANDLE
get_fh() const
489 HANDLE m_fh
; ///< File handle
493 // Common routines for devices with HANDLEs
495 win_smart_device::~win_smart_device() throw()
497 if (m_fh
!= INVALID_HANDLE_VALUE
)
501 bool win_smart_device::is_open() const
503 return (m_fh
!= INVALID_HANDLE_VALUE
);
506 bool win_smart_device::close()
508 if (m_fh
== INVALID_HANDLE_VALUE
)
510 BOOL rc
= ::CloseHandle(m_fh
);
511 m_fh
= INVALID_HANDLE_VALUE
;
516 /////////////////////////////////////////////////////////////////////////////
518 #define SMART_CYL_LOW 0x4F
519 #define SMART_CYL_HI 0xC2
521 static void print_ide_regs(const IDEREGS
* r
, int out
)
523 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
524 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
525 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
528 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
530 pout(" Input : "); print_ide_regs(ri
, 0);
532 pout(" Output: "); print_ide_regs(ro
, 1);
536 /////////////////////////////////////////////////////////////////////////////
538 // call SMART_GET_VERSION, return device map or -1 on error
540 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
542 GETVERSIONINPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
543 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
546 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
547 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
549 pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
553 assert(num_out
== sizeof(GETVERSIONINPARAMS
));
555 if (ata_debugmode
> 1) {
556 pout(" SMART_GET_VERSION suceeded, bytes returned: %u\n"
557 " Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
558 (unsigned)num_out
, vers
.bVersion
, vers
.bRevision
,
559 (unsigned)vers
.fCapabilities
, vers
.bIDEDeviceMap
);
560 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
561 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
562 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, (unsigned)vers_ex
.dwDeviceMapEx
);
566 *ata_version_ex
= vers_ex
;
568 // TODO: Check vers.fCapabilities here?
569 return vers
.bIDEDeviceMap
;
573 // call SMART_* ioctl
575 static int smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
577 SENDCMDINPARAMS inpar
;
578 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
580 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
581 const SENDCMDOUTPARAMS
* outpar
;
583 unsigned int size_out
;
586 memset(&inpar
, 0, sizeof(inpar
));
587 inpar
.irDriveRegs
= *regs
;
589 // Older drivers may require bits 5 and 7 set
590 // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
591 inpar
.irDriveRegs
.bDriveHeadReg
|= 0xa0;
593 // Drive number 0-3 was required on Win9x/ME only
594 //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
595 //inpar.bDriveNumber = drive;
599 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
600 inpar_ex
.bPortNumber
= port
;
603 if (datasize
== 512) {
604 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
605 inpar
.cBufferSize
= size_out
= 512;
607 else if (datasize
== 0) {
608 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
609 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
610 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
611 // Note: cBufferSize must be 0 on Win9x
620 memset(&outbuf
, 0, sizeof(outbuf
));
622 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
623 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
624 // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
625 long err
= GetLastError();
626 if (ata_debugmode
&& (err
!= ERROR_INVALID_PARAMETER
|| ata_debugmode
> 1)) {
627 pout(" %s failed, Error=%ld\n", name
, err
);
628 print_ide_regs_io(regs
, NULL
);
630 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
631 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
632 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
635 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
637 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
639 if (outpar
->DriverStatus
.bDriverError
) {
641 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
642 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
643 print_ide_regs_io(regs
, NULL
);
645 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
649 if (ata_debugmode
> 1) {
650 pout(" %s suceeded, bytes returned: %u (buffer %u)\n", name
,
651 (unsigned)num_out
, (unsigned)outpar
->cBufferSize
);
652 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
653 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
657 memcpy(data
, outpar
->bBuffer
, 512);
658 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
659 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
660 memcpy(regs
, outpar
->bBuffer
, sizeof(IDEREGS
));
661 else { // Workaround for driver not returning regs
663 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
664 *regs
= inpar
.irDriveRegs
;
672 /////////////////////////////////////////////////////////////////////////////
673 // IDE PASS THROUGH (2000, XP, undocumented)
675 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
676 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
678 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
680 if (datasize
> 512) {
684 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
685 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
687 const unsigned char magic
= 0xcf;
695 buf
->DataBufferSize
= datasize
;
697 buf
->DataBuffer
[0] = magic
;
699 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
700 buf
, size
, buf
, size
, &num_out
, NULL
)) {
701 long err
= GetLastError();
703 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
704 print_ide_regs_io(regs
, NULL
);
706 VirtualFree(buf
, 0, MEM_RELEASE
);
707 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
712 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
714 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
715 print_ide_regs_io(regs
, &buf
->IdeReg
);
717 VirtualFree(buf
, 0, MEM_RELEASE
);
722 // Check and copy data
725 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
727 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
728 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
729 print_ide_regs_io(regs
, &buf
->IdeReg
);
731 VirtualFree(buf
, 0, MEM_RELEASE
);
735 memcpy(data
, buf
->DataBuffer
, datasize
);
738 if (ata_debugmode
> 1) {
739 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
740 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
741 print_ide_regs_io(regs
, &buf
->IdeReg
);
745 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
746 VirtualFree(buf
, 0, MEM_RELEASE
);
751 /////////////////////////////////////////////////////////////////////////////
752 // ATA PASS THROUGH (Win2003, XP SP2)
755 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
756 // transfer per command. Therefore, multi-sector transfers are only supported
757 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
758 // or READ/WRITE LOG EXT work only with single sector transfers.
759 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
761 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
763 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
765 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
768 ATA_PASS_THROUGH_EX apt
;
770 UCHAR ucDataBuf
[max_sectors
* 512];
771 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
773 const unsigned char magic
= 0xcf;
775 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
776 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
778 //ab.apt.TargetId = 0;
780 ab
.apt
.TimeOutValue
= 10;
781 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
782 ab
.apt
.DataBufferOffset
= size
;
785 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
789 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
790 ab
.apt
.DataTransferLength
= datasize
;
792 ab
.ucDataBuf
[0] = magic
;
794 else if (datasize
< 0) {
795 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
799 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
800 ab
.apt
.DataTransferLength
= -datasize
;
802 memcpy(ab
.ucDataBuf
, data
, -datasize
);
805 assert(ab
.apt
.AtaFlags
== 0);
806 assert(ab
.apt
.DataTransferLength
== 0);
809 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
810 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
811 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
815 *ptfregs
= *prev_regs
;
816 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
820 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
821 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
822 long err
= GetLastError();
824 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
825 print_ide_regs_io(regs
, NULL
);
827 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
832 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
834 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
835 print_ide_regs_io(regs
, ctfregs
);
841 // Check and copy data
844 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
846 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out
);
847 print_ide_regs_io(regs
, ctfregs
);
852 memcpy(data
, ab
.ucDataBuf
, datasize
);
855 if (ata_debugmode
> 1) {
856 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out
);
857 print_ide_regs_io(regs
, ctfregs
);
861 *prev_regs
= *ptfregs
;
867 /////////////////////////////////////////////////////////////////////////////
868 // SMART IOCTL via SCSI MINIPORT ioctl
870 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
871 // miniport driver (via SCSI port driver scsiport.sys).
872 // It can be used to skip the missing or broken handling of some SMART
873 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
875 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
878 DWORD code
= 0; const char * name
= 0;
879 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
880 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
882 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
883 case ATA_SMART_READ_VALUES
:
884 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
885 case ATA_SMART_READ_THRESHOLDS
:
886 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
887 case ATA_SMART_ENABLE
:
888 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
889 case ATA_SMART_DISABLE
:
890 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
891 case ATA_SMART_STATUS
:
892 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
893 case ATA_SMART_AUTOSAVE
:
894 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
895 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
896 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
897 case ATA_SMART_IMMEDIATE_OFFLINE
:
898 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
899 case ATA_SMART_AUTO_OFFLINE
:
900 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
901 case ATA_SMART_READ_LOG_SECTOR
:
902 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
903 case ATA_SMART_WRITE_LOG_SECTOR
:
904 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
916 SENDCMDOUTPARAMS out
;
920 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
921 memset(&sb
, 0, sizeof(sb
));
925 if (datasize
> (int)sizeof(sb
.space
)+1) {
931 else if (datasize
< 0) {
932 if (-datasize
> (int)sizeof(sb
.space
)+1) {
937 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
939 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
940 size
= sizeof(IDEREGS
);
943 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
944 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
945 sb
.srbc
.Timeout
= 60; // seconds
946 sb
.srbc
.ControlCode
= code
;
947 //sb.srbc.ReturnCode = 0;
948 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
949 sb
.params
.in
.irDriveRegs
= *regs
;
950 sb
.params
.in
.cBufferSize
= size
;
952 // Call miniport ioctl
953 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
955 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
956 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
957 long err
= GetLastError();
959 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
960 print_ide_regs_io(regs
, NULL
);
962 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
967 if (sb
.srbc
.ReturnCode
) {
969 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name
, (unsigned)sb
.srbc
.ReturnCode
);
970 print_ide_regs_io(regs
, NULL
);
976 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
978 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
979 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
980 print_ide_regs_io(regs
, NULL
);
982 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
986 if (ata_debugmode
> 1) {
987 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name
,
988 (unsigned)num_out
, (unsigned)sb
.params
.out
.cBufferSize
);
989 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
990 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
994 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
995 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
996 memcpy(regs
, sb
.params
.out
.bBuffer
, sizeof(IDEREGS
));
1002 /////////////////////////////////////////////////////////////////////////////
1003 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1005 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1008 SRB_IO_CONTROL srbc
;
1012 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1014 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1018 memset(&sb
, 0, sizeof(sb
));
1019 strncpy((char *)sb
.srbc
.Signature
, "<3ware>", sizeof(sb
.srbc
.Signature
));
1020 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1021 sb
.srbc
.Timeout
= 60; // seconds
1022 sb
.srbc
.ControlCode
= 0xA0000000;
1023 sb
.srbc
.ReturnCode
= 0;
1024 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1026 sb
.regs
.bReserved
= port
;
1029 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1030 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1031 long err
= GetLastError();
1032 if (ata_debugmode
) {
1033 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1034 print_ide_regs_io(regs
, NULL
);
1036 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1040 if (sb
.srbc
.ReturnCode
) {
1041 if (ata_debugmode
) {
1042 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb
.srbc
.ReturnCode
);
1043 print_ide_regs_io(regs
, NULL
);
1051 memcpy(data
, sb
.buffer
, datasize
);
1053 if (ata_debugmode
> 1) {
1054 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out
);
1055 print_ide_regs_io(regs
, &sb
.regs
);
1063 /////////////////////////////////////////////////////////////////////////////
1065 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1066 // 3DM/CLI "Rescan Controller" function does not to always update it.
1068 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1070 SRB_IO_CONTROL srbc
;
1071 memset(&srbc
, 0, sizeof(srbc
));
1072 strncpy((char *)srbc
.Signature
, "<3ware>", sizeof(srbc
.Signature
));
1073 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1074 srbc
.Timeout
= 60; // seconds
1075 srbc
.ControlCode
= 0xCC010014;
1076 srbc
.ReturnCode
= 0;
1080 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1081 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1082 long err
= GetLastError();
1084 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1085 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1088 if (srbc
.ReturnCode
) {
1090 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc
.ReturnCode
);
1094 if (ata_debugmode
> 1)
1095 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1100 /////////////////////////////////////////////////////////////////////////////
1101 // IOCTL_STORAGE_QUERY_PROPERTY
1103 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1104 STORAGE_DEVICE_DESCRIPTOR desc
;
1108 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1109 // (This works without admin rights)
1111 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1113 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
1114 memset(data
, 0, sizeof(*data
));
1117 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1118 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1119 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
1120 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
1125 if (ata_debugmode
> 1 || scsi_debugmode
> 1) {
1126 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1128 " Product: \"%s\"\n"
1129 " Revision: \"%s\"\n"
1131 " BusType: 0x%02x\n",
1132 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: "(null)"),
1133 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: "(null)"),
1134 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: "(null)"),
1135 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1142 /////////////////////////////////////////////////////////////////////////////
1143 // IOCTL_STORAGE_PREDICT_FAILURE
1145 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1146 // or -1 on error, opionally return VendorSpecific data.
1147 // (This works without admin rights)
1149 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1151 STORAGE_PREDICT_FAILURE pred
;
1152 memset(&pred
, 0, sizeof(pred
));
1155 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
1156 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
1157 if (ata_debugmode
> 1)
1158 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
1163 if (ata_debugmode
> 1) {
1164 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1165 " PredictFailure: 0x%08x\n"
1166 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1167 (unsigned)pred
.PredictFailure
,
1168 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
1169 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
1173 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
1174 return (!pred
.PredictFailure
? 0 : 1);
1178 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1179 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
1181 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1182 if (storage_query_property_ioctl(hdevice
, &data
))
1185 memset(id
, 0, sizeof(*id
));
1187 // Some drivers split ATA model string into VendorId and ProductId,
1188 // others return it as ProductId only.
1189 char model
[sizeof(id
->model
) + 1] = "";
1192 if (data
.desc
.VendorIdOffset
) {
1193 for ( ;i
< sizeof(model
)-1 && data
.raw
[data
.desc
.VendorIdOffset
+i
]; i
++)
1194 model
[i
] = data
.raw
[data
.desc
.VendorIdOffset
+i
];
1197 if (data
.desc
.ProductIdOffset
) {
1198 while (i
> 1 && model
[i
-2] == ' ') // Keep last blank from VendorId
1200 // Ignore VendorId "ATA"
1201 if (i
<= 4 && !strncmp(model
, "ATA", 3) && (i
== 3 || model
[3] == ' '))
1203 for (unsigned j
= 0; i
< sizeof(model
)-1 && data
.raw
[data
.desc
.ProductIdOffset
+j
]; i
++, j
++)
1204 model
[i
] = data
.raw
[data
.desc
.ProductIdOffset
+j
];
1207 while (i
> 0 && model
[i
-1] == ' ')
1210 copy_swapped(id
->model
, model
, sizeof(id
->model
));
1212 if (data
.desc
.ProductRevisionOffset
)
1213 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
1215 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1216 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1220 // Get Serial Number in IDENTIFY from WMI
1221 static bool get_serial_from_wmi(int drive
, ata_identify_device
* id
)
1223 bool debug
= (ata_debugmode
> 1);
1226 if (!ws
.connect()) {
1228 pout("WMI connect failed\n");
1233 if (!ws
.query1(wo
, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
1234 "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive
))
1237 std::string serial
= wo
.get_str("SerialNumber");
1239 pout(" WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive
, wo
.get_str("Model").c_str(), serial
.c_str());
1241 copy_swapped(id
->serial_no
, serial
.c_str(), sizeof(id
->serial_no
));
1246 /////////////////////////////////////////////////////////////////////////////
1247 // USB ID detection using WMI
1249 // Get USB ID for a physical or logical drive number
1250 static bool get_usb_id(int phydrive
, int logdrive
,
1251 unsigned short & vendor_id
,
1252 unsigned short & product_id
)
1254 bool debug
= (scsi_debugmode
> 1);
1257 if (!ws
.connect()) {
1259 pout("WMI connect failed\n");
1267 if (0 <= logdrive
&& logdrive
<= 'Z'-'A') {
1268 // Drive letter -> Partition info
1269 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition",
1273 std::string partid
= wo
.get_str("DeviceID");
1275 pout("%c: --> \"%s\" -->\n", 'A'+logdrive
, partid
.c_str());
1277 // Partition ID -> Physical drive info
1278 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
1282 name
= wo
.get_str("Model");
1284 pout("%s --> \"%s\":\n", wo
.get_str("DeviceID").c_str(), name
.c_str());
1287 else if (phydrive
>= 0) {
1288 // Physical drive number -> Physical drive info
1289 if (!ws
.query1(wo
, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive
))
1292 name
= wo
.get_str("Model");
1294 pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive
, name
.c_str());
1300 // Get USB_CONTROLLER -> DEVICE associations
1302 if (!ws
.query(we
, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
1305 unsigned short usb_venid
= 0, prev_usb_venid
= 0;
1306 unsigned short usb_proid
= 0, prev_usb_proid
= 0;
1307 std::string prev_usb_ant
;
1308 std::string prev_ant
, ant
, dep
;
1310 const regular_expression
regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED
);
1312 while (we
.next(wo
)) {
1314 // Find next 'USB_CONTROLLER, DEVICE' pair
1315 ant
= wo
.get_str("Antecedent");
1316 dep
= wo
.get_str("Dependent");
1318 if (debug
&& ant
!= prev_ant
)
1319 pout(" %s:\n", ant
.c_str());
1322 regmatch_t match
[2];
1323 if (!(regex
.execute(dep
.c_str(), 2, match
) && match
[1].rm_so
>= 0)) {
1325 pout(" | (\"%s\")\n", dep
.c_str());
1329 std::string
devid(dep
.c_str()+match
[1].rm_so
, match
[1].rm_eo
-match
[1].rm_so
);
1331 if (str_starts_with(devid
, "USB\\\\VID_")) {
1332 // USB bridge entry, save CONTROLLER, ID
1334 if (!(sscanf(devid
.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
1335 &prev_usb_venid
, &prev_usb_proid
, &nc
) == 2 && nc
== 9+4+5+4)) {
1336 prev_usb_venid
= prev_usb_proid
= 0;
1340 pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid
.c_str(), prev_usb_venid
, prev_usb_proid
);
1342 else if (str_starts_with(devid
, "USBSTOR\\\\") || str_starts_with(devid
, "SCSI\\\\")) {
1343 // USBSTORage or SCSI device found
1345 pout(" +--> \"%s\"\n", devid
.c_str());
1349 if (!ws
.query1(wo2
, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid
.c_str()))
1351 std::string name2
= wo2
.get_str("Name");
1353 // Continue if not name of physical disk drive
1354 if (name2
!= name
) {
1356 pout(" +---> (\"%s\")\n", name2
.c_str());
1360 // Fail if previous USB bridge is associated to other controller or ID is unknown
1361 if (!(ant
== prev_usb_ant
&& prev_usb_venid
)) {
1363 pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2
.c_str());
1367 // Handle multiple devices with same name
1369 // Fail if multiple devices with same name have different USB bridge types
1370 if (!(usb_venid
== prev_usb_venid
&& usb_proid
== prev_usb_proid
)) {
1372 pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2
.c_str());
1378 usb_venid
= prev_usb_venid
;
1379 usb_proid
= prev_usb_proid
;
1381 pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2
.c_str(), usb_venid
, usb_proid
);
1383 // Continue to check for duplicate names ...
1387 pout(" | \"%s\"\n", devid
.c_str());
1394 vendor_id
= usb_venid
;
1395 product_id
= usb_proid
;
1401 /////////////////////////////////////////////////////////////////////////////
1403 // Call GetDevicePowerState()
1404 // returns: 1=active, 0=standby, -1=error
1405 // (This would also work for SCSI drives)
1407 static int get_device_power_state(HANDLE hdevice
)
1410 if (!GetDevicePowerState(hdevice
, &state
)) {
1411 long err
= GetLastError();
1413 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
1414 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1415 // TODO: This may not work as expected on transient errors,
1416 // because smartd interprets -1 as SLEEP mode regardless of errno.
1420 if (ata_debugmode
> 1)
1421 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
1426 /////////////////////////////////////////////////////////////////////////////
1429 class win_ata_device
1430 : public /*implements*/ ata_device
,
1431 public /*extends*/ win_smart_device
1434 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
1436 virtual ~win_ata_device() throw();
1438 virtual bool open();
1440 virtual bool is_powered_down();
1442 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
1444 virtual bool ata_identify_is_cached() const;
1447 bool open(bool query_device
);
1449 bool open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
);
1451 std::string m_options
;
1452 bool m_usr_options
; // options set by user?
1453 bool m_admin
; // open with admin access?
1454 int m_phydrive
; // PhysicalDriveN or -1
1455 bool m_id_is_cached
; // ata_identify_is_cached() return value.
1456 bool m_is_3ware
; // LSI/3ware controller detected?
1457 int m_port
; // LSI/3ware port
1458 int m_smartver_state
;
1462 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1463 : smart_device(intf
, dev_name
, "ata", req_type
),
1464 m_usr_options(false),
1467 m_id_is_cached(false),
1474 win_ata_device::~win_ata_device() throw()
1478 // Get default ATA device options
1480 static const char * ata_get_def_options()
1482 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
1483 // STORAGE_*, SCSI_MINIPORT_*
1488 bool win_ata_device::open()
1490 // Open device for r/w operations
1494 bool win_ata_device::open(bool query_device
)
1496 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
1497 // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
1498 char drive
[2+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
1499 if ( sscanf(name
, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive
, &n1
, options
, &n2
) >= 1
1500 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1501 return open(sdxy_to_phydrive(drive
), -1, options
, -1, query_device
);
1503 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
1504 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
1506 if ( sscanf(name
, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
1507 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1508 return open(sdxy_to_phydrive(drive
), -1, options
, port
, query_device
);
1510 // pd<m>,N => Physical drive <m>, RAID port N
1511 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
1512 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
1513 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
1514 return open(phydrive
, -1, "", (int)port
, query_device
);
1516 // [a-zA-Z]: => Physical drive behind logical drive 0-25
1517 int logdrive
= drive_letter(name
);
1518 if (logdrive
>= 0) {
1519 return open(-1, logdrive
, "", -1, query_device
);
1522 return set_err(EINVAL
);
1526 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
)
1530 if (0 <= phydrive
&& phydrive
<= 255)
1531 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive
= phydrive
));
1532 else if (0 <= logdrive
&& logdrive
<= 'Z'-'A')
1533 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
1535 return set_err(ENOENT
);
1538 HANDLE h
= INVALID_HANDLE_VALUE
;
1539 if (!(*options
&& !options
[strspn(options
, "fp")]) && !query_device
) {
1540 // Open with admin rights
1542 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
1543 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1544 NULL
, OPEN_EXISTING
, 0, 0);
1546 if (h
== INVALID_HANDLE_VALUE
) {
1547 // Open without admin rights
1549 h
= CreateFileA(devpath
, 0,
1550 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1551 NULL
, OPEN_EXISTING
, 0, 0);
1553 if (h
== INVALID_HANDLE_VALUE
) {
1554 long err
= GetLastError();
1555 if (err
== ERROR_FILE_NOT_FOUND
)
1556 set_err(ENOENT
, "%s: not found", devpath
);
1557 else if (err
== ERROR_ACCESS_DENIED
)
1558 set_err(EACCES
, "%s: access denied", devpath
);
1560 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
1565 // Warn once if admin rights are missing
1566 if (!m_admin
&& !query_device
) {
1567 static bool noadmin_warning
= false;
1568 if (!noadmin_warning
) {
1569 pout("Warning: Limited functionality due to missing admin rights\n");
1570 noadmin_warning
= true;
1574 if (ata_debugmode
> 1)
1575 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
1577 m_usr_options
= false;
1579 // Save user options
1580 m_options
= options
; m_usr_options
= true;
1583 // RAID: SMART_* and SCSI_MINIPORT
1586 // Set default options according to Windows version
1587 static const char * def_options
= ata_get_def_options();
1588 m_options
= def_options
;
1591 // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
1596 // 3ware RAID: Get port map
1597 GETVERSIONINPARAMS_EX vers_ex
;
1598 int devmap
= smart_get_version(h
, &vers_ex
);
1600 // 3ware RAID if vendor id present
1601 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1603 unsigned long portmap
= 0;
1604 if (port
>= 0 && devmap
>= 0) {
1605 // 3ware RAID: check vendor id
1607 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
1608 "This is no 3ware 9000 controller or driver has no SMART support.\n",
1609 vers_ex
.wIdentifier
);
1613 portmap
= vers_ex
.dwDeviceMapEx
;
1616 pout("%s: ATA driver has no SMART support\n", devpath
);
1617 if (!is_permissive()) {
1619 return set_err(ENOSYS
);
1622 m_smartver_state
= 1;
1625 // 3ware RAID: update devicemap first
1627 if (!update_3ware_devicemap_ioctl(h
)) {
1628 if ( smart_get_version(h
, &vers_ex
) >= 0
1629 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
1630 portmap
= vers_ex
.dwDeviceMapEx
;
1632 // Check port existence
1633 if (!(portmap
& (1L << port
))) {
1634 if (!is_permissive()) {
1636 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
1645 /////////////////////////////////////////////////////////////////////////////
1647 // Query OS if device is powered up or down.
1648 bool win_ata_device::is_powered_down()
1650 // To check power mode, we open device for query operations only.
1651 // Opening for SMART r/w operations can already spin up the disk.
1652 bool self_open
= !is_open();
1656 int rc
= get_device_power_state(get_fh());
1662 /////////////////////////////////////////////////////////////////////////////
1664 // Interface to ATA devices
1665 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
1667 // No multi-sector support for now, see above
1668 // warning about IOCTL_ATA_PASS_THROUGH
1669 if (!ata_cmd_is_supported(in
,
1670 ata_device::supports_data_out
|
1671 ata_device::supports_output_regs
|
1672 ata_device::supports_48bit
)
1676 // 3ware RAID: SMART DISABLE without port number disables SMART functions
1677 if ( m_is_3ware
&& m_port
< 0
1678 && in
.in_regs
.command
== ATA_SMART_CMD
1679 && in
.in_regs
.features
== ATA_SMART_DISABLE
)
1680 return set_err(ENOSYS
, "SMART DISABLE requires 3ware port number");
1682 // Determine ioctl functions valid for this ATA cmd
1683 const char * valid_options
= 0;
1685 switch (in
.in_regs
.command
) {
1686 case ATA_IDENTIFY_DEVICE
:
1687 case ATA_IDENTIFY_PACKET_DEVICE
:
1688 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1689 // and SCSI_MINIPORT_* if requested by user
1690 valid_options
= (m_usr_options
? "saimf" : "saif");
1693 case ATA_CHECK_POWER_MODE
:
1694 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
1695 valid_options
= "pai3";
1699 switch (in
.in_regs
.features
) {
1700 case ATA_SMART_READ_VALUES
:
1701 case ATA_SMART_READ_THRESHOLDS
:
1702 case ATA_SMART_AUTOSAVE
:
1703 case ATA_SMART_ENABLE
:
1704 case ATA_SMART_DISABLE
:
1705 case ATA_SMART_AUTO_OFFLINE
:
1706 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1707 // and SCSI_MINIPORT_* if requested by user
1708 valid_options
= (m_usr_options
? "saimf" : "saif");
1711 case ATA_SMART_IMMEDIATE_OFFLINE
:
1712 // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
1713 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ ?
1717 case ATA_SMART_READ_LOG_SECTOR
:
1718 // SMART_RCV_DRIVE_DATA does not support READ_LOG
1719 // Try SCSI_MINIPORT also to skip buggy class driver
1720 // SMART functions do not support multi sector I/O.
1722 valid_options
= (m_usr_options
? "saim3" : "aim3");
1724 valid_options
= "a";
1727 case ATA_SMART_WRITE_LOG_SECTOR
:
1728 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
1729 // but SCSI_MINIPORT_* only if requested by user and single sector.
1730 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
1733 case ATA_SMART_STATUS
:
1734 valid_options
= (m_usr_options
? "saimf" : "saif");
1738 // Unknown SMART command, handle below
1744 // Other ATA command, handle below
1748 if (!valid_options
) {
1749 // No special ATA command found above, select a generic pass through ioctl.
1750 if (!( in
.direction
== ata_cmd_in::no_data
1751 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
1752 || in
.in_regs
.is_48bit_cmd() )
1753 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
1754 valid_options
= "a";
1756 // ATA/IDE_PASS_THROUGH
1757 valid_options
= "ai";
1761 // Restrict to IOCTL_STORAGE_*
1762 if (strchr(valid_options
, 'f'))
1763 valid_options
= "f";
1764 else if (strchr(valid_options
, 'p'))
1765 valid_options
= "p";
1767 return set_err(ENOSYS
, "Function requires admin rights");
1771 IDEREGS regs
, prev_regs
;
1773 const ata_in_regs
& lo
= in
.in_regs
;
1774 regs
.bFeaturesReg
= lo
.features
;
1775 regs
.bSectorCountReg
= lo
.sector_count
;
1776 regs
.bSectorNumberReg
= lo
.lba_low
;
1777 regs
.bCylLowReg
= lo
.lba_mid
;
1778 regs
.bCylHighReg
= lo
.lba_high
;
1779 regs
.bDriveHeadReg
= lo
.device
;
1780 regs
.bCommandReg
= lo
.command
;
1783 if (in
.in_regs
.is_48bit_cmd()) {
1784 const ata_in_regs
& hi
= in
.in_regs
.prev
;
1785 prev_regs
.bFeaturesReg
= hi
.features
;
1786 prev_regs
.bSectorCountReg
= hi
.sector_count
;
1787 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
1788 prev_regs
.bCylLowReg
= hi
.lba_mid
;
1789 prev_regs
.bCylHighReg
= hi
.lba_high
;
1790 prev_regs
.bDriveHeadReg
= hi
.device
;
1791 prev_regs
.bCommandReg
= hi
.command
;
1792 prev_regs
.bReserved
= 0;
1795 // Set data direction
1798 switch (in
.direction
) {
1799 case ata_cmd_in::no_data
:
1801 case ata_cmd_in::data_in
:
1802 datasize
= (int)in
.size
;
1803 data
= (char *)in
.buffer
;
1805 case ata_cmd_in::data_out
:
1806 datasize
= -(int)in
.size
;
1807 data
= (char *)in
.buffer
;
1810 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
1815 // Try all valid ioctls in the order specified in m_options
1816 bool powered_up
= false;
1817 bool out_regs_set
= false;
1818 bool id_is_cached
= false;
1819 const char * options
= m_options
.c_str();
1821 for (int i
= 0; ; i
++) {
1822 char opt
= options
[i
];
1825 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
1826 // Power up reported by GetDevicePowerState() and no ioctl available
1827 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
1828 regs
.bSectorCountReg
= 0xff;
1829 out_regs_set
= true;
1833 return set_err(ENOSYS
);
1835 if (!strchr(valid_options
, opt
))
1836 // Invalid for this command
1840 assert( datasize
== 0 || datasize
== 512
1841 || (datasize
== -512 && strchr("am", opt
))
1842 || (datasize
> 512 && opt
== 'a'));
1847 // call SMART_GET_VERSION once for each drive
1848 if (m_smartver_state
> 1) {
1849 rc
= -1; errno
= ENOSYS
;
1852 if (!m_smartver_state
) {
1853 assert(m_port
== -1);
1854 GETVERSIONINPARAMS_EX vers_ex
;
1855 if (smart_get_version(get_fh(), &vers_ex
) < 0) {
1856 if (!failuretest_permissive
) {
1857 m_smartver_state
= 2;
1858 rc
= -1; errno
= ENOSYS
;
1861 failuretest_permissive
--;
1864 // 3ware RAID if vendor id present
1865 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1868 m_smartver_state
= 1;
1870 rc
= smart_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1871 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
1872 id_is_cached
= (m_port
< 0); // Not cached by 3ware driver
1875 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
1876 id_is_cached
= (m_port
< 0);
1879 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
1880 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
1882 out_regs_set
= true;
1885 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
1886 out_regs_set
= true;
1889 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
1890 ata_identify_device
* id
= reinterpret_cast<ata_identify_device
*>(data
);
1891 rc
= get_identify_from_device_property(get_fh(), id
);
1892 if (rc
== 0 && m_phydrive
>= 0)
1893 get_serial_from_wmi(m_phydrive
, id
);
1894 id_is_cached
= true;
1896 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
1897 case ATA_SMART_READ_VALUES
:
1898 rc
= storage_predict_failure_ioctl(get_fh(), data
);
1902 case ATA_SMART_ENABLE
:
1905 case ATA_SMART_STATUS
:
1906 rc
= storage_predict_failure_ioctl(get_fh());
1908 // Good SMART status
1909 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
1913 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
1918 errno
= ENOSYS
; rc
= -1;
1921 errno
= ENOSYS
; rc
= -1;
1925 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1926 out_regs_set
= true;
1929 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
1930 rc
= get_device_power_state(get_fh());
1932 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
1933 // spin up the drive => simulate ATA result STANDBY.
1934 regs
.bSectorCountReg
= 0x00;
1935 out_regs_set
= true;
1938 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
1939 // only if it is selected by the device driver => try a passthrough ioctl to get the
1940 // actual mode, if none available simulate ACTIVE/IDLE.
1942 rc
= -1; errno
= ENOSYS
;
1948 // Working ioctl found
1951 if (errno
!= ENOSYS
)
1952 // Abort on I/O error
1953 return set_err(errno
);
1955 out_regs_set
= false;
1956 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
1959 // Return IDEREGS if set
1961 ata_out_regs
& lo
= out
.out_regs
;
1962 lo
.error
= regs
.bFeaturesReg
;
1963 lo
.sector_count
= regs
.bSectorCountReg
;
1964 lo
.lba_low
= regs
.bSectorNumberReg
;
1965 lo
.lba_mid
= regs
.bCylLowReg
;
1966 lo
.lba_high
= regs
.bCylHighReg
;
1967 lo
.device
= regs
.bDriveHeadReg
;
1968 lo
.status
= regs
.bCommandReg
;
1969 if (in
.in_regs
.is_48bit_cmd()) {
1970 ata_out_regs
& hi
= out
.out_regs
.prev
;
1971 hi
.sector_count
= prev_regs
.bSectorCountReg
;
1972 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
1973 hi
.lba_mid
= prev_regs
.bCylLowReg
;
1974 hi
.lba_high
= prev_regs
.bCylHighReg
;
1978 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
1979 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
1980 // Update ata_identify_is_cached() result according to ioctl used.
1981 m_id_is_cached
= id_is_cached
;
1986 // Return true if OS caches the ATA identify sector
1987 bool win_ata_device::ata_identify_is_cached() const
1989 return m_id_is_cached
;
1993 //////////////////////////////////////////////////////////////////////
1997 : virtual public /*extends*/ smart_device
2000 enum { max_number_of_ports
= 32 };
2002 /// Get bitmask of used ports
2003 unsigned get_ports_used();
2007 : smart_device(never_called
)
2008 { memset(&m_phy_ent
, 0, sizeof(m_phy_ent
)); }
2010 typedef signed char port_2_index_map
[max_number_of_ports
];
2012 /// Get phy info and port mapping, return #ports or -1 on error
2013 int get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
, port_2_index_map
& p2i
);
2015 /// Select physical drive
2016 bool select_port(int port
);
2018 /// Get info for selected physical drive
2019 const CSMI_SAS_PHY_ENTITY
& get_phy_ent() const
2020 { return m_phy_ent
; }
2022 /// Call platform-specific CSMI ioctl
2023 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2024 unsigned csmi_bufsiz
) = 0;
2027 CSMI_SAS_PHY_ENTITY m_phy_ent
; ///< CSMI info for this phy
2031 /////////////////////////////////////////////////////////////////////////////
2033 int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
, port_2_index_map
& p2i
)
2035 // max_number_of_ports must match CSMI_SAS_PHY_INFO.Phy[] array size
2036 typedef char ASSERT_phy_info_size
[
2037 (int)(sizeof(phy_info
.Phy
) / sizeof(phy_info
.Phy
[0])) == max_number_of_ports
? 1 : -1]
2040 // Get driver info to check CSMI support
2041 CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf
;
2042 memset(&driver_info_buf
, 0, sizeof(driver_info_buf
));
2043 if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO
, &driver_info_buf
.IoctlHeader
, sizeof(driver_info_buf
)))
2046 if (scsi_debugmode
> 1) {
2047 const CSMI_SAS_DRIVER_INFO
& driver_info
= driver_info_buf
.Information
;
2048 pout("CSMI_SAS_DRIVER_INFO:\n");
2049 pout(" Name: \"%.81s\"\n", driver_info
.szName
);
2050 pout(" Description: \"%.81s\"\n", driver_info
.szDescription
);
2051 pout(" Revision: %d.%d\n", driver_info
.usMajorRevision
, driver_info
.usMinorRevision
);
2055 CSMI_SAS_PHY_INFO_BUFFER phy_info_buf
;
2056 memset(&phy_info_buf
, 0, sizeof(phy_info_buf
));
2057 if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO
, &phy_info_buf
.IoctlHeader
, sizeof(phy_info_buf
)))
2060 phy_info
= phy_info_buf
.Information
;
2062 if (phy_info
.bNumberOfPhys
> max_number_of_ports
) {
2063 set_err(EIO
, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info
.bNumberOfPhys
);
2067 // Create port -> index map
2069 // Phy[i].Value 9.x 10.4 14.8 15.2
2070 // ---------------------------------------------------
2071 // bPortIdentifier 0xff 0xff port 0x00
2072 // Identify.bPhyIdentifier port port port port
2073 // Attached.bPhyIdentifier 0x00 0x00 0x00 port
2074 // Empty ports in Phy[]? no ? yes yes
2076 int number_of_ports
;
2077 for (int mode
= 0; ; mode
++) {
2078 for (int i
= 0; i
< max_number_of_ports
; i
++)
2081 number_of_ports
= 0;
2083 for (int i
= 0; i
< max_number_of_ports
; i
++) {
2084 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2085 if (pe
.Identify
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2088 // Use a bPhyIdentifier or the bPortIdentifier if unique,
2089 // otherwise use table index
2092 case 0: port
= pe
.Attached
.bPhyIdentifier
; break;
2093 case 1: port
= pe
.Identify
.bPhyIdentifier
; break;
2094 case 2: port
= pe
.bPortIdentifier
; break;
2095 default: port
= i
; break;
2097 if (!(port
< max_number_of_ports
&& p2i
[port
] == -1)) {
2103 if (number_of_ports
<= port
)
2104 number_of_ports
= port
+ 1;
2107 if (unique
|| mode
> 2)
2111 if (scsi_debugmode
> 1) {
2112 pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info
.bNumberOfPhys
);
2113 for (int i
= 0; i
< max_number_of_ports
; i
++) {
2114 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2115 const CSMI_SAS_IDENTIFY
& id
= pe
.Identify
, & at
= pe
.Attached
;
2116 if (id
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2120 for (int p
= 0; p
< max_number_of_ports
&& port
< 0; p
++) {
2125 pout("Phy[%d] Port: %d\n", i
, port
);
2126 pout(" Type: 0x%02x, 0x%02x\n", id
.bDeviceType
, at
.bDeviceType
);
2127 pout(" InitProto: 0x%02x, 0x%02x\n", id
.bInitiatorPortProtocol
, at
.bInitiatorPortProtocol
);
2128 pout(" TargetProto: 0x%02x, 0x%02x\n", id
.bTargetPortProtocol
, at
.bTargetPortProtocol
);
2129 pout(" PortIdent: 0x%02x\n", pe
.bPortIdentifier
);
2130 pout(" PhyIdent: 0x%02x, 0x%02x\n", id
.bPhyIdentifier
, at
.bPhyIdentifier
);
2131 const unsigned char * b
= id
.bSASAddress
;
2132 pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ",
2133 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2135 pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
2136 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2140 return number_of_ports
;
2143 unsigned csmi_device::get_ports_used()
2145 CSMI_SAS_PHY_INFO phy_info
;
2146 port_2_index_map p2i
;
2147 int number_of_ports
= get_phy_info(phy_info
, p2i
);
2148 if (number_of_ports
< 0)
2151 unsigned ports_used
= 0;
2152 for (int p
= 0; p
< max_number_of_ports
; p
++) {
2156 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2157 if (pe
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2159 switch (pe
.Attached
.bTargetPortProtocol
) {
2160 case CSMI_SAS_PROTOCOL_SATA
:
2161 case CSMI_SAS_PROTOCOL_STP
:
2167 ports_used
|= (1 << p
);
2173 bool csmi_device::select_port(int port
)
2175 if (!(0 <= port
&& port
< max_number_of_ports
))
2176 return set_err(EINVAL
, "Invalid port number %d", port
);
2178 CSMI_SAS_PHY_INFO phy_info
;
2179 port_2_index_map p2i
;
2180 int number_of_ports
= get_phy_info(phy_info
, p2i
);
2181 if (number_of_ports
< 0)
2184 int port_index
= p2i
[port
];
2185 if (port_index
< 0) {
2186 if (port
< number_of_ports
)
2187 return set_err(ENOENT
, "Port %d is disabled", port
);
2189 return set_err(ENOENT
, "Port %d does not exist (#ports: %d)", port
,
2193 const CSMI_SAS_PHY_ENTITY
& phy_ent
= phy_info
.Phy
[port_index
];
2194 if (phy_ent
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2195 return set_err(ENOENT
, "No device on port %d", port
);
2197 switch (phy_ent
.Attached
.bTargetPortProtocol
) {
2198 case CSMI_SAS_PROTOCOL_SATA
:
2199 case CSMI_SAS_PROTOCOL_STP
:
2202 return set_err(ENOENT
, "No SATA device on port %d (protocol: %d)",
2203 port
, phy_ent
.Attached
.bTargetPortProtocol
);
2206 m_phy_ent
= phy_ent
;
2211 //////////////////////////////////////////////////////////////////////
2214 class csmi_ata_device
2215 : virtual public /*extends*/ csmi_device
,
2216 virtual public /*implements*/ ata_device
2219 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
2223 : smart_device(never_called
) { }
2227 //////////////////////////////////////////////////////////////////////
2229 bool csmi_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2231 if (!ata_cmd_is_supported(in
,
2232 ata_device::supports_data_out
|
2233 ata_device::supports_output_regs
|
2234 ata_device::supports_multi_sector
|
2235 ata_device::supports_48bit
,
2240 // Create buffer with appropriate size
2241 raw_buffer
pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER
) + in
.size
);
2242 CSMI_SAS_STP_PASSTHRU_BUFFER
* pthru_buf
= (CSMI_SAS_STP_PASSTHRU_BUFFER
*)pthru_raw_buf
.data();
2244 // Set addresses from Phy info
2245 CSMI_SAS_STP_PASSTHRU
& pthru
= pthru_buf
->Parameters
;
2246 const CSMI_SAS_PHY_ENTITY
& phy_ent
= get_phy_ent();
2247 pthru
.bPhyIdentifier
= phy_ent
.Identify
.bPhyIdentifier
;
2248 pthru
.bPortIdentifier
= phy_ent
.bPortIdentifier
;
2249 memcpy(pthru
.bDestinationSASAddress
, phy_ent
.Attached
.bSASAddress
,
2250 sizeof(pthru
.bDestinationSASAddress
));
2251 pthru
.bConnectionRate
= CSMI_SAS_LINK_RATE_NEGOTIATED
;
2253 // Set transfer mode
2254 switch (in
.direction
) {
2255 case ata_cmd_in::no_data
:
2256 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_UNSPECIFIED
;
2258 case ata_cmd_in::data_in
:
2259 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_READ
;
2260 pthru
.uDataLength
= in
.size
;
2262 case ata_cmd_in::data_out
:
2263 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_WRITE
;
2264 pthru
.uDataLength
= in
.size
;
2265 memcpy(pthru_buf
->bDataBuffer
, in
.buffer
, in
.size
);
2268 return set_err(EINVAL
, "csmi_ata_device::ata_pass_through: invalid direction=%d",
2272 // Set host-to-device FIS
2274 unsigned char * fis
= pthru
.bCommandFIS
;
2275 const ata_in_regs
& lo
= in
.in_regs
;
2276 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2277 fis
[ 0] = 0x27; // Type: host-to-device FIS
2278 fis
[ 1] = 0x80; // Bit7: Update command register
2279 fis
[ 2] = lo
.command
;
2280 fis
[ 3] = lo
.features
;
2281 fis
[ 4] = lo
.lba_low
;
2282 fis
[ 5] = lo
.lba_mid
;
2283 fis
[ 6] = lo
.lba_high
;
2284 fis
[ 7] = lo
.device
;
2285 fis
[ 8] = hi
.lba_low
;
2286 fis
[ 9] = hi
.lba_mid
;
2287 fis
[10] = hi
.lba_high
;
2288 fis
[11] = hi
.features
;
2289 fis
[12] = lo
.sector_count
;
2290 fis
[13] = hi
.sector_count
;
2294 if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU
, &pthru_buf
->IoctlHeader
, pthru_raw_buf
.size())) {
2298 // Get device-to-host FIS
2300 const unsigned char * fis
= pthru_buf
->Status
.bStatusFIS
;
2301 ata_out_regs
& lo
= out
.out_regs
;
2302 lo
.status
= fis
[ 2];
2304 lo
.lba_low
= fis
[ 4];
2305 lo
.lba_mid
= fis
[ 5];
2306 lo
.lba_high
= fis
[ 6];
2307 lo
.device
= fis
[ 7];
2308 lo
.sector_count
= fis
[12];
2309 if (in
.in_regs
.is_48bit_cmd()) {
2310 ata_out_regs
& hi
= out
.out_regs
.prev
;
2311 hi
.lba_low
= fis
[ 8];
2312 hi
.lba_mid
= fis
[ 9];
2313 hi
.lba_high
= fis
[10];
2314 hi
.sector_count
= fis
[13];
2319 if (in
.direction
== ata_cmd_in::data_in
)
2320 // TODO: Check ptru_buf->Status.uDataBytes
2321 memcpy(in
.buffer
, pthru_buf
->bDataBuffer
, in
.size
);
2327 //////////////////////////////////////////////////////////////////////
2330 class win_csmi_device
2331 : public /*implements*/ csmi_ata_device
2334 win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2335 const char * req_type
);
2337 virtual ~win_csmi_device() throw();
2339 virtual bool open();
2341 virtual bool close();
2343 virtual bool is_open() const;
2348 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2349 unsigned csmi_bufsiz
);
2352 HANDLE m_fh
; ///< Controller device handle
2353 int m_port
; ///< Port number
2357 //////////////////////////////////////////////////////////////////////
2359 win_csmi_device::win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2360 const char * req_type
)
2361 : smart_device(intf
, dev_name
, "ata", req_type
),
2362 m_fh(INVALID_HANDLE_VALUE
), m_port(-1)
2366 win_csmi_device::~win_csmi_device() throw()
2368 if (m_fh
!= INVALID_HANDLE_VALUE
)
2372 bool win_csmi_device::is_open() const
2374 return (m_fh
!= INVALID_HANDLE_VALUE
);
2377 bool win_csmi_device::close()
2379 if (m_fh
== INVALID_HANDLE_VALUE
)
2381 BOOL rc
= CloseHandle(m_fh
);
2382 m_fh
= INVALID_HANDLE_VALUE
;
2387 bool win_csmi_device::open_scsi()
2390 unsigned contr_no
= ~0, port
= ~0; int nc
= -1;
2391 const char * name
= skipdev(get_dev_name());
2392 if (!( sscanf(name
, "csmi%u,%u%n", &contr_no
, &port
, &nc
) >= 0
2393 && nc
== (int)strlen(name
) && contr_no
<= 9 && port
< 32) )
2394 return set_err(EINVAL
);
2396 // Open controller handle
2398 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%u:", contr_no
);
2400 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2401 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2402 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
2404 if (h
== INVALID_HANDLE_VALUE
) {
2405 long err
= GetLastError();
2406 if (err
== ERROR_FILE_NOT_FOUND
)
2407 set_err(ENOENT
, "%s: not found", devpath
);
2408 else if (err
== ERROR_ACCESS_DENIED
)
2409 set_err(EACCES
, "%s: access denied", devpath
);
2411 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2415 if (scsi_debugmode
> 1)
2416 pout(" %s: successfully opened\n", devpath
);
2424 bool win_csmi_device::open()
2429 // Get Phy info for this drive
2430 if (!select_port(m_port
)) {
2439 bool win_csmi_device::csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2440 unsigned csmi_bufsiz
)
2442 // Determine signature
2445 case CC_CSMI_SAS_GET_DRIVER_INFO
:
2446 sig
= CSMI_ALL_SIGNATURE
; break;
2447 case CC_CSMI_SAS_GET_PHY_INFO
:
2448 case CC_CSMI_SAS_STP_PASSTHRU
:
2449 sig
= CSMI_SAS_SIGNATURE
; break;
2451 return set_err(ENOSYS
, "Unknown CSMI code=%u", code
);
2455 csmi_buffer
->HeaderLength
= sizeof(IOCTL_HEADER
);
2456 strncpy((char *)csmi_buffer
->Signature
, sig
, sizeof(csmi_buffer
->Signature
));
2457 csmi_buffer
->Timeout
= CSMI_SAS_TIMEOUT
;
2458 csmi_buffer
->ControlCode
= code
;
2459 csmi_buffer
->ReturnCode
= 0;
2460 csmi_buffer
->Length
= csmi_bufsiz
- sizeof(IOCTL_HEADER
);
2464 if (!DeviceIoControl(m_fh
, IOCTL_SCSI_MINIPORT
,
2465 csmi_buffer
, csmi_bufsiz
, csmi_buffer
, csmi_bufsiz
, &num_out
, (OVERLAPPED
*)0)) {
2466 long err
= GetLastError();
2468 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code
, err
);
2469 if ( err
== ERROR_INVALID_FUNCTION
2470 || err
== ERROR_NOT_SUPPORTED
2471 || err
== ERROR_DEV_NOT_EXIST
)
2472 return set_err(ENOSYS
, "CSMI is not supported (Error=%ld)", err
);
2474 return set_err(EIO
, "CSMI(%u) failed with Error=%ld", code
, err
);
2478 if (csmi_buffer
->ReturnCode
) {
2479 if (scsi_debugmode
) {
2480 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
2481 code
, (unsigned)csmi_buffer
->ReturnCode
);
2483 return set_err(EIO
, "CSMI(%u) failed with ReturnCode=%u", code
, (unsigned)csmi_buffer
->ReturnCode
);
2486 if (scsi_debugmode
> 1)
2487 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code
, (unsigned)num_out
);
2493 //////////////////////////////////////////////////////////////////////
2494 // win_tw_cli_device
2496 // Routines for pseudo device /dev/tw_cli/*
2497 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
2498 // TODO: This is OS independent
2500 class win_tw_cli_device
2501 : public /*implements*/ ata_device_with_command_set
2504 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2506 virtual bool is_open() const;
2508 virtual bool open();
2510 virtual bool close();
2513 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
2516 bool m_ident_valid
, m_smart_valid
;
2517 ata_identify_device m_ident_buf
;
2518 ata_smart_values m_smart_buf
;
2522 /////////////////////////////////////////////////////////////////////////////
2524 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2525 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
2526 m_ident_valid(false), m_smart_valid(false)
2528 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
2529 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
2533 bool win_tw_cli_device::is_open() const
2535 return (m_ident_valid
|| m_smart_valid
);
2539 // Get clipboard data
2541 static int get_clipboard(char * data
, int datasize
)
2543 if (!OpenClipboard(NULL
))
2545 HANDLE h
= GetClipboardData(CF_TEXT
);
2550 const void * p
= GlobalLock(h
);
2551 int n
= GlobalSize(h
);
2561 // Run a command, write stdout to dataout
2562 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
2564 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
2566 // Create stdout pipe
2567 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
2568 HANDLE pipe_out_w
, h
;
2569 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
2571 HANDLE self
= GetCurrentProcess();
2573 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
2574 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
2575 CloseHandle(pipe_out_w
);
2579 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
2580 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
2581 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
2586 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
2587 si
.hStdInput
= INVALID_HANDLE_VALUE
;
2588 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
2589 si
.dwFlags
= STARTF_USESTDHANDLES
;
2590 PROCESS_INFORMATION pi
;
2592 NULL
, const_cast<char *>(cmd
),
2593 NULL
, NULL
, TRUE
/*inherit*/,
2594 CREATE_NO_WINDOW
/*do not create a new console window*/,
2595 NULL
, NULL
, &si
, &pi
)) {
2596 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
2599 CloseHandle(pi
.hThread
);
2600 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
2602 // Copy stdout to output buffer
2604 while (i
< outsize
) {
2606 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
2610 CloseHandle(pipe_out_r
);
2612 WaitForSingleObject(pi
.hProcess
, INFINITE
);
2613 CloseHandle(pi
.hProcess
);
2618 static const char * findstr(const char * str
, const char * sub
)
2620 const char * s
= strstr(str
, sub
);
2621 return (s
? s
+strlen(sub
) : "");
2625 bool win_tw_cli_device::open()
2627 m_ident_valid
= m_smart_valid
= false;
2628 const char * name
= skipdev(get_dev_name());
2629 // Read tw_cli or 3DM browser output into buffer
2631 int size
= -1, n1
= -1, n2
= -1;
2632 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
2633 size
= get_clipboard(buffer
, sizeof(buffer
));
2635 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
2636 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
2638 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
2639 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
2641 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
2642 if (ata_debugmode
> 1)
2643 pout("%s: Run: \"%s\"\n", name
, cmd
);
2644 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
2647 return set_err(EINVAL
);
2650 if (ata_debugmode
> 1)
2651 pout("%s: Read %d bytes\n", name
, size
);
2653 return set_err(ENOENT
);
2654 if (size
>= (int)sizeof(buffer
))
2655 return set_err(EIO
);
2658 if (ata_debugmode
> 1)
2659 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
2661 // Fake identify sector
2662 ASSERT_SIZEOF(ata_identify_device
, 512);
2663 ata_identify_device
* id
= &m_ident_buf
;
2664 memset(id
, 0, sizeof(*id
));
2665 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
2666 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
2667 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
2668 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
2669 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
2671 id
->words047_079
[49-47] = 0x0200; // size valid
2672 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
2673 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
2675 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2676 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2678 // Parse smart data hex dump
2679 const char * s
= findstr(buffer
, "Drive Smart Data:");
2681 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
2683 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
2685 const char * s1
= findstr(s
, "<td class"); // html version
2688 s
+= strcspn(s
, "\r\n");
2691 s
= buffer
; // try raw hex dump without header
2693 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
2696 unsigned x
= ~0; int n
= -1;
2697 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
2699 sd
[i
] = (unsigned char)x
;
2700 if (!(++i
< 512 && n
> 0))
2703 if (*s
== '<') // "<br>"
2704 s
+= strcspn(s
, "\r\n");
2707 if (!id
->model
[1]) {
2708 // No useful data found
2709 char * err
= strstr(buffer
, "Error:");
2711 err
= strstr(buffer
, "error :");
2712 if (err
&& (err
= strchr(err
, ':'))) {
2713 // Show tw_cli error message
2715 err
[strcspn(err
, "\r\n")] = 0;
2716 return set_err(EIO
, "%s", err
);
2718 return set_err(EIO
);
2723 m_ident_valid
= true;
2724 m_smart_valid
= !!sd
;
2729 bool win_tw_cli_device::close()
2731 m_ident_valid
= m_smart_valid
= false;
2736 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
2742 memcpy(data
, &m_ident_buf
, 512);
2747 memcpy(data
, &m_smart_buf
, 512);
2751 case STATUS_CHECK
: // Fake "good" SMART status
2756 // Arrive here for all unsupported commands
2762 /////////////////////////////////////////////////////////////////////////////
2764 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
2766 class win_scsi_device
2767 : public /*implements*/ scsi_device
,
2768 virtual public /*extends*/ win_smart_device
2771 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2773 virtual bool open();
2775 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
2778 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
2782 /////////////////////////////////////////////////////////////////////////////
2784 win_scsi_device::win_scsi_device(smart_interface
* intf
,
2785 const char * dev_name
, const char * req_type
)
2786 : smart_device(intf
, dev_name
, "scsi", req_type
)
2790 bool win_scsi_device::open()
2792 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2793 // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
2794 char drive
[2+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
2795 if ( sscanf(name
, "sd%2[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
2796 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
2797 return open(sdxy_to_phydrive(drive
), -1, -1, sub_addr
);
2799 // pd<m>,N => Physical drive <m>, RAID port N
2800 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
2801 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
2802 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
2803 return open(pd_num
, -1, -1, sub_addr
);
2805 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2806 int logdrive
= drive_letter(name
);
2807 if (logdrive
>= 0) {
2808 return open(-1, logdrive
, -1, -1);
2810 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
2811 int tape_num
= -1; n1
= -1;
2812 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2813 return open(-1, -1, tape_num
, -1);
2815 tape_num
= -1; n1
= -1;
2816 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2817 return open(-1, -1, tape_num
, -1);
2819 // tape<m> => tape drive <m>
2820 tape_num
= -1; n1
= -1;
2821 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2822 return open(-1, -1, tape_num
, -1);
2825 return set_err(EINVAL
);
2828 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
2831 b
[sizeof(b
) - 1] = '\0';
2833 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
2834 else if (ld_num
>= 0)
2835 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
2836 else if (tape_num
>= 0)
2837 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
2844 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
2845 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
2846 OPEN_EXISTING
, 0, 0);
2847 if (h
== INVALID_HANDLE_VALUE
) {
2848 set_err(ENODEV
, "%s: Open failed, Error=%u", b
, (unsigned)GetLastError());
2857 SCSI_PASS_THROUGH_DIRECT spt
;
2859 UCHAR ucSenseBuf
[64];
2860 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
2863 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
2864 // Used if DataTransferLength not supported by *_DIRECT.
2865 static long scsi_pass_through_indirect(HANDLE h
,
2866 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
2868 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
2869 SCSI_PASS_THROUGH spt
;
2871 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
2872 UCHAR ucDataBuf
[512];
2875 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
2876 memset(&sb
, 0, sizeof(sb
));
2878 // DATA_OUT not implemented yet
2879 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
2880 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
2881 return ERROR_INVALID_PARAMETER
;
2883 sb
.spt
.Length
= sizeof(sb
.spt
);
2884 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
2885 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
2886 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2887 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
2888 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
2889 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
2890 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
2891 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
2894 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
2895 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2896 return GetLastError();
2898 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
2899 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
2900 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
2902 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
2903 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
2904 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
2909 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
2910 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
2912 int report
= scsi_debugmode
; // TODO
2916 const unsigned char * ucp
= iop
->cmnd
;
2919 const int sz
= (int)sizeof(buff
);
2921 np
= scsi_get_opcode_name(ucp
[0]);
2922 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
2923 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
2924 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
2926 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
2927 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2929 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
2930 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
2931 (trunc
? " [only first 256 bytes shown]" : ""));
2932 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2935 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
2939 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
2940 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
2941 set_err(EINVAL
, "cmnd_len too large");
2945 memset(&sb
, 0, sizeof(sb
));
2946 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
2947 sb
.spt
.CdbLength
= iop
->cmnd_len
;
2948 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
2949 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2950 sb
.spt
.SenseInfoOffset
=
2951 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
2952 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
2955 switch (iop
->dxfer_dir
) {
2957 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
2959 case DXFER_FROM_DEVICE
:
2960 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
2961 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2962 sb
.spt
.DataBuffer
= iop
->dxferp
;
2963 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
2964 // transfers (needed for SMART STATUS check of JMicron USB bridges)
2965 if (sb
.spt
.DataTransferLength
== 1)
2968 case DXFER_TO_DEVICE
:
2969 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
2970 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2971 sb
.spt
.DataBuffer
= iop
->dxferp
;
2974 set_err(EINVAL
, "bad dxfer_dir");
2981 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
2982 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2983 err
= GetLastError();
2986 err
= scsi_pass_through_indirect(get_fh(), &sb
);
2989 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
2990 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
2991 (direct
? "_DIRECT" : ""), err
);
2993 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
2994 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
2995 int slen
= sb
.ucSenseBuf
[7] + 8;
2997 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
2998 slen
= sizeof(sb
.ucSenseBuf
);
2999 if (slen
> (int)iop
->max_sense_len
)
3000 slen
= iop
->max_sense_len
;
3001 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3002 iop
->resp_sense_len
= slen
;
3005 pout(" >>> Sense buffer, len=%d:\n", slen
);
3006 dStrHex(iop
->sensep
, slen
, 1);
3008 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3009 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3010 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3011 iop
->sensep
[2], iop
->sensep
[3]);
3013 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3014 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3015 iop
->sensep
[12], iop
->sensep
[13]);
3018 iop
->resp_sense_len
= 0;
3020 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
3021 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3025 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3026 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3027 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3028 (trunc
? " [only first 256 bytes shown]" : ""));
3029 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3035 /////////////////////////////////////////////////////////////////////////////
3036 /// Areca RAID support
3038 // TODO: combine with above scsi_pass_through_direct()
3039 static long scsi_pass_through_direct(HANDLE fd
, UCHAR targetid
, struct scsi_cmnd_io
* iop
)
3041 int report
= scsi_debugmode
; // TODO
3045 const unsigned char * ucp
= iop
->cmnd
;
3048 const int sz
= (int)sizeof(buff
);
3050 np
= scsi_get_opcode_name(ucp
[0]);
3051 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3052 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3053 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3055 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3056 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3058 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3059 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3060 (trunc
? " [only first 256 bytes shown]" : ""));
3061 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3064 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3068 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
3069 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
3073 memset(&sb
, 0, sizeof(sb
));
3074 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
3075 //sb.spt.PathId = 0;
3076 sb
.spt
.TargetId
= targetid
;
3078 sb
.spt
.CdbLength
= iop
->cmnd_len
;
3079 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
3080 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3081 sb
.spt
.SenseInfoOffset
=
3082 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
3083 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
3086 switch (iop
->dxfer_dir
) {
3088 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
3090 case DXFER_FROM_DEVICE
:
3091 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
3092 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3093 sb
.spt
.DataBuffer
= iop
->dxferp
;
3094 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
3095 // transfers (needed for SMART STATUS check of JMicron USB bridges)
3096 if (sb
.spt
.DataTransferLength
== 1)
3099 case DXFER_TO_DEVICE
:
3100 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
3101 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3102 sb
.spt
.DataBuffer
= iop
->dxferp
;
3111 if (!DeviceIoControl(fd
, IOCTL_SCSI_PASS_THROUGH_DIRECT
,
3112 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3113 err
= GetLastError();
3116 err
= scsi_pass_through_indirect(fd
, &sb
);
3123 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
3124 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3125 int slen
= sb
.ucSenseBuf
[7] + 8;
3127 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
3128 slen
= sizeof(sb
.ucSenseBuf
);
3129 if (slen
> (int)iop
->max_sense_len
)
3130 slen
= iop
->max_sense_len
;
3131 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3132 iop
->resp_sense_len
= slen
;
3135 pout(" >>> Sense buffer, len=%d:\n", slen
);
3136 dStrHex(iop
->sensep
, slen
, 1);
3138 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3139 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3140 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3141 iop
->sensep
[2], iop
->sensep
[3]);
3143 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3144 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3145 iop
->sensep
[12], iop
->sensep
[13]);
3148 iop
->resp_sense_len
= 0;
3150 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
3151 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3155 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3156 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3157 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3158 (trunc
? " [only first 256 bytes shown]" : ""));
3159 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3166 /////////////////////////////////////////////////////////////////////////////
3167 // win_areca_scsi_device
3168 // SAS(SCSI) device behind Areca RAID Controller
3170 class win_areca_scsi_device
3171 : public /*implements*/ areca_scsi_device
,
3172 public /*extends*/ win_smart_device
3175 win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3176 virtual bool open();
3177 virtual smart_device
* autodetect_open();
3178 virtual bool arcmsr_lock();
3179 virtual bool arcmsr_unlock();
3180 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3187 /////////////////////////////////////////////////////////////////////////////
3189 win_areca_scsi_device::win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3190 : smart_device(intf
, dev_name
, "areca", "areca")
3192 set_fh(INVALID_HANDLE_VALUE
);
3193 set_disknum(disknum
);
3195 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3198 bool win_areca_scsi_device::open()
3206 hFh
= CreateFile( get_dev_name(),
3207 GENERIC_READ
|GENERIC_WRITE
,
3208 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3213 if(hFh
== INVALID_HANDLE_VALUE
)
3222 smart_device
* win_areca_scsi_device::autodetect_open()
3227 int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3229 int ioctlreturn
= 0;
3231 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3232 if ( ioctlreturn
|| iop
->scsi_status
)
3234 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3235 if ( ioctlreturn
|| iop
->scsi_status
)
3245 bool win_areca_scsi_device::arcmsr_lock()
3247 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3251 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3252 return set_err(EINVAL
, "unable to parse device name");
3254 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3255 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3256 if ( m_mutex
== NULL
)
3258 return set_err(EIO
, "CreateMutex failed");
3261 // atomic access to driver
3262 WaitForSingleObject(m_mutex
, INFINITE
);
3268 bool win_areca_scsi_device::arcmsr_unlock()
3270 if( m_mutex
!= NULL
)
3272 ReleaseMutex(m_mutex
);
3273 CloseHandle(m_mutex
);
3280 /////////////////////////////////////////////////////////////////////////////
3281 // win_areca_ata_device
3282 // SATA(ATA) device behind Areca RAID Controller
3284 class win_areca_ata_device
3285 : public /*implements*/ areca_ata_device
,
3286 public /*extends*/ win_smart_device
3289 win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3290 virtual bool open();
3291 virtual smart_device
* autodetect_open();
3292 virtual bool arcmsr_lock();
3293 virtual bool arcmsr_unlock();
3294 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3301 /////////////////////////////////////////////////////////////////////////////
3303 win_areca_ata_device::win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3304 : smart_device(intf
, dev_name
, "areca", "areca")
3306 set_fh(INVALID_HANDLE_VALUE
);
3307 set_disknum(disknum
);
3309 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3312 bool win_areca_ata_device::open()
3320 hFh
= CreateFile( get_dev_name(),
3321 GENERIC_READ
|GENERIC_WRITE
,
3322 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3327 if(hFh
== INVALID_HANDLE_VALUE
)
3336 smart_device
* win_areca_ata_device::autodetect_open()
3338 // autodetect device type
3339 int is_ata
= arcmsr_get_dev_type();
3353 smart_device_auto_ptr
newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
3356 newdev
->open(); // TODO: Can possibly pass open fd
3358 return newdev
.release();
3361 int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3363 int ioctlreturn
= 0;
3365 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3366 if ( ioctlreturn
|| iop
->scsi_status
)
3368 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3369 if ( ioctlreturn
|| iop
->scsi_status
)
3379 bool win_areca_ata_device::arcmsr_lock()
3381 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3385 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3386 return set_err(EINVAL
, "unable to parse device name");
3388 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3389 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3390 if ( m_mutex
== NULL
)
3392 return set_err(EIO
, "CreateMutex failed");
3395 // atomic access to driver
3396 WaitForSingleObject(m_mutex
, INFINITE
);
3402 bool win_areca_ata_device::arcmsr_unlock()
3404 if( m_mutex
!= NULL
)
3406 ReleaseMutex(m_mutex
);
3407 CloseHandle(m_mutex
);
3414 /////////////////////////////////////////////////////////////////////////////
3415 // win_aacraid_device
3416 // PMC aacraid Support
3418 class win_aacraid_device
3419 :public /*implements*/ scsi_device
,
3420 public /*extends*/ win_smart_device
3423 win_aacraid_device(smart_interface
*intf
, const char *dev_name
,unsigned int ctrnum
, unsigned int target
, unsigned int lun
);
3425 virtual ~win_aacraid_device() throw();
3427 virtual bool open();
3429 virtual bool scsi_pass_through(struct scsi_cmnd_io
*iop
);
3432 //Device Host number
3435 //Channel(Lun) of the device
3443 /////////////////////////////////////////////////////////////////////////////
3445 win_aacraid_device::win_aacraid_device(smart_interface
* intf
,
3446 const char *dev_name
, unsigned ctrnum
, unsigned target
, unsigned lun
)
3447 : smart_device(intf
, dev_name
, "aacraid", "aacraid"),
3448 m_ctrnum(ctrnum
), m_lun(lun
), m_target(target
)
3450 set_info().info_name
= strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name
, m_ctrnum
, m_lun
, m_target
);
3451 set_info().dev_type
= strprintf("aacraid,%d,%d,%d", m_ctrnum
, m_lun
, m_target
);
3454 win_aacraid_device::~win_aacraid_device() throw()
3458 bool win_aacraid_device::open()
3463 HANDLE hFh
= CreateFile( get_dev_name(),
3464 GENERIC_READ
|GENERIC_WRITE
,
3465 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3470 if (hFh
== INVALID_HANDLE_VALUE
)
3471 return set_err(ENODEV
, "Open failed, Error=%u", (unsigned)GetLastError());
3477 bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io
*iop
)
3479 int report
= scsi_debugmode
;
3483 const unsigned char * ucp
= iop
->cmnd
;
3486 const int sz
= (int)sizeof(buff
);
3487 np
= scsi_get_opcode_name(ucp
[0]);
3488 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3489 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3490 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3492 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3493 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3495 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3496 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3497 (trunc
? " [only first 256 bytes shown]" : ""));
3498 dStrHex((const char *)iop
->dxferp
,
3499 (trunc
? 256 : (int)iop
->dxfer_len
) , 1);
3502 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3503 pout("buff %s\n",buff
);
3506 char ioBuffer
[1000];
3507 SRB_IO_CONTROL
* pSrbIO
= (SRB_IO_CONTROL
*) ioBuffer
;
3508 SCSI_REQUEST_BLOCK
* pScsiIO
= (SCSI_REQUEST_BLOCK
*) (ioBuffer
+ sizeof(SRB_IO_CONTROL
));
3509 DWORD scsiRequestBlockSize
= sizeof(SCSI_REQUEST_BLOCK
);
3510 char *pRequestSenseIO
= (char *) (ioBuffer
+ sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
);
3511 DWORD dataOffset
= (sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
+ 7) & 0xfffffff8;
3512 char *pDataIO
= (char *) (ioBuffer
+ dataOffset
);
3513 memset(pScsiIO
, 0, scsiRequestBlockSize
);
3514 pScsiIO
->Length
= (USHORT
) scsiRequestBlockSize
;
3515 pScsiIO
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
3516 pScsiIO
->PathId
= 0;
3517 pScsiIO
->TargetId
= m_target
;
3518 pScsiIO
->Lun
= m_lun
;
3519 pScsiIO
->CdbLength
= (int)iop
->cmnd_len
;
3520 switch(iop
->dxfer_dir
){
3522 pScsiIO
->SrbFlags
= SRB_NoDataXfer
;
3524 case DXFER_FROM_DEVICE
:
3525 pScsiIO
->SrbFlags
|= SRB_DataIn
;
3527 case DXFER_TO_DEVICE
:
3528 pScsiIO
->SrbFlags
|= SRB_DataOut
;
3531 pout("aacraid: bad dxfer_dir\n");
3532 return set_err(EINVAL
, "aacraid: bad dxfer_dir\n");
3534 pScsiIO
->DataTransferLength
= (ULONG
)iop
->dxfer_len
;
3535 pScsiIO
->TimeOutValue
= iop
->timeout
;
3536 UCHAR
*pCdb
= (UCHAR
*) pScsiIO
->Cdb
;
3537 memcpy(pCdb
, iop
->cmnd
, 16);
3538 if (iop
->max_sense_len
){
3539 memset(pRequestSenseIO
, 0, iop
->max_sense_len
);
3541 if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_OUT
){
3542 memcpy(pDataIO
, iop
->dxferp
, iop
->dxfer_len
);
3544 else if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_IN
){
3545 memset(pDataIO
, 0, iop
->dxfer_len
);
3548 DWORD bytesReturned
= 0;
3549 memset(pSrbIO
, 0, sizeof(SRB_IO_CONTROL
));
3550 pSrbIO
->HeaderLength
= sizeof(SRB_IO_CONTROL
);
3551 memcpy(pSrbIO
->Signature
, "AACAPI", 7);
3552 pSrbIO
->ControlCode
= ARCIOCTL_SEND_RAW_SRB
;
3553 pSrbIO
->Length
= (dataOffset
+ iop
->dxfer_len
- sizeof(SRB_IO_CONTROL
) + 7) & 0xfffffff8;
3554 pSrbIO
->Timeout
= 3*60;
3556 if (!DeviceIoControl(
3558 IOCTL_SCSI_MINIPORT
,
3560 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3562 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3566 return set_err(EIO
, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
3569 iop
->scsi_status
= pScsiIO
->ScsiStatus
;
3570 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3571 int slen
= sizeof(pRequestSenseIO
) + 8;
3572 if (slen
> (int)sizeof(pRequestSenseIO
))
3573 slen
= sizeof(pRequestSenseIO
);
3574 if (slen
> (int)iop
->max_sense_len
)
3575 slen
= (int)iop
->max_sense_len
;
3576 memcpy(iop
->sensep
, pRequestSenseIO
, slen
);
3577 iop
->resp_sense_len
= slen
;
3580 pout(" >>> Sense buffer, len=%d:\n", slen
);
3581 dStrHex(iop
->sensep
, slen
, 1);
3583 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3584 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3585 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3586 iop
->sensep
[2], iop
->sensep
[3]);
3588 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3589 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3590 iop
->sensep
[12], iop
->sensep
[13]);
3594 iop
->resp_sense_len
= 0;
3597 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
){
3598 memcpy(iop
->dxferp
,pDataIO
, iop
->dxfer_len
);
3600 if((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)){
3601 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3602 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3603 (trunc
? " [only first 256 bytes shown]" : ""));
3604 dStrHex((CHAR
*)pDataIO
, (trunc
? 256 : (int)(iop
->dxfer_len
)) , 1);
3610 /////////////////////////////////////////////////////////////////////////////
3613 class win_nvme_device
3614 : public /*implements*/ nvme_device
,
3615 public /*extends*/ win_smart_device
3618 win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3619 const char * req_type
, unsigned nsid
);
3621 virtual bool open();
3623 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
3625 bool open_scsi(int n
);
3634 /////////////////////////////////////////////////////////////////////////////
3636 win_nvme_device::win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3637 const char * req_type
, unsigned nsid
)
3638 : smart_device(intf
, dev_name
, "nvme", req_type
),
3644 bool win_nvme_device::open_scsi(int n
)
3646 // TODO: Use common open function for all devices using "\\.\ScsiN:"
3648 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%d:", n
);
3650 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
3651 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3652 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
3654 if (h
== INVALID_HANDLE_VALUE
) {
3655 long err
= GetLastError();
3656 if (nvme_debugmode
> 1)
3657 pout(" %s: Open failed, Error=%ld\n", devpath
, err
);
3658 if (err
== ERROR_FILE_NOT_FOUND
)
3659 set_err(ENOENT
, "%s: not found", devpath
);
3660 else if (err
== ERROR_ACCESS_DENIED
)
3661 set_err(EACCES
, "%s: access denied", devpath
);
3663 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3667 if (nvme_debugmode
> 1)
3668 pout(" %s: successfully opened\n", devpath
);
3674 // Check if NVMe pass-through works
3675 bool win_nvme_device::probe()
3677 smartmontools::nvme_id_ctrl id_ctrl
;
3679 in
.set_data_in(smartmontools::nvme_admin_identify
, &id_ctrl
, sizeof(id_ctrl
));
3684 bool ok
= nvme_pass_through(in
, out
);
3685 if (!ok
&& nvme_debugmode
> 1)
3686 pout(" nvme probe failed: %s\n", get_errmsg());
3690 bool win_nvme_device::open()
3692 if (m_scsi_no
< 0) {
3693 // First open -> search of NVMe devices
3694 const char * name
= skipdev(get_dev_name());
3695 char s
[2+1] = ""; int n1
= -1, n2
= -1, len
= strlen(name
);
3696 unsigned no
= ~0, nsid
= 0xffffffff;
3697 sscanf(name
, "nvm%2[es]%u%nn%u%n", s
, &no
, &n1
, &nsid
, &n2
);
3699 if (!( (n1
== len
|| (n2
== len
&& nsid
> 0))
3700 && s
[0] == 'e' && (!s
[1] || s
[1] == 's') ))
3701 return set_err(EINVAL
);
3704 // /dev/nvmeN* -> search for nth NVMe device
3705 unsigned nvme_cnt
= 0;
3706 for (int i
= 0; i
< 32; i
++) {
3707 if (!open_scsi(i
)) {
3708 if (get_errno() == EACCES
)
3712 // Done if pass-through works and correct number
3714 if (nvme_cnt
== no
) {
3724 return set_err(ENOENT
);
3728 // /dev/nvmesN* -> use "\\.\ScsiN:"
3738 // Reopen same "\\.\ScsiN:"
3739 if (!open_scsi(m_scsi_no
))
3746 bool win_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
3748 // Create buffer with appropriate size
3749 raw_buffer
pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
) + in
.size
);
3750 NVME_PASS_THROUGH_IOCTL
* pthru
=
3751 reinterpret_cast<NVME_PASS_THROUGH_IOCTL
*>(pthru_raw_buf
.data());
3754 pthru
->SrbIoCtrl
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
3755 memcpy(pthru
->SrbIoCtrl
.Signature
, NVME_SIG_STR
, sizeof(NVME_SIG_STR
)-1);
3756 pthru
->SrbIoCtrl
.Timeout
= 60;
3757 pthru
->SrbIoCtrl
.ControlCode
= NVME_PASS_THROUGH_SRB_IO_CODE
;
3758 pthru
->SrbIoCtrl
.ReturnCode
= 0;
3759 pthru
->SrbIoCtrl
.Length
= pthru_raw_buf
.size() - sizeof(SRB_IO_CONTROL
);
3761 pthru
->NVMeCmd
[0] = in
.opcode
;
3762 pthru
->NVMeCmd
[1] = in
.nsid
;
3763 pthru
->NVMeCmd
[10] = in
.cdw10
;
3764 pthru
->NVMeCmd
[11] = in
.cdw11
;
3765 pthru
->NVMeCmd
[12] = in
.cdw12
;
3766 pthru
->NVMeCmd
[13] = in
.cdw13
;
3767 pthru
->NVMeCmd
[14] = in
.cdw14
;
3768 pthru
->NVMeCmd
[15] = in
.cdw15
;
3770 pthru
->Direction
= in
.direction();
3771 // pthru->QueueId = 0; // AdminQ
3772 // pthru->DataBufferLen = 0;
3773 if (in
.direction() & nvme_cmd_in::data_out
) {
3774 pthru
->DataBufferLen
= in
.size
;
3775 memcpy(pthru
->DataBuffer
, in
.buffer
, in
.size
);
3777 // pthru->MetaDataLen = 0;
3778 pthru
->ReturnBufferLen
= pthru_raw_buf
.size();
3780 // Call NVME_PASS_THROUGH
3782 BOOL ok
= DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT
,
3783 pthru
, pthru_raw_buf
.size(), pthru
, pthru_raw_buf
.size(),
3784 &num_out
, (OVERLAPPED
*)0);
3787 unsigned status
= pthru
->CplEntry
[3] >> 17;
3789 return set_nvme_err(out
, status
);
3792 return set_err(EIO
, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError());
3794 if (in
.direction() & nvme_cmd_in::data_in
)
3795 memcpy(in
.buffer
, pthru
->DataBuffer
, in
.size
);
3797 out
.result
= pthru
->CplEntry
[0];
3802 /////////////////////////////////////////////////////////////////////////////
3803 // win10_nvme_device
3805 class win10_nvme_device
3806 : public /*implements*/ nvme_device
,
3807 public /*extends*/ win_smart_device
3810 win10_nvme_device(smart_interface
* intf
, const char * dev_name
,
3811 const char * req_type
, unsigned nsid
);
3813 virtual bool open();
3815 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
3818 bool open(int phydrive
);
3822 /////////////////////////////////////////////////////////////////////////////
3824 win10_nvme_device::win10_nvme_device(smart_interface
* intf
, const char * dev_name
,
3825 const char * req_type
, unsigned nsid
)
3826 : smart_device(intf
, dev_name
, "nvme", req_type
),
3831 bool win10_nvme_device::open()
3833 // TODO: Use common /dev/ parsing functions
3834 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
3835 // sd[a-z]([a-z])? => Physical drive 0-701
3836 char drive
[2 + 1] = ""; int n
= -1;
3837 if (sscanf(name
, "sd%2[a-z]%n", drive
, &n
) == 1 && n
== len
)
3838 return open(sdxy_to_phydrive(drive
));
3840 // pdN => Physical drive N
3841 int phydrive
= -1; n
= -1;
3842 if (sscanf(name
, "pd%d%n", &phydrive
, &n
) == 1 && phydrive
>= 0 && n
== len
)
3843 return open(phydrive
);
3845 return set_err(EINVAL
);
3848 bool win10_nvme_device::open(int phydrive
)
3850 // TODO: Use common open function for all devices using "\\.\PhysicalDriveN"
3852 snprintf(devpath
, sizeof(devpath
) - 1, "\\\\.\\PhysicalDrive%d", phydrive
);
3854 // No GENERIC_READ/WRITE access required, this works without admin rights
3855 HANDLE h
= CreateFileA(devpath
, 0, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
3856 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, (HANDLE
)0);
3858 if (h
== INVALID_HANDLE_VALUE
) {
3859 long err
= GetLastError();
3860 if (nvme_debugmode
> 1)
3861 pout(" %s: Open failed, Error=%ld\n", devpath
, err
);
3862 if (err
== ERROR_FILE_NOT_FOUND
)
3863 set_err(ENOENT
, "%s: not found", devpath
);
3864 else if (err
== ERROR_ACCESS_DENIED
)
3865 set_err(EACCES
, "%s: access denied", devpath
);
3867 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3871 if (nvme_debugmode
> 1)
3872 pout(" %s: successfully opened\n", devpath
);
3876 // Use broadcast namespace if no NSID specified
3877 // TODO: Get NSID of current device
3879 set_nsid(0xffffffff);
3883 struct STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
3885 struct { // STORAGE_PROPERTY_QUERY without AdditionalsParameters[1]
3886 STORAGE_PROPERTY_ID PropertyId
;
3887 STORAGE_QUERY_TYPE QueryType
;
3889 win10::STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecific
;
3893 bool win10_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
3895 // Create buffer with appropriate size
3896 raw_buffer
spsq_raw_buf(offsetof(STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
, DataBuffer
) + in
.size
);
3897 STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
* spsq
=
3898 reinterpret_cast<STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
*>(spsq_raw_buf
.data());
3900 // Set NVMe specifc STORAGE_PROPERTY_QUERY
3901 spsq
->PropertyQuery
.QueryType
= PropertyStandardQuery
;
3902 spsq
->ProtocolSpecific
.ProtocolType
= win10::ProtocolTypeNvme
;
3904 switch (in
.opcode
) {
3905 case smartmontools::nvme_admin_identify
:
3906 if (!in
.nsid
) // Identify controller
3907 spsq
->PropertyQuery
.PropertyId
= win10::StorageAdapterProtocolSpecificProperty
;
3909 spsq
->PropertyQuery
.PropertyId
= win10::StorageDeviceProtocolSpecificProperty
;
3910 spsq
->ProtocolSpecific
.DataType
= win10::NVMeDataTypeIdentify
;
3911 spsq
->ProtocolSpecific
.ProtocolDataRequestValue
= in
.cdw10
;
3913 case smartmontools::nvme_admin_get_log_page
:
3914 spsq
->PropertyQuery
.PropertyId
= win10::StorageDeviceProtocolSpecificProperty
;
3915 spsq
->ProtocolSpecific
.DataType
= win10::NVMeDataTypeLogPage
;
3916 spsq
->ProtocolSpecific
.ProtocolDataRequestValue
= in
.cdw10
& 0xff; // LID only ?
3918 // TODO: nvme_admin_get_features
3920 return set_err(ENOSYS
, "NVMe admin command 0x%02x not supported", in
.opcode
);
3923 spsq
->ProtocolSpecific
.ProtocolDataRequestSubValue
= in
.nsid
; // ?
3924 spsq
->ProtocolSpecific
.ProtocolDataOffset
= sizeof(spsq
->ProtocolSpecific
);
3925 spsq
->ProtocolSpecific
.ProtocolDataLength
= in
.size
;
3927 if (in
.direction() & nvme_cmd_in::data_out
)
3928 memcpy(spsq
->DataBuffer
, in
.buffer
, in
.size
);
3930 if (nvme_debugmode
> 1)
3931 pout(" [STORAGE_QUERY_PROPERTY: Id=%u, Type=%u, Value=0x%08x, SubVal=0x%08x]\n",
3932 (unsigned)spsq
->PropertyQuery
.PropertyId
,
3933 (unsigned)spsq
->ProtocolSpecific
.DataType
,
3934 (unsigned)spsq
->ProtocolSpecific
.ProtocolDataRequestValue
,
3935 (unsigned)spsq
->ProtocolSpecific
.ProtocolDataRequestSubValue
);
3937 // Call IOCTL_STORAGE_QUERY_PROPERTY
3940 if (!DeviceIoControl(get_fh(), IOCTL_STORAGE_QUERY_PROPERTY
,
3941 spsq
, spsq_raw_buf
.size(), spsq
, spsq_raw_buf
.size(),
3942 &num_out
, (OVERLAPPED
*)0)) {
3943 err
= GetLastError();
3946 if (nvme_debugmode
> 1)
3947 pout(" [STORAGE_QUERY_PROPERTY: ReturnData=0x%08x, Reserved[3]={0x%x, 0x%x, 0x%x}]\n",
3948 (unsigned)spsq
->ProtocolSpecific
.FixedProtocolReturnData
,
3949 (unsigned)spsq
->ProtocolSpecific
.Reserved
[0],
3950 (unsigned)spsq
->ProtocolSpecific
.Reserved
[1],
3951 (unsigned)spsq
->ProtocolSpecific
.Reserved
[2]);
3953 // NVMe status is checked by IOCTL
3955 return set_err(EIO
, "IOCTL_STORAGE_QUERY_PROPERTY(NVMe) failed, Error=%ld", err
);
3957 if (in
.direction() & nvme_cmd_in::data_in
)
3958 memcpy(in
.buffer
, spsq
->DataBuffer
, in
.size
);
3960 out
.result
= spsq
->ProtocolSpecific
.FixedProtocolReturnData
; // Completion DW0 ?
3965 /////////////////////////////////////////////////////////////////////////////
3966 // win_smart_interface
3967 // Platform specific interface
3969 class win_smart_interface
3970 : public /*implements*/ smart_interface
3973 virtual std::string
get_os_version_str();
3975 virtual std::string
get_app_examples(const char * appname
);
3978 virtual int64_t get_timer_usec();
3981 virtual bool disable_system_auto_standby(bool disable
);
3983 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
3984 const char * pattern
= 0);
3987 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
3989 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
3991 virtual nvme_device
* get_nvme_device(const char * name
, const char * type
, unsigned nsid
);
3993 virtual smart_device
* autodetect_smart_device(const char * name
);
3995 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
3997 virtual std::string
get_valid_custom_dev_types_str();
4000 ata_device
* get_usb_device(const char * name
, int phydrive
, int logdrive
= -1);
4004 /////////////////////////////////////////////////////////////////////////////
4007 // Running on 64-bit Windows as 32-bit app ?
4008 static bool is_wow64()
4010 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
4011 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))
4012 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
4013 if (!IsWow64Process_p
)
4016 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
4022 // Return info string about build host and OS version
4023 std::string
win_smart_interface::get_os_version_str()
4025 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
4026 = SMARTMONTOOLS_BUILD_HOST
;
4029 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
4030 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
4031 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
4033 // Starting with Windows 8.1, GetVersionEx() does no longer report the
4034 // actual OS version, see:
4035 // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
4037 // RtlGetVersion() is not affected
4038 LONG
/*NTSTATUS*/ (WINAPI
/*NTAPI*/ * RtlGetVersion_p
)(LPOSVERSIONINFOEXW
) =
4039 (LONG (WINAPI
*)(LPOSVERSIONINFOEXW
))
4040 GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
4042 OSVERSIONINFOEXW vi
; memset(&vi
, 0, sizeof(vi
));
4043 vi
.dwOSVersionInfoSize
= sizeof(vi
);
4044 if (!RtlGetVersion_p
|| RtlGetVersion_p(&vi
)) {
4045 if (!GetVersionExW((OSVERSIONINFOW
*)&vi
))
4051 if ( vi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
4052 && vi
.dwMajorVersion
<= 0xf && vi
.dwMinorVersion
<= 0xf) {
4053 switch ( (vi
.dwMajorVersion
<< 4 | vi
.dwMinorVersion
) << 1
4054 | (vi
.wProductType
> VER_NT_WORKSTATION
? 1 : 0) ) {
4056 case 0x50<<1 | 1: w
= "2000"; break;
4057 case 0x51<<1 : w
= "xp"; break;
4058 case 0x52<<1 : w
= "xp64"; break;
4059 case 0x52<<1 | 1: w
= (!GetSystemMetrics(89/*SM_SERVERR2*/)
4062 case 0x60<<1 : w
= "vista"; break;
4063 case 0x60<<1 | 1: w
= "2008"; break;
4064 case 0x61<<1 : w
= "win7"; break;
4065 case 0x61<<1 | 1: w
= "2008r2"; break;
4066 case 0x62<<1 : w
= "win8"; break;
4067 case 0x62<<1 | 1: w
= "2012"; break;
4068 case 0x63<<1 : w
= "win8.1"; break;
4069 case 0x63<<1 | 1: w
= "2012r2"; break;
4071 switch (vi
.dwBuildNumber
) {
4072 case 10240: w
= "w10-1507"; break;
4073 case 10586: w
= "w10-1511"; break;
4074 case 14393: w
= "w10-1607"; break;
4075 case 15063: w
= "w10-1703"; break;
4076 case 16299: w
= "w10-1709"; break;
4077 default: w
= "w10"; build
= vi
.dwBuildNumber
; break;
4080 switch (vi
.dwBuildNumber
) {
4081 case 14393: w
= "2016-1607"; break;
4082 default: w
= "2016"; build
= vi
.dwBuildNumber
; break;
4087 const char * w64
= "";
4094 snprintf(vptr
, vlen
, "-%s%u.%u%s",
4095 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "??"),
4096 (unsigned)vi
.dwMajorVersion
, (unsigned)vi
.dwMinorVersion
, w64
);
4098 snprintf(vptr
, vlen
, "-%s%s-b%u", w
, w64
, build
);
4099 else if (vi
.wServicePackMinor
)
4100 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
4101 else if (vi
.wServicePackMajor
)
4102 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
4104 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
4109 // MSVCRT only provides ftime() which uses GetSystemTime()
4110 // This provides only ~15ms resolution by default.
4111 // Use QueryPerformanceCounter instead (~300ns).
4112 // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
4113 int64_t win_smart_interface::get_timer_usec()
4115 static int64_t freq
= 0;
4119 freq
= (QueryPerformanceFrequency(&t
) ? t
.QuadPart
: -1);
4121 return smart_interface::get_timer_usec();
4123 if (!QueryPerformanceCounter(&t
))
4125 if (!(0 <= t
.QuadPart
&& t
.QuadPart
<= (int64_t)(~(uint64_t)0 >> 1)/1000000))
4128 return (t
.QuadPart
* 1000000LL) / freq
;
4130 #endif // __CYGWIN__
4133 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
4135 const char * testname
= skipdev(name
);
4136 if (!strncmp(testname
, "csmi", 4))
4137 return new win_csmi_device(this, name
, type
);
4138 if (!strncmp(testname
, "tw_cli", 6))
4139 return new win_tw_cli_device(this, name
, type
);
4140 return new win_ata_device(this, name
, type
);
4143 scsi_device
* win_smart_interface::get_scsi_device(const char * name
, const char * type
)
4145 return new win_scsi_device(this, name
, type
);
4148 nvme_device
* win_smart_interface::get_nvme_device(const char * name
, const char * type
,
4151 if (str_starts_with(skipdev(name
), "nvme"))
4152 return new win_nvme_device(this, name
, type
, nsid
);
4153 return new win10_nvme_device(this, name
, type
, nsid
);
4157 smart_device
* win_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
4160 int disknum
= -1, n1
= -1, n2
= -1;
4164 if (sscanf(type
, "areca,%n%d/%d%n", &n1
, &disknum
, &encnum
, &n2
) >= 1 || n1
== 6) {
4165 if (!(1 <= disknum
&& disknum
<= 128)) {
4166 set_err(EINVAL
, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum
);
4169 if (!(1 <= encnum
&& encnum
<= 8)) {
4170 set_err(EINVAL
, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum
);
4174 name
= skipdev(name
);
4175 #define ARECA_MAX_CTLR_NUM 16
4178 if (sscanf(name
, "arcmsr%d%n", &ctlrindex
, &n1
) >= 1 && n1
== (int)strlen(name
)) {
4180 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
4181 2. map arcmsrX into "\\\\.\\scsiX"
4183 for (int idx
= 0; idx
< ARECA_MAX_CTLR_NUM
; idx
++) {
4184 memset(devpath
, 0, sizeof(devpath
));
4185 snprintf(devpath
, sizeof(devpath
), "\\\\.\\scsi%d:", idx
);
4186 win_areca_ata_device
*arcdev
= new win_areca_ata_device(this, devpath
, disknum
, encnum
);
4187 if(arcdev
->arcmsr_probe()) {
4188 if(ctlrindex
-- == 0) {
4194 set_err(ENOENT
, "No Areca controller found");
4197 set_err(EINVAL
, "Option -d areca,N/E requires device name /dev/arcmsrX");
4202 unsigned ctrnum
, lun
, target
;
4205 if ( sscanf(type
, "aacraid,%u,%u,%u%n", &ctrnum
, &lun
, &target
, &n1
) >= 3
4206 && n1
== (int)strlen(type
)) {
4207 #define aacraid_MAX_CTLR_NUM 16
4208 if (ctrnum
>= aacraid_MAX_CTLR_NUM
) {
4209 set_err(EINVAL
, "aacraid: invalid host number %u", ctrnum
);
4214 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and
4215 2. map ARCX into "\\\\.\\scsiX"
4217 memset(devpath
, 0, sizeof(devpath
));
4218 unsigned ctlrindex
= 0;
4219 for (int portNum
= 0; portNum
< aacraid_MAX_CTLR_NUM
; portNum
++){
4221 snprintf(subKey
, sizeof(subKey
), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum
);
4223 long regStatus
= RegOpenKeyExA(HKEY_LOCAL_MACHINE
, subKey
, 0, KEY_READ
, &hScsiKey
);
4224 if (regStatus
== ERROR_SUCCESS
){
4225 char driverName
[20];
4226 DWORD driverNameSize
= sizeof(driverName
);
4228 regStatus
= RegQueryValueExA(hScsiKey
, "Driver", NULL
, ®Type
, (LPBYTE
) driverName
, &driverNameSize
);
4229 if (regStatus
== ERROR_SUCCESS
){
4230 if (regType
== REG_SZ
){
4231 if (stricmp(driverName
, "arcsas") == 0){
4232 if(ctrnum
== ctlrindex
){
4233 snprintf(devpath
, sizeof(devpath
), "\\\\.\\Scsi%d:", portNum
);
4234 return get_sat_device("sat,auto",
4235 new win_aacraid_device(this, devpath
, ctrnum
, target
, lun
));
4241 RegCloseKey(hScsiKey
);
4245 set_err(EINVAL
, "aacraid: host %u not found", ctrnum
);
4252 std::string
win_smart_interface::get_valid_custom_dev_types_str()
4254 return "aacraid,H,L,ID, areca,N[/E]";
4258 // Return value for device detection functions
4259 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_SAT
, DEV_USB
, DEV_NVME
};
4261 // Return true if ATA drive behind a SAT layer
4262 static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
4264 if (!data
->desc
.VendorIdOffset
)
4266 if (strcmp(data
->raw
+ data
->desc
.VendorIdOffset
, "ATA "))
4271 // Return true if Intel ICHxR RAID volume
4272 static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
4274 if (!(data
->desc
.VendorIdOffset
&& data
->desc
.ProductIdOffset
))
4276 const char * vendor
= data
->raw
+ data
->desc
.VendorIdOffset
;
4277 if (!(!strnicmp(vendor
, "Intel", 5) && strspn(vendor
+5, " ") == strlen(vendor
+5)))
4279 if (strnicmp(data
->raw
+ data
->desc
.ProductIdOffset
, "Raid ", 5))
4284 // get DEV_* for open handle
4285 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4287 // Get BusType from device descriptor
4288 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
4289 if (storage_query_property_ioctl(hdevice
, &data
))
4292 // Newer BusType* values are missing in older includes
4293 switch ((int)data
.desc
.BusType
) {
4295 case 0x0b: // BusTypeSata
4296 // Certain Intel AHCI drivers (C600+/C220+) have broken
4297 // IOCTL_ATA_PASS_THROUGH support and a working SAT layer
4302 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
4310 // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
4311 if (is_intel_raid_volume(&data
))
4313 // LSI/3ware RAID volume: supports SMART_*
4314 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
4319 case 0x09: // BusTypeiScsi
4320 case 0x0a: // BusTypeSas
4329 case 0x11: // BusTypeNVMe?
4338 // get DEV_* for device path
4339 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
4342 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
4343 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4344 if (h
== INVALID_HANDLE_VALUE
) {
4346 h
= CreateFileA(path
, 0,
4347 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4348 if (h
== INVALID_HANDLE_VALUE
)
4351 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
4352 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
4353 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
4358 // get DEV_* for physical drive number
4359 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4362 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
4363 return get_controller_type(path
, ata_version_ex
);
4366 static win_dev_type
get_phy_drive_type(int drive
)
4368 return get_phy_drive_type(drive
, 0);
4371 // get DEV_* for logical drive number
4372 static win_dev_type
get_log_drive_type(int drive
)
4375 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
4376 return get_controller_type(path
);
4379 static win_dev_type
get_dev_type(const char * name
, int & phydrive
, int & logdrive
)
4381 phydrive
= logdrive
= -1;
4383 name
= skipdev(name
);
4384 if (!strncmp(name
, "st", 2))
4386 if (!strncmp(name
, "nst", 3))
4388 if (!strncmp(name
, "tape", 4))
4391 logdrive
= drive_letter(name
);
4392 if (logdrive
>= 0) {
4393 win_dev_type type
= get_log_drive_type(logdrive
);
4394 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
4397 char drive
[2+1] = "";
4398 if (sscanf(name
, "sd%2[a-z]", drive
) == 1) {
4399 phydrive
= sdxy_to_phydrive(drive
);
4400 return get_phy_drive_type(phydrive
);
4403 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
4404 return get_phy_drive_type(phydrive
);
4410 ata_device
* win_smart_interface::get_usb_device(const char * name
,
4411 int phydrive
, int logdrive
/* = -1 */)
4413 // Get USB bridge ID
4414 unsigned short vendor_id
= 0, product_id
= 0;
4415 if (!get_usb_id(phydrive
, logdrive
, vendor_id
, product_id
)) {
4416 set_err(EINVAL
, "Unable to read USB device ID");
4420 // Get type name for this ID
4421 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
4425 // Return SAT/USB device for this type
4426 return get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
4429 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
4431 const char * testname
= skipdev(name
);
4432 if (str_starts_with(testname
, "hd"))
4433 return new win_ata_device(this, name
, "");
4435 if (str_starts_with(testname
, "tw_cli"))
4436 return new win_tw_cli_device(this, name
, "");
4438 if (str_starts_with(testname
, "csmi"))
4439 return new win_csmi_device(this, name
, "");
4441 if (str_starts_with(testname
, "nvme"))
4442 return new win_nvme_device(this, name
, "", 0 /* use default nsid */);
4444 int phydrive
= -1, logdrive
= -1;
4445 win_dev_type type
= get_dev_type(name
, phydrive
, logdrive
);
4447 if (type
== DEV_ATA
)
4448 return new win_ata_device(this, name
, "");
4450 if (type
== DEV_SCSI
)
4451 return new win_scsi_device(this, name
, "");
4453 if (type
== DEV_SAT
)
4454 return get_sat_device("sat", new win_scsi_device(this, name
, ""));
4456 if (type
== DEV_USB
)
4457 return get_usb_device(name
, phydrive
, logdrive
);
4459 if (type
== DEV_NVME
)
4460 return new win10_nvme_device(this, name
, "", 0 /* use default nsid */);
4467 bool win_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
4468 const char * type
, const char * pattern
/* = 0*/)
4471 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
4475 // Check for "[*,]pd" type
4477 char type2
[16+1] = "";
4480 if (!strcmp(type
, "pd")) {
4484 else if (sscanf(type
, "%16[^,],pd%n", type2
, &nc
) == 1 &&
4485 nc
== (int)strlen(type
)) {
4492 bool ata
, scsi
, sat
, usb
, csmi
, nvme
;
4494 ata
= scsi
= usb
= sat
= csmi
= true;
4495 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
4502 ata
= scsi
= usb
= sat
= csmi
= nvme
= false;
4503 if (!strcmp(type
, "ata"))
4505 else if (!strcmp(type
, "scsi"))
4507 else if (!strcmp(type
, "sat"))
4509 else if (!strcmp(type
, "usb"))
4511 else if (!strcmp(type
, "csmi"))
4513 else if (!strcmp(type
, "nvme"))
4517 "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], "
4518 "sat[,pd], usb[,pd], csmi, nvme, pd", type
);
4525 if (ata
|| scsi
|| sat
|| usb
|| nvme
) {
4526 // Scan up to 128 drives and 2 3ware controllers
4527 const int max_raid
= 2;
4528 bool raid_seen
[max_raid
] = {false, false};
4530 for (int i
= 0; i
< 128; i
++) {
4532 snprintf(name
, sizeof(name
), "/dev/pd%d", i
);
4533 else if (i
+ 'a' <= 'z')
4534 snprintf(name
, sizeof(name
), "/dev/sd%c", i
+ 'a');
4536 snprintf(name
, sizeof(name
), "/dev/sd%c%c",
4537 i
/ ('z'-'a'+1) - 1 + 'a',
4538 i
% ('z'-'a'+1) + 'a');
4540 smart_device
* dev
= 0;
4541 GETVERSIONINPARAMS_EX vers_ex
;
4543 switch (get_phy_drive_type(i
, (ata
? &vers_ex
: 0))) {
4545 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
4549 // Interpret RAID drive map if present
4550 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
4551 // Skip if too many controllers or logical drive from this controller already seen
4552 if (!(vers_ex
.wControllerId
< max_raid
&& !raid_seen
[vers_ex
.wControllerId
]))
4554 raid_seen
[vers_ex
.wControllerId
] = true;
4555 // Add physical drives
4556 int len
= strlen(name
);
4557 for (unsigned int pi
= 0; pi
< 32; pi
++) {
4558 if (vers_ex
.dwDeviceMapEx
& (1L << pi
)) {
4559 snprintf(name
+len
, sizeof(name
)-1-len
, ",%u", pi
);
4560 devlist
.push_back( new win_ata_device(this, name
, "ata") );
4566 dev
= new win_ata_device(this, name
, "ata");
4570 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
4573 dev
= new win_scsi_device(this, name
, "scsi");
4577 // STORAGE_QUERY_PROPERTY returned VendorId "ATA "
4580 dev
= get_sat_device("sat", new win_scsi_device(this, name
, ""));
4584 // STORAGE_QUERY_PROPERTY returned USB
4587 dev
= get_usb_device(name
, i
);
4589 // Unknown or unsupported USB ID, return as SCSI
4590 dev
= new win_scsi_device(this, name
, "");
4594 // STORAGE_QUERY_PROPERTY returned NVMe
4597 dev
= new win10_nvme_device(this, name
, "", 0 /* use default nsid */);
4605 devlist
.push_back(dev
);
4610 // Scan CSMI devices
4611 for (int i
= 0; i
<= 9; i
++) {
4612 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,0", i
);
4613 win_csmi_device
test_dev(this, name
, "");
4614 if (!test_dev
.open_scsi())
4617 unsigned ports_used
= test_dev
.get_ports_used();
4621 for (int pi
= 0; pi
< 32; pi
++) {
4622 if (!(ports_used
& (1 << pi
)))
4624 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,%d", i
, pi
);
4625 devlist
.push_back( new win_csmi_device(this, name
, "ata") );
4631 // Scan \\.\Scsi[0-31] for up to 10 NVMe devices
4633 for (int i
= 0; i
< 32; i
++) {
4634 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4635 win_nvme_device
test_dev(this, name
, "", 0);
4636 if (!test_dev
.open_scsi(i
)) {
4637 if (test_dev
.get_errno() == EACCES
)
4642 if (!test_dev
.probe())
4644 if (++nvme_cnt
>= 10)
4648 for (int i
= 0; i
< nvme_cnt
; i
++) {
4649 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4650 devlist
.push_back( new win_nvme_device(this, name
, "nvme", 0) );
4657 // get examples for smartctl
4658 std::string
win_smart_interface::get_app_examples(const char * appname
)
4660 if (strcmp(appname
, "smartctl"))
4662 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
4663 " smartctl -a /dev/sda (Prints all SMART information)\n\n"
4664 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
4665 " (Enables SMART on first disk)\n\n"
4666 " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n"
4667 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
4668 " (Prints Self-Test & Attribute errors)\n"
4669 " smartctl -a /dev/sda\n"
4670 " (Prints all information for disk on PhysicalDrive 0)\n"
4671 " smartctl -a /dev/pd3\n"
4672 " (Prints all information for disk on PhysicalDrive 3)\n"
4673 " smartctl -a /dev/tape1\n"
4674 " (Prints all information for SCSI tape on Tape 1)\n"
4675 " smartctl -A /dev/hdb,3\n"
4676 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
4677 " smartctl -A /dev/tw_cli/c0/p1\n"
4678 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
4679 " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
4680 " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
4681 " on 1st Areca RAID controller)\n"
4683 " ATA SMART access methods and ordering may be specified by modifiers\n"
4684 " following the device name: /dev/hdX:[saicm], where\n"
4685 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
4686 " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
4687 " 'm': IOCTL_SCSI_MINIPORT_*.\n"
4689 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
4694 bool win_smart_interface::disable_system_auto_standby(bool disable
)
4697 SYSTEM_POWER_STATUS ps
;
4698 if (!GetSystemPowerStatus(&ps
))
4699 return set_err(ENOSYS
, "Unknown power status");
4700 if (ps
.ACLineStatus
!= 1) {
4701 SetThreadExecutionState(ES_CONTINUOUS
);
4702 if (ps
.ACLineStatus
== 0)
4703 set_err(EIO
, "AC offline");
4705 set_err(EIO
, "Unknown AC line status");
4710 if (!SetThreadExecutionState(ES_CONTINUOUS
| (disable
? ES_SYSTEM_REQUIRED
: 0)))
4711 return set_err(ENOSYS
);
4718 /////////////////////////////////////////////////////////////////////////////
4720 // Initialize platform interface and register with smi()
4721 void smart_interface::init()
4724 // Remove "." from DLL search path if supported
4725 // to prevent DLL preloading attacks
4726 BOOL (WINAPI
* SetDllDirectoryA_p
)(LPCSTR
) = (BOOL (WINAPI
*)(LPCSTR
))
4727 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4728 if (SetDllDirectoryA_p
)
4729 SetDllDirectoryA_p("");
4732 static os_win32::win_smart_interface the_win_interface
;
4733 smart_interface::set(&the_win_interface
);
4739 // Get exe directory
4740 // (prototype in utiliy.h)
4741 std::string
get_exe_dir()
4743 char path
[MAX_PATH
];
4744 // Get path of this exe
4745 if (!GetModuleFileNameA(GetModuleHandleA(0), path
, sizeof(path
)))
4746 throw std::runtime_error("GetModuleFileName() failed");
4747 // Replace backslash by slash
4749 for (int i
= 0; path
[i
]; i
++)
4750 if (path
[i
] == '\\') {
4751 path
[i
] = '/'; sl
= i
;