4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-18 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 * SPDX-License-Identifier: GPL-2.0-or-later
19 #define _WIN32_WINNT WINVER
26 #include "dev_interface.h"
27 #include "dev_ata_cmd_set.h"
28 #include "dev_areca.h"
30 #include "os_win32/wmiquery.h"
31 #include "os_win32/popen.h"
33 // TODO: Move from smartctl.h to other include file
34 extern unsigned char failuretest_permissive
;
42 #define assert(x) /* */
45 #include <stddef.h> // offsetof()
46 #include <io.h> // access()
48 // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
49 #define WIN32_LEAN_AND_MEAN
53 // i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32
54 // (Missing: FILE_DEVICE_SCSI)
59 #elif HAVE_DDK_NTDDDISK_H
60 // older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
61 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
62 #include <ddk/ntdddisk.h>
63 #include <ddk/ntddscsi.h>
64 #include <ddk/ntddstor.h>
66 // MSVC10, older MinGW
67 // (Missing: IOCTL_SCSI_MINIPORT_*)
73 // csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
74 // (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
84 // Silence -Wunused-local-typedefs warning from g++ >= 4.8
86 #define ATTR_UNUSED __attribute__((unused))
88 #define ATTR_UNUSED /**/
91 // Macro to check constants at compile time using a dummy typedef
92 #define ASSERT_CONST(c, n) \
93 typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED
94 #define ASSERT_SIZEOF(t, n) \
95 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED
98 #define SELECT_WIN_32_64(x32, x64) (x32)
100 #define SELECT_WIN_32_64(x32, x64) (x64)
103 // Cygwin does no longer provide strn?icmp() compatibility macros
104 // MSVCRT does not provide strn?casecmp()
105 #if defined(__CYGWIN__) && !defined(stricmp)
106 #define stricmp strcasecmp
107 #define strnicmp strncasecmp
110 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 4848 2018-12-05 18:30:46Z chrfranke $";
112 /////////////////////////////////////////////////////////////////////////////
113 // Windows I/O-controls, some declarations are missing in the include files
117 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
119 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
120 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
121 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
122 ASSERT_SIZEOF(GETVERSIONINPARAMS
, 24);
123 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
124 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
127 // IDE PASS THROUGH (2000, XP, undocumented)
129 #ifndef IOCTL_IDE_PASS_THROUGH
131 #define IOCTL_IDE_PASS_THROUGH \
132 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
134 #endif // IOCTL_IDE_PASS_THROUGH
140 ULONG DataBufferSize
;
146 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
147 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
150 // ATA PASS THROUGH (Win2003, XP SP2)
152 #ifndef IOCTL_ATA_PASS_THROUGH
154 #define IOCTL_ATA_PASS_THROUGH \
155 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
157 typedef struct _ATA_PASS_THROUGH_EX
{
163 UCHAR ReservedAsUchar
;
164 ULONG DataTransferLength
;
166 ULONG ReservedAsUlong
;
167 ULONG_PTR DataBufferOffset
;
168 UCHAR PreviousTaskFile
[8];
169 UCHAR CurrentTaskFile
[8];
170 } ATA_PASS_THROUGH_EX
;
172 #define ATA_FLAGS_DRDY_REQUIRED 0x01
173 #define ATA_FLAGS_DATA_IN 0x02
174 #define ATA_FLAGS_DATA_OUT 0x04
175 #define ATA_FLAGS_48BIT_COMMAND 0x08
176 #define ATA_FLAGS_USE_DMA 0x10
177 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
179 #endif // IOCTL_ATA_PASS_THROUGH
181 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
182 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, SELECT_WIN_32_64(40, 48));
185 // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
187 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
188 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT
, 0x04d014);
189 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, SELECT_WIN_32_64(44, 56));
190 ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT
, SELECT_WIN_32_64(44, 56));
193 // SMART IOCTL via SCSI MINIPORT ioctl
195 #ifndef FILE_DEVICE_SCSI
196 #define FILE_DEVICE_SCSI 0x001b
199 #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
201 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
202 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
203 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
204 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
205 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
206 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
207 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
208 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
209 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
210 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
211 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
212 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
213 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
215 #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
217 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
218 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
221 // IOCTL_STORAGE_QUERY_PROPERTY
223 #ifndef IOCTL_STORAGE_QUERY_PROPERTY
225 #define IOCTL_STORAGE_QUERY_PROPERTY \
226 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
228 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
232 UCHAR DeviceTypeModifier
;
233 BOOLEAN RemovableMedia
;
234 BOOLEAN CommandQueueing
;
235 ULONG VendorIdOffset
;
236 ULONG ProductIdOffset
;
237 ULONG ProductRevisionOffset
;
238 ULONG SerialNumberOffset
;
239 STORAGE_BUS_TYPE BusType
;
240 ULONG RawPropertiesLength
;
241 UCHAR RawDeviceProperties
[1];
242 } STORAGE_DEVICE_DESCRIPTOR
;
244 typedef enum _STORAGE_QUERY_TYPE
{
245 PropertyStandardQuery
= 0,
248 PropertyQueryMaxDefined
249 } STORAGE_QUERY_TYPE
;
251 typedef enum _STORAGE_PROPERTY_ID
{
252 StorageDeviceProperty
= 0,
253 StorageAdapterProperty
,
254 StorageDeviceIdProperty
,
255 StorageDeviceUniqueIdProperty
,
256 StorageDeviceWriteCacheProperty
,
257 StorageMiniportProperty
,
258 StorageAccessAlignmentProperty
259 } STORAGE_PROPERTY_ID
;
261 typedef struct _STORAGE_PROPERTY_QUERY
{
262 STORAGE_PROPERTY_ID PropertyId
;
263 STORAGE_QUERY_TYPE QueryType
;
264 UCHAR AdditionalParameters
[1];
265 } STORAGE_PROPERTY_QUERY
;
267 #endif // IOCTL_STORAGE_QUERY_PROPERTY
269 ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY
, 0x002d1400);
270 ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR
, 36+1+3);
271 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY
, 8+1+3);
274 // IOCTL_STORAGE_QUERY_PROPERTY: Windows 10 enhancements
278 // enum STORAGE_PROPERTY_ID: new values
279 const STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty
= (STORAGE_PROPERTY_ID
)49;
280 const STORAGE_PROPERTY_ID StorageDeviceProtocolSpecificProperty
= (STORAGE_PROPERTY_ID
)50;
282 typedef enum _STORAGE_PROTOCOL_TYPE
{
283 ProtocolTypeUnknown
= 0,
288 } STORAGE_PROTOCOL_TYPE
;
290 typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE
{
291 NVMeDataTypeUnknown
= 0,
292 NVMeDataTypeIdentify
,
295 } STORAGE_PROTOCOL_NVME_DATA_TYPE
;
297 typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA
{
298 STORAGE_PROTOCOL_TYPE ProtocolType
;
300 ULONG ProtocolDataRequestValue
;
301 ULONG ProtocolDataRequestSubValue
;
302 ULONG ProtocolDataOffset
;
303 ULONG ProtocolDataLength
;
304 ULONG FixedProtocolReturnData
;
306 } STORAGE_PROTOCOL_SPECIFIC_DATA
;
308 ASSERT_SIZEOF(STORAGE_PROTOCOL_SPECIFIC_DATA
, 40);
313 // IOCTL_STORAGE_PREDICT_FAILURE
315 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE
, 0x002d1100);
316 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
319 // 3ware specific versions of SMART ioctl structs
321 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
325 typedef struct _GETVERSIONINPARAMS_EX
{
331 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
332 WORD wIdentifier
; // Vendor specific identifier
333 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
335 } GETVERSIONINPARAMS_EX
;
337 typedef struct _SENDCMDINPARAMS_EX
{
341 BYTE bPortNumber
; // 3ware specific: port number
342 WORD wIdentifier
; // Vendor specific identifier
345 } SENDCMDINPARAMS_EX
;
349 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONINPARAMS
));
350 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
355 #ifndef NVME_PASS_THROUGH_SRB_IO_CODE
357 #define NVME_SIG_STR "NvmeMini"
358 #define NVME_STORPORT_DRIVER 0xe000
360 #define NVME_PASS_THROUGH_SRB_IO_CODE \
361 CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
364 typedef struct _NVME_PASS_THROUGH_IOCTL
366 SRB_IO_CONTROL SrbIoCtrl
;
367 ULONG VendorSpecific
[6];
368 ULONG NVMeCmd
[16]; // Command DW[0...15]
369 ULONG CplEntry
[4]; // Completion DW[0...3]
370 ULONG Direction
; // 0=No, 1=Out, 2=In, 3=I/O
371 ULONG QueueId
; // 0=AdminQ
372 ULONG DataBufferLen
; // sizeof(DataBuffer) if Data In
374 ULONG ReturnBufferLen
; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
376 } NVME_PASS_THROUGH_IOCTL
;
379 #endif // NVME_PASS_THROUGH_SRB_IO_CODE
381 ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE
, (int)0xe0002000);
382 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, 152+1);
383 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
)+1);
388 ASSERT_SIZEOF(IOCTL_HEADER
, sizeof(SRB_IO_CONTROL
));
389 ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER
, 204);
390 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER
, 2080);
391 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER
, 168);
395 ASSERT_SIZEOF(SCSI_REQUEST_BLOCK
, SELECT_WIN_32_64(64, 88));
399 /////////////////////////////////////////////////////////////////////////////
401 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
404 #pragma warning(disable:4250)
407 static int is_permissive()
409 if (!failuretest_permissive
) {
410 pout("To continue, add one or more '-T permissive' options.\n");
413 failuretest_permissive
--;
417 // return number for drive letter, -1 on error
418 // "[A-Za-z]:([/\\][.]?)?" => 0-25
419 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
420 static int drive_letter(const char * s
)
422 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
424 && (!s
[2] || ( strchr("/\\\"", s
[2])
425 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
426 (s
[0] & 0x1f) - 1 : -1);
429 // Skip trailing "/dev/", do not allow "/dev/X:"
430 static const char * skipdev(const char * s
)
432 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
435 // "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
436 static int sdxy_to_phydrive(const char (& xy
)[2+1])
438 int phydrive
= xy
[0] - 'a';
440 phydrive
= (phydrive
+ 1) * ('z' - 'a' + 1) + (xy
[1] - 'a');
444 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
446 int srclen
= strcspn(src
, "\r\n");
448 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
449 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
451 if (i
< destsize
-1 && i
< srclen
)
456 /////////////////////////////////////////////////////////////////////////////
459 class win_smart_device
460 : virtual public /*implements*/ smart_device
464 : smart_device(never_called
),
465 m_fh(INVALID_HANDLE_VALUE
)
468 virtual ~win_smart_device() throw();
470 virtual bool is_open() const;
472 virtual bool close();
475 /// Set handle for open() in derived classes.
476 void set_fh(HANDLE fh
)
479 /// Return handle for derived classes.
480 HANDLE
get_fh() const
484 HANDLE m_fh
; ///< File handle
488 // Common routines for devices with HANDLEs
490 win_smart_device::~win_smart_device() throw()
492 if (m_fh
!= INVALID_HANDLE_VALUE
)
496 bool win_smart_device::is_open() const
498 return (m_fh
!= INVALID_HANDLE_VALUE
);
501 bool win_smart_device::close()
503 if (m_fh
== INVALID_HANDLE_VALUE
)
505 BOOL rc
= ::CloseHandle(m_fh
);
506 m_fh
= INVALID_HANDLE_VALUE
;
511 /////////////////////////////////////////////////////////////////////////////
513 #define SMART_CYL_LOW 0x4F
514 #define SMART_CYL_HI 0xC2
516 static void print_ide_regs(const IDEREGS
* r
, int out
)
518 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
519 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
520 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
523 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
525 pout(" Input : "); print_ide_regs(ri
, 0);
527 pout(" Output: "); print_ide_regs(ro
, 1);
531 /////////////////////////////////////////////////////////////////////////////
533 // call SMART_GET_VERSION, return device map or -1 on error
535 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
537 GETVERSIONINPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
538 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
541 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
542 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
544 pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
548 assert(num_out
== sizeof(GETVERSIONINPARAMS
));
550 if (ata_debugmode
> 1) {
551 pout(" SMART_GET_VERSION succeeded, bytes returned: %u\n"
552 " Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
553 (unsigned)num_out
, vers
.bVersion
, vers
.bRevision
,
554 (unsigned)vers
.fCapabilities
, vers
.bIDEDeviceMap
);
555 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
556 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
557 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, (unsigned)vers_ex
.dwDeviceMapEx
);
561 *ata_version_ex
= vers_ex
;
563 // TODO: Check vers.fCapabilities here?
564 return vers
.bIDEDeviceMap
;
568 // call SMART_* ioctl
570 static int smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
572 SENDCMDINPARAMS inpar
;
573 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
575 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
576 const SENDCMDOUTPARAMS
* outpar
;
578 unsigned int size_out
;
581 memset(&inpar
, 0, sizeof(inpar
));
582 inpar
.irDriveRegs
= *regs
;
584 // Older drivers may require bits 5 and 7 set
585 // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
586 inpar
.irDriveRegs
.bDriveHeadReg
|= 0xa0;
588 // Drive number 0-3 was required on Win9x/ME only
589 //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
590 //inpar.bDriveNumber = drive;
594 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
595 inpar_ex
.bPortNumber
= port
;
598 if (datasize
== 512) {
599 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
600 inpar
.cBufferSize
= size_out
= 512;
602 else if (datasize
== 0) {
603 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
604 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
605 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
606 // Note: cBufferSize must be 0 on Win9x
615 memset(&outbuf
, 0, sizeof(outbuf
));
617 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
618 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
619 // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
620 long err
= GetLastError();
621 if (ata_debugmode
&& (err
!= ERROR_INVALID_PARAMETER
|| ata_debugmode
> 1)) {
622 pout(" %s failed, Error=%ld\n", name
, err
);
623 print_ide_regs_io(regs
, NULL
);
625 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
626 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
627 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
630 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
632 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
634 if (outpar
->DriverStatus
.bDriverError
) {
636 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
637 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
638 print_ide_regs_io(regs
, NULL
);
640 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
644 if (ata_debugmode
> 1) {
645 pout(" %s succeeded, bytes returned: %u (buffer %u)\n", name
,
646 (unsigned)num_out
, (unsigned)outpar
->cBufferSize
);
647 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
648 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
652 memcpy(data
, outpar
->bBuffer
, 512);
653 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
654 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
655 memcpy(regs
, outpar
->bBuffer
, sizeof(IDEREGS
));
656 else { // Workaround for driver not returning regs
658 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
659 *regs
= inpar
.irDriveRegs
;
667 /////////////////////////////////////////////////////////////////////////////
668 // IDE PASS THROUGH (2000, XP, undocumented)
670 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
671 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
673 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
675 if (datasize
> 512) {
679 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
680 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
682 const unsigned char magic
= 0xcf;
690 buf
->DataBufferSize
= datasize
;
692 buf
->DataBuffer
[0] = magic
;
694 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
695 buf
, size
, buf
, size
, &num_out
, NULL
)) {
696 long err
= GetLastError();
698 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
699 print_ide_regs_io(regs
, NULL
);
701 VirtualFree(buf
, 0, MEM_RELEASE
);
702 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
707 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
709 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
710 print_ide_regs_io(regs
, &buf
->IdeReg
);
712 VirtualFree(buf
, 0, MEM_RELEASE
);
717 // Check and copy data
720 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
722 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
723 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
724 print_ide_regs_io(regs
, &buf
->IdeReg
);
726 VirtualFree(buf
, 0, MEM_RELEASE
);
730 memcpy(data
, buf
->DataBuffer
, datasize
);
733 if (ata_debugmode
> 1) {
734 pout(" IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %u (buffer %u)\n",
735 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
736 print_ide_regs_io(regs
, &buf
->IdeReg
);
740 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
741 VirtualFree(buf
, 0, MEM_RELEASE
);
746 /////////////////////////////////////////////////////////////////////////////
747 // ATA PASS THROUGH (Win2003, XP SP2)
750 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
751 // transfer per command. Therefore, multi-sector transfers are only supported
752 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
753 // or READ/WRITE LOG EXT work only with single sector transfers.
754 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
756 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
758 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
760 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
763 ATA_PASS_THROUGH_EX apt
;
765 UCHAR ucDataBuf
[max_sectors
* 512];
766 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
768 const unsigned char magic
= 0xcf;
770 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
771 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
773 //ab.apt.TargetId = 0;
775 ab
.apt
.TimeOutValue
= 60; // seconds
776 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
777 ab
.apt
.DataBufferOffset
= size
;
780 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
784 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
785 ab
.apt
.DataTransferLength
= datasize
;
787 ab
.ucDataBuf
[0] = magic
;
789 else if (datasize
< 0) {
790 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
794 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
795 ab
.apt
.DataTransferLength
= -datasize
;
797 memcpy(ab
.ucDataBuf
, data
, -datasize
);
800 assert(ab
.apt
.AtaFlags
== 0);
801 assert(ab
.apt
.DataTransferLength
== 0);
804 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
805 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
806 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
810 *ptfregs
= *prev_regs
;
811 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
815 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
816 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
817 long err
= GetLastError();
819 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
820 print_ide_regs_io(regs
, NULL
);
822 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
827 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
829 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
830 print_ide_regs_io(regs
, ctfregs
);
836 // Check and copy data
839 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
841 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out
);
842 print_ide_regs_io(regs
, ctfregs
);
847 memcpy(data
, ab
.ucDataBuf
, datasize
);
850 if (ata_debugmode
> 1) {
851 pout(" IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out
);
852 print_ide_regs_io(regs
, ctfregs
);
856 *prev_regs
= *ptfregs
;
862 /////////////////////////////////////////////////////////////////////////////
863 // SMART IOCTL via SCSI MINIPORT ioctl
865 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
866 // miniport driver (via SCSI port driver scsiport.sys).
867 // It can be used to skip the missing or broken handling of some SMART
868 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
870 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
873 DWORD code
= 0; const char * name
= 0;
874 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
875 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
877 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
878 case ATA_SMART_READ_VALUES
:
879 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
880 case ATA_SMART_READ_THRESHOLDS
:
881 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
882 case ATA_SMART_ENABLE
:
883 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
884 case ATA_SMART_DISABLE
:
885 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
886 case ATA_SMART_STATUS
:
887 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
888 case ATA_SMART_AUTOSAVE
:
889 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
890 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
891 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
892 case ATA_SMART_IMMEDIATE_OFFLINE
:
893 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
894 case ATA_SMART_AUTO_OFFLINE
:
895 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
896 case ATA_SMART_READ_LOG_SECTOR
:
897 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
898 case ATA_SMART_WRITE_LOG_SECTOR
:
899 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
911 SENDCMDOUTPARAMS out
;
915 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
916 memset(&sb
, 0, sizeof(sb
));
920 if (datasize
> (int)sizeof(sb
.space
)+1) {
926 else if (datasize
< 0) {
927 if (-datasize
> (int)sizeof(sb
.space
)+1) {
932 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
934 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
935 size
= sizeof(IDEREGS
);
938 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
939 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
940 sb
.srbc
.Timeout
= 60; // seconds
941 sb
.srbc
.ControlCode
= code
;
942 //sb.srbc.ReturnCode = 0;
943 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
944 sb
.params
.in
.irDriveRegs
= *regs
;
945 sb
.params
.in
.cBufferSize
= size
;
947 // Call miniport ioctl
948 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
950 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
951 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
952 long err
= GetLastError();
954 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
955 print_ide_regs_io(regs
, NULL
);
957 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
962 if (sb
.srbc
.ReturnCode
) {
964 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name
, (unsigned)sb
.srbc
.ReturnCode
);
965 print_ide_regs_io(regs
, NULL
);
971 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
973 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
974 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
975 print_ide_regs_io(regs
, NULL
);
977 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
981 if (ata_debugmode
> 1) {
982 pout(" IOCTL_SCSI_MINIPORT_%s succeeded, bytes returned: %u (buffer %u)\n", name
,
983 (unsigned)num_out
, (unsigned)sb
.params
.out
.cBufferSize
);
984 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
985 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
989 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
990 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
991 memcpy(regs
, sb
.params
.out
.bBuffer
, sizeof(IDEREGS
));
997 /////////////////////////////////////////////////////////////////////////////
998 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1000 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1003 SRB_IO_CONTROL srbc
;
1007 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1009 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1013 memset(&sb
, 0, sizeof(sb
));
1014 strncpy((char *)sb
.srbc
.Signature
, "<3ware>", sizeof(sb
.srbc
.Signature
));
1015 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1016 sb
.srbc
.Timeout
= 60; // seconds
1017 sb
.srbc
.ControlCode
= 0xA0000000;
1018 sb
.srbc
.ReturnCode
= 0;
1019 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1021 sb
.regs
.bReserved
= port
;
1024 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1025 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1026 long err
= GetLastError();
1027 if (ata_debugmode
) {
1028 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1029 print_ide_regs_io(regs
, NULL
);
1031 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1035 if (sb
.srbc
.ReturnCode
) {
1036 if (ata_debugmode
) {
1037 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb
.srbc
.ReturnCode
);
1038 print_ide_regs_io(regs
, NULL
);
1046 memcpy(data
, sb
.buffer
, datasize
);
1048 if (ata_debugmode
> 1) {
1049 pout(" ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out
);
1050 print_ide_regs_io(regs
, &sb
.regs
);
1058 /////////////////////////////////////////////////////////////////////////////
1060 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1061 // 3DM/CLI "Rescan Controller" function does not to always update it.
1063 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1065 SRB_IO_CONTROL srbc
;
1066 memset(&srbc
, 0, sizeof(srbc
));
1067 strncpy((char *)srbc
.Signature
, "<3ware>", sizeof(srbc
.Signature
));
1068 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1069 srbc
.Timeout
= 60; // seconds
1070 srbc
.ControlCode
= 0xCC010014;
1071 srbc
.ReturnCode
= 0;
1075 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1076 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1077 long err
= GetLastError();
1079 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1080 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1083 if (srbc
.ReturnCode
) {
1085 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc
.ReturnCode
);
1089 if (ata_debugmode
> 1)
1090 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n");
1095 /////////////////////////////////////////////////////////////////////////////
1096 // IOCTL_STORAGE_QUERY_PROPERTY
1098 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1099 STORAGE_DEVICE_DESCRIPTOR desc
;
1103 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1104 // (This works without admin rights)
1106 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1108 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
1109 memset(data
, 0, sizeof(*data
));
1112 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1113 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1114 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
1115 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
1120 if (ata_debugmode
> 1 || scsi_debugmode
> 1) {
1121 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1123 " Product: \"%s\"\n"
1124 " Revision: \"%s\"\n"
1126 " BusType: 0x%02x\n",
1127 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: "(null)"),
1128 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: "(null)"),
1129 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: "(null)"),
1130 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1137 /////////////////////////////////////////////////////////////////////////////
1138 // IOCTL_STORAGE_PREDICT_FAILURE
1140 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1141 // or -1 on error, optionally return VendorSpecific data.
1142 // (This works without admin rights)
1144 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1146 STORAGE_PREDICT_FAILURE pred
;
1147 memset(&pred
, 0, sizeof(pred
));
1150 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
1151 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
1152 if (ata_debugmode
> 1)
1153 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
1158 if (ata_debugmode
> 1) {
1159 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1160 " PredictFailure: 0x%08x\n"
1161 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1162 (unsigned)pred
.PredictFailure
,
1163 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
1164 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
1168 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
1169 return (!pred
.PredictFailure
? 0 : 1);
1173 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1174 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
1176 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1177 if (storage_query_property_ioctl(hdevice
, &data
))
1180 memset(id
, 0, sizeof(*id
));
1182 // Some drivers split ATA model string into VendorId and ProductId,
1183 // others return it as ProductId only.
1184 char model
[sizeof(id
->model
) + 1] = "";
1187 if (data
.desc
.VendorIdOffset
) {
1188 for ( ;i
< sizeof(model
)-1 && data
.raw
[data
.desc
.VendorIdOffset
+i
]; i
++)
1189 model
[i
] = data
.raw
[data
.desc
.VendorIdOffset
+i
];
1192 if (data
.desc
.ProductIdOffset
) {
1193 while (i
> 1 && model
[i
-2] == ' ') // Keep last blank from VendorId
1195 // Ignore VendorId "ATA"
1196 if (i
<= 4 && !strncmp(model
, "ATA", 3) && (i
== 3 || model
[3] == ' '))
1198 for (unsigned j
= 0; i
< sizeof(model
)-1 && data
.raw
[data
.desc
.ProductIdOffset
+j
]; i
++, j
++)
1199 model
[i
] = data
.raw
[data
.desc
.ProductIdOffset
+j
];
1202 while (i
> 0 && model
[i
-1] == ' ')
1205 copy_swapped(id
->model
, model
, sizeof(id
->model
));
1207 if (data
.desc
.ProductRevisionOffset
)
1208 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
1210 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1211 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1215 // Get Serial Number in IDENTIFY from WMI
1216 static bool get_serial_from_wmi(int drive
, ata_identify_device
* id
)
1218 bool debug
= (ata_debugmode
> 1);
1221 if (!ws
.connect()) {
1223 pout("WMI connect failed\n");
1228 if (!ws
.query1(wo
, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
1229 "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive
))
1232 std::string serial
= wo
.get_str("SerialNumber");
1234 pout(" WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive
, wo
.get_str("Model").c_str(), serial
.c_str());
1236 copy_swapped(id
->serial_no
, serial
.c_str(), sizeof(id
->serial_no
));
1241 /////////////////////////////////////////////////////////////////////////////
1242 // USB ID detection using WMI
1244 // Get USB ID for a physical or logical drive number
1245 static bool get_usb_id(int phydrive
, int logdrive
,
1246 unsigned short & vendor_id
,
1247 unsigned short & product_id
)
1249 bool debug
= (scsi_debugmode
> 1);
1252 if (!ws
.connect()) {
1254 pout("WMI connect failed\n");
1262 if (0 <= logdrive
&& logdrive
<= 'Z'-'A') {
1263 // Drive letter -> Partition info
1264 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition",
1268 std::string partid
= wo
.get_str("DeviceID");
1270 pout("%c: --> \"%s\" -->\n", 'A'+logdrive
, partid
.c_str());
1272 // Partition ID -> Physical drive info
1273 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
1277 name
= wo
.get_str("Model");
1279 pout("%s --> \"%s\":\n", wo
.get_str("DeviceID").c_str(), name
.c_str());
1282 else if (phydrive
>= 0) {
1283 // Physical drive number -> Physical drive info
1284 if (!ws
.query1(wo
, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive
))
1287 name
= wo
.get_str("Model");
1289 pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive
, name
.c_str());
1295 // Get USB_CONTROLLER -> DEVICE associations
1297 if (!ws
.query(we
, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
1300 unsigned short usb_venid
= 0, prev_usb_venid
= 0;
1301 unsigned short usb_proid
= 0, prev_usb_proid
= 0;
1302 std::string prev_usb_ant
;
1303 std::string prev_ant
, ant
, dep
;
1305 const regular_expression
regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"");
1307 while (we
.next(wo
)) {
1309 // Find next 'USB_CONTROLLER, DEVICE' pair
1310 ant
= wo
.get_str("Antecedent");
1311 dep
= wo
.get_str("Dependent");
1313 if (debug
&& ant
!= prev_ant
)
1314 pout(" %s:\n", ant
.c_str());
1317 regular_expression::match_range match
[2];
1318 if (!(regex
.execute(dep
.c_str(), 2, match
) && match
[1].rm_so
>= 0)) {
1320 pout(" | (\"%s\")\n", dep
.c_str());
1324 std::string
devid(dep
.c_str()+match
[1].rm_so
, match
[1].rm_eo
-match
[1].rm_so
);
1326 if (str_starts_with(devid
, "USB\\\\VID_")) {
1327 // USB bridge entry, save CONTROLLER, ID
1329 if (!(sscanf(devid
.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
1330 &prev_usb_venid
, &prev_usb_proid
, &nc
) == 2 && nc
== 9+4+5+4)) {
1331 prev_usb_venid
= prev_usb_proid
= 0;
1335 pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid
.c_str(), prev_usb_venid
, prev_usb_proid
);
1337 else if (str_starts_with(devid
, "USBSTOR\\\\") || str_starts_with(devid
, "SCSI\\\\")) {
1338 // USBSTORage or SCSI device found
1340 pout(" +--> \"%s\"\n", devid
.c_str());
1344 if (!ws
.query1(wo2
, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid
.c_str()))
1346 std::string name2
= wo2
.get_str("Name");
1348 // Continue if not name of physical disk drive
1349 if (name2
!= name
) {
1351 pout(" +---> (\"%s\")\n", name2
.c_str());
1355 // Fail if previous USB bridge is associated to other controller or ID is unknown
1356 if (!(ant
== prev_usb_ant
&& prev_usb_venid
)) {
1358 pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2
.c_str());
1362 // Handle multiple devices with same name
1364 // Fail if multiple devices with same name have different USB bridge types
1365 if (!(usb_venid
== prev_usb_venid
&& usb_proid
== prev_usb_proid
)) {
1367 pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2
.c_str());
1373 usb_venid
= prev_usb_venid
;
1374 usb_proid
= prev_usb_proid
;
1376 pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2
.c_str(), usb_venid
, usb_proid
);
1378 // Continue to check for duplicate names ...
1382 pout(" | \"%s\"\n", devid
.c_str());
1389 vendor_id
= usb_venid
;
1390 product_id
= usb_proid
;
1396 /////////////////////////////////////////////////////////////////////////////
1398 // Call GetDevicePowerState()
1399 // returns: 1=active, 0=standby, -1=error
1400 // (This would also work for SCSI drives)
1402 static int get_device_power_state(HANDLE hdevice
)
1405 if (!GetDevicePowerState(hdevice
, &state
)) {
1406 long err
= GetLastError();
1408 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
1409 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1410 // TODO: This may not work as expected on transient errors,
1411 // because smartd interprets -1 as SLEEP mode regardless of errno.
1415 if (ata_debugmode
> 1)
1416 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
1421 /////////////////////////////////////////////////////////////////////////////
1424 class win_ata_device
1425 : public /*implements*/ ata_device
,
1426 public /*extends*/ win_smart_device
1429 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
1431 virtual ~win_ata_device() throw();
1433 virtual bool open();
1435 virtual bool is_powered_down();
1437 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
1439 virtual bool ata_identify_is_cached() const;
1442 bool open(bool query_device
);
1444 bool open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
);
1446 std::string m_options
;
1447 bool m_usr_options
; // options set by user?
1448 bool m_admin
; // open with admin access?
1449 int m_phydrive
; // PhysicalDriveN or -1
1450 bool m_id_is_cached
; // ata_identify_is_cached() return value.
1451 bool m_is_3ware
; // LSI/3ware controller detected?
1452 int m_port
; // LSI/3ware port
1453 int m_smartver_state
;
1457 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1458 : smart_device(intf
, dev_name
, "ata", req_type
),
1459 m_usr_options(false),
1462 m_id_is_cached(false),
1469 win_ata_device::~win_ata_device() throw()
1473 // Get default ATA device options
1475 static const char * ata_get_def_options()
1477 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
1478 // STORAGE_*, SCSI_MINIPORT_*
1483 bool win_ata_device::open()
1485 // Open device for r/w operations
1489 bool win_ata_device::open(bool query_device
)
1491 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
1492 // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
1493 char drive
[2+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
1494 if ( sscanf(name
, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive
, &n1
, options
, &n2
) >= 1
1495 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1496 return open(sdxy_to_phydrive(drive
), -1, options
, -1, query_device
);
1498 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
1499 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
1501 if ( sscanf(name
, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
1502 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1503 return open(sdxy_to_phydrive(drive
), -1, options
, port
, query_device
);
1505 // pd<m>,N => Physical drive <m>, RAID port N
1506 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
1507 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
1508 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
1509 return open(phydrive
, -1, "", (int)port
, query_device
);
1511 // [a-zA-Z]: => Physical drive behind logical drive 0-25
1512 int logdrive
= drive_letter(name
);
1513 if (logdrive
>= 0) {
1514 return open(-1, logdrive
, "", -1, query_device
);
1517 return set_err(EINVAL
);
1521 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
)
1525 if (0 <= phydrive
&& phydrive
<= 255)
1526 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive
= phydrive
));
1527 else if (0 <= logdrive
&& logdrive
<= 'Z'-'A')
1528 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
1530 return set_err(ENOENT
);
1533 HANDLE h
= INVALID_HANDLE_VALUE
;
1534 if (!(*options
&& !options
[strspn(options
, "fp")]) && !query_device
) {
1535 // Open with admin rights
1537 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
1538 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1539 NULL
, OPEN_EXISTING
, 0, 0);
1541 if (h
== INVALID_HANDLE_VALUE
) {
1542 // Open without admin rights
1544 h
= CreateFileA(devpath
, 0,
1545 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1546 NULL
, OPEN_EXISTING
, 0, 0);
1548 if (h
== INVALID_HANDLE_VALUE
) {
1549 long err
= GetLastError();
1550 if (err
== ERROR_FILE_NOT_FOUND
)
1551 set_err(ENOENT
, "%s: not found", devpath
);
1552 else if (err
== ERROR_ACCESS_DENIED
)
1553 set_err(EACCES
, "%s: access denied", devpath
);
1555 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
1560 // Warn once if admin rights are missing
1561 if (!m_admin
&& !query_device
) {
1562 static bool noadmin_warning
= false;
1563 if (!noadmin_warning
) {
1564 pout("Warning: Limited functionality due to missing admin rights\n");
1565 noadmin_warning
= true;
1569 if (ata_debugmode
> 1)
1570 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
1572 m_usr_options
= false;
1574 // Save user options
1575 m_options
= options
; m_usr_options
= true;
1578 // RAID: SMART_* and SCSI_MINIPORT
1581 // Set default options according to Windows version
1582 static const char * def_options
= ata_get_def_options();
1583 m_options
= def_options
;
1586 // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
1591 // 3ware RAID: Get port map
1592 GETVERSIONINPARAMS_EX vers_ex
;
1593 int devmap
= smart_get_version(h
, &vers_ex
);
1595 // 3ware RAID if vendor id present
1596 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1598 unsigned portmap
= 0;
1599 if (port
>= 0 && devmap
>= 0) {
1600 // 3ware RAID: check vendor id
1602 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
1603 "This is no 3ware 9000 controller or driver has no SMART support.\n",
1604 vers_ex
.wIdentifier
);
1608 portmap
= vers_ex
.dwDeviceMapEx
;
1611 pout("%s: ATA driver has no SMART support\n", devpath
);
1612 if (!is_permissive()) {
1614 return set_err(ENOSYS
);
1617 m_smartver_state
= 1;
1620 // 3ware RAID: update devicemap first
1621 if (!update_3ware_devicemap_ioctl(h
)) {
1622 if ( smart_get_version(h
, &vers_ex
) >= 0
1623 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
1624 portmap
= vers_ex
.dwDeviceMapEx
;
1626 // Check port existence
1627 if (!(portmap
& (1U << port
))) {
1628 if (!is_permissive()) {
1630 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
1639 /////////////////////////////////////////////////////////////////////////////
1641 // Query OS if device is powered up or down.
1642 bool win_ata_device::is_powered_down()
1644 // To check power mode, we open device for query operations only.
1645 // Opening for SMART r/w operations can already spin up the disk.
1646 bool self_open
= !is_open();
1650 int rc
= get_device_power_state(get_fh());
1656 /////////////////////////////////////////////////////////////////////////////
1658 // Interface to ATA devices
1659 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
1661 // No multi-sector support for now, see above
1662 // warning about IOCTL_ATA_PASS_THROUGH
1663 if (!ata_cmd_is_supported(in
,
1664 ata_device::supports_data_out
|
1665 ata_device::supports_output_regs
|
1666 ata_device::supports_48bit
)
1670 // 3ware RAID: SMART DISABLE without port number disables SMART functions
1671 if ( m_is_3ware
&& m_port
< 0
1672 && in
.in_regs
.command
== ATA_SMART_CMD
1673 && in
.in_regs
.features
== ATA_SMART_DISABLE
)
1674 return set_err(ENOSYS
, "SMART DISABLE requires 3ware port number");
1676 // Determine ioctl functions valid for this ATA cmd
1677 const char * valid_options
= 0;
1679 switch (in
.in_regs
.command
) {
1680 case ATA_IDENTIFY_DEVICE
:
1681 case ATA_IDENTIFY_PACKET_DEVICE
:
1682 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1683 // and SCSI_MINIPORT_* if requested by user
1684 valid_options
= (m_usr_options
? "saimf" : "saif");
1687 case ATA_CHECK_POWER_MODE
:
1688 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
1689 valid_options
= "pai3";
1693 switch (in
.in_regs
.features
) {
1694 case ATA_SMART_READ_VALUES
:
1695 case ATA_SMART_READ_THRESHOLDS
:
1696 case ATA_SMART_AUTOSAVE
:
1697 case ATA_SMART_ENABLE
:
1698 case ATA_SMART_DISABLE
:
1699 case ATA_SMART_AUTO_OFFLINE
:
1700 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1701 // and SCSI_MINIPORT_* if requested by user
1702 valid_options
= (m_usr_options
? "saimf" : "saif");
1705 case ATA_SMART_IMMEDIATE_OFFLINE
:
1706 // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
1707 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ ?
1711 case ATA_SMART_READ_LOG_SECTOR
:
1712 // SMART_RCV_DRIVE_DATA does not support READ_LOG
1713 // Try SCSI_MINIPORT also to skip buggy class driver
1714 // SMART functions do not support multi sector I/O.
1716 valid_options
= (m_usr_options
? "saim3" : "aim3");
1718 valid_options
= "a";
1721 case ATA_SMART_WRITE_LOG_SECTOR
:
1722 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
1723 // but SCSI_MINIPORT_* only if requested by user and single sector.
1724 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
1727 case ATA_SMART_STATUS
:
1728 valid_options
= (m_usr_options
? "saimf" : "saif");
1732 // Unknown SMART command, handle below
1738 // Other ATA command, handle below
1742 if (!valid_options
) {
1743 // No special ATA command found above, select a generic pass through ioctl.
1744 if (!( in
.direction
== ata_cmd_in::no_data
1745 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
1746 || in
.in_regs
.is_48bit_cmd() )
1747 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
1748 valid_options
= "a";
1750 // ATA/IDE_PASS_THROUGH
1751 valid_options
= "ai";
1755 // Restrict to IOCTL_STORAGE_*
1756 if (strchr(valid_options
, 'f'))
1757 valid_options
= "f";
1758 else if (strchr(valid_options
, 'p'))
1759 valid_options
= "p";
1761 return set_err(ENOSYS
, "Function requires admin rights");
1765 IDEREGS regs
, prev_regs
;
1767 const ata_in_regs
& lo
= in
.in_regs
;
1768 regs
.bFeaturesReg
= lo
.features
;
1769 regs
.bSectorCountReg
= lo
.sector_count
;
1770 regs
.bSectorNumberReg
= lo
.lba_low
;
1771 regs
.bCylLowReg
= lo
.lba_mid
;
1772 regs
.bCylHighReg
= lo
.lba_high
;
1773 regs
.bDriveHeadReg
= lo
.device
;
1774 regs
.bCommandReg
= lo
.command
;
1777 if (in
.in_regs
.is_48bit_cmd()) {
1778 const ata_in_regs
& hi
= in
.in_regs
.prev
;
1779 prev_regs
.bFeaturesReg
= hi
.features
;
1780 prev_regs
.bSectorCountReg
= hi
.sector_count
;
1781 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
1782 prev_regs
.bCylLowReg
= hi
.lba_mid
;
1783 prev_regs
.bCylHighReg
= hi
.lba_high
;
1784 prev_regs
.bDriveHeadReg
= hi
.device
;
1785 prev_regs
.bCommandReg
= hi
.command
;
1786 prev_regs
.bReserved
= 0;
1789 // Set data direction
1792 switch (in
.direction
) {
1793 case ata_cmd_in::no_data
:
1795 case ata_cmd_in::data_in
:
1796 datasize
= (int)in
.size
;
1797 data
= (char *)in
.buffer
;
1799 case ata_cmd_in::data_out
:
1800 datasize
= -(int)in
.size
;
1801 data
= (char *)in
.buffer
;
1804 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
1809 // Try all valid ioctls in the order specified in m_options
1810 bool powered_up
= false;
1811 bool out_regs_set
= false;
1812 bool id_is_cached
= false;
1813 const char * options
= m_options
.c_str();
1815 for (int i
= 0; ; i
++) {
1816 char opt
= options
[i
];
1819 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
1820 // Power up reported by GetDevicePowerState() and no ioctl available
1821 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
1822 regs
.bSectorCountReg
= 0xff;
1823 out_regs_set
= true;
1827 return set_err(ENOSYS
);
1829 if (!strchr(valid_options
, opt
))
1830 // Invalid for this command
1834 assert( datasize
== 0 || datasize
== 512
1835 || (datasize
== -512 && strchr("am", opt
))
1836 || (datasize
> 512 && opt
== 'a'));
1841 // call SMART_GET_VERSION once for each drive
1842 if (m_smartver_state
> 1) {
1843 rc
= -1; errno
= ENOSYS
;
1846 if (!m_smartver_state
) {
1847 assert(m_port
== -1);
1848 GETVERSIONINPARAMS_EX vers_ex
;
1849 if (smart_get_version(get_fh(), &vers_ex
) < 0) {
1850 if (!failuretest_permissive
) {
1851 m_smartver_state
= 2;
1852 rc
= -1; errno
= ENOSYS
;
1855 failuretest_permissive
--;
1858 // 3ware RAID if vendor id present
1859 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1862 m_smartver_state
= 1;
1864 rc
= smart_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1865 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
1866 id_is_cached
= (m_port
< 0); // Not cached by 3ware driver
1869 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
1870 id_is_cached
= (m_port
< 0);
1873 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
1874 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
1876 out_regs_set
= true;
1879 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
1880 out_regs_set
= true;
1883 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
1884 ata_identify_device
* id
= reinterpret_cast<ata_identify_device
*>(data
);
1885 rc
= get_identify_from_device_property(get_fh(), id
);
1886 if (rc
== 0 && m_phydrive
>= 0)
1887 get_serial_from_wmi(m_phydrive
, id
);
1888 id_is_cached
= true;
1890 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
1891 case ATA_SMART_READ_VALUES
:
1892 rc
= storage_predict_failure_ioctl(get_fh(), data
);
1896 case ATA_SMART_ENABLE
:
1899 case ATA_SMART_STATUS
:
1900 rc
= storage_predict_failure_ioctl(get_fh());
1902 // Good SMART status
1903 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
1907 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
1912 errno
= ENOSYS
; rc
= -1;
1915 errno
= ENOSYS
; rc
= -1;
1919 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1920 out_regs_set
= true;
1923 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
1924 rc
= get_device_power_state(get_fh());
1926 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
1927 // spin up the drive => simulate ATA result STANDBY.
1928 regs
.bSectorCountReg
= 0x00;
1929 out_regs_set
= true;
1932 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
1933 // only if it is selected by the device driver => try a passthrough ioctl to get the
1934 // actual mode, if none available simulate ACTIVE/IDLE.
1936 rc
= -1; errno
= ENOSYS
;
1942 // Working ioctl found
1945 if (errno
!= ENOSYS
)
1946 // Abort on I/O error
1947 return set_err(errno
);
1949 out_regs_set
= false;
1950 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
1953 // Return IDEREGS if set
1955 ata_out_regs
& lo
= out
.out_regs
;
1956 lo
.error
= regs
.bFeaturesReg
;
1957 lo
.sector_count
= regs
.bSectorCountReg
;
1958 lo
.lba_low
= regs
.bSectorNumberReg
;
1959 lo
.lba_mid
= regs
.bCylLowReg
;
1960 lo
.lba_high
= regs
.bCylHighReg
;
1961 lo
.device
= regs
.bDriveHeadReg
;
1962 lo
.status
= regs
.bCommandReg
;
1963 if (in
.in_regs
.is_48bit_cmd()) {
1964 ata_out_regs
& hi
= out
.out_regs
.prev
;
1965 hi
.sector_count
= prev_regs
.bSectorCountReg
;
1966 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
1967 hi
.lba_mid
= prev_regs
.bCylLowReg
;
1968 hi
.lba_high
= prev_regs
.bCylHighReg
;
1972 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
1973 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
1974 // Update ata_identify_is_cached() result according to ioctl used.
1975 m_id_is_cached
= id_is_cached
;
1980 // Return true if OS caches the ATA identify sector
1981 bool win_ata_device::ata_identify_is_cached() const
1983 return m_id_is_cached
;
1987 //////////////////////////////////////////////////////////////////////
1991 : virtual public /*extends*/ smart_device
1994 enum { max_number_of_ports
= 32 };
1996 /// Get bitmask of used ports
1997 unsigned get_ports_used();
2001 : smart_device(never_called
)
2002 { memset(&m_phy_ent
, 0, sizeof(m_phy_ent
)); }
2004 typedef signed char port_2_index_map
[max_number_of_ports
];
2006 /// Get phy info and port mapping, return #ports or -1 on error
2007 int get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
, port_2_index_map
& p2i
);
2009 /// Select physical drive
2010 bool select_port(int port
);
2012 /// Get info for selected physical drive
2013 const CSMI_SAS_PHY_ENTITY
& get_phy_ent() const
2014 { return m_phy_ent
; }
2016 /// Call platform-specific CSMI ioctl
2017 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2018 unsigned csmi_bufsiz
) = 0;
2021 CSMI_SAS_PHY_ENTITY m_phy_ent
; ///< CSMI info for this phy
2025 /////////////////////////////////////////////////////////////////////////////
2027 int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
, port_2_index_map
& p2i
)
2029 // max_number_of_ports must match CSMI_SAS_PHY_INFO.Phy[] array size
2030 typedef char ASSERT_phy_info_size
[
2031 (int)(sizeof(phy_info
.Phy
) / sizeof(phy_info
.Phy
[0])) == max_number_of_ports
? 1 : -1]
2034 // Get driver info to check CSMI support
2035 CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf
;
2036 memset(&driver_info_buf
, 0, sizeof(driver_info_buf
));
2037 if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO
, &driver_info_buf
.IoctlHeader
, sizeof(driver_info_buf
)))
2040 if (scsi_debugmode
> 1) {
2041 const CSMI_SAS_DRIVER_INFO
& driver_info
= driver_info_buf
.Information
;
2042 pout("CSMI_SAS_DRIVER_INFO:\n");
2043 pout(" Name: \"%.81s\"\n", driver_info
.szName
);
2044 pout(" Description: \"%.81s\"\n", driver_info
.szDescription
);
2045 pout(" Revision: %d.%d\n", driver_info
.usMajorRevision
, driver_info
.usMinorRevision
);
2049 CSMI_SAS_PHY_INFO_BUFFER phy_info_buf
;
2050 memset(&phy_info_buf
, 0, sizeof(phy_info_buf
));
2051 if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO
, &phy_info_buf
.IoctlHeader
, sizeof(phy_info_buf
)))
2054 phy_info
= phy_info_buf
.Information
;
2056 if (phy_info
.bNumberOfPhys
> max_number_of_ports
) {
2057 set_err(EIO
, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info
.bNumberOfPhys
);
2061 // Create port -> index map
2063 // Phy[i].Value 9.x 10.x 14.8 15.2 16.0
2064 // ----------------------------------------------------------
2065 // bPortIdentifier 0xff 0xff port 0x00 port
2066 // Identify.bPhyIdentifier index? index? index index port
2067 // Attached.bPhyIdentifier 0x00 0x00 0x00 index 0x00
2069 // Empty ports with hotplug support may appear in Phy[].
2071 int number_of_ports
;
2072 for (int mode
= 0; ; mode
++) {
2073 for (int i
= 0; i
< max_number_of_ports
; i
++)
2076 number_of_ports
= 0;
2078 for (int i
= 0; i
< max_number_of_ports
; i
++) {
2079 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2080 if (pe
.Identify
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2083 // Try to detect which field contains the actual port number.
2084 // Use a bPhyIdentifier or the bPortIdentifier if unique
2085 // and not always identical to table index, otherwise use index.
2088 case 0: port
= pe
.Attached
.bPhyIdentifier
; break;
2089 case 1: port
= pe
.Identify
.bPhyIdentifier
; break;
2090 case 2: port
= pe
.bPortIdentifier
; break;
2091 default: port
= i
; break;
2093 if (!(port
< max_number_of_ports
&& p2i
[port
] == -1)) {
2099 if (number_of_ports
<= port
)
2100 number_of_ports
= port
+ 1;
2105 if (found
|| mode
> 2)
2109 if (scsi_debugmode
> 1) {
2110 pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info
.bNumberOfPhys
);
2111 for (int i
= 0; i
< max_number_of_ports
; i
++) {
2112 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2113 const CSMI_SAS_IDENTIFY
& id
= pe
.Identify
, & at
= pe
.Attached
;
2114 if (id
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2118 for (int p
= 0; p
< max_number_of_ports
&& port
< 0; p
++) {
2123 pout("Phy[%d] Port: %d\n", i
, port
);
2124 pout(" Type: 0x%02x, 0x%02x\n", id
.bDeviceType
, at
.bDeviceType
);
2125 pout(" InitProto: 0x%02x, 0x%02x\n", id
.bInitiatorPortProtocol
, at
.bInitiatorPortProtocol
);
2126 pout(" TargetProto: 0x%02x, 0x%02x\n", id
.bTargetPortProtocol
, at
.bTargetPortProtocol
);
2127 pout(" PortIdent: 0x%02x\n", pe
.bPortIdentifier
);
2128 pout(" PhyIdent: 0x%02x, 0x%02x\n", id
.bPhyIdentifier
, at
.bPhyIdentifier
);
2129 const unsigned char * b
= id
.bSASAddress
;
2130 pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ",
2131 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2133 pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
2134 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2138 return number_of_ports
;
2141 unsigned csmi_device::get_ports_used()
2143 CSMI_SAS_PHY_INFO phy_info
;
2144 port_2_index_map p2i
;
2145 int number_of_ports
= get_phy_info(phy_info
, p2i
);
2146 if (number_of_ports
< 0)
2149 unsigned ports_used
= 0;
2150 for (int p
= 0; p
< max_number_of_ports
; p
++) {
2154 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2155 if (pe
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2157 switch (pe
.Attached
.bTargetPortProtocol
) {
2158 case CSMI_SAS_PROTOCOL_SATA
:
2159 case CSMI_SAS_PROTOCOL_STP
:
2165 ports_used
|= (1U << p
);
2171 bool csmi_device::select_port(int port
)
2173 if (!(0 <= port
&& port
< max_number_of_ports
))
2174 return set_err(EINVAL
, "Invalid port number %d", port
);
2176 CSMI_SAS_PHY_INFO phy_info
;
2177 port_2_index_map p2i
;
2178 int number_of_ports
= get_phy_info(phy_info
, p2i
);
2179 if (number_of_ports
< 0)
2182 int port_index
= p2i
[port
];
2183 if (port_index
< 0) {
2184 if (port
< number_of_ports
)
2185 return set_err(ENOENT
, "Port %d is disabled", port
);
2187 return set_err(ENOENT
, "Port %d does not exist (#ports: %d)", port
,
2191 const CSMI_SAS_PHY_ENTITY
& phy_ent
= phy_info
.Phy
[port_index
];
2192 if (phy_ent
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2193 return set_err(ENOENT
, "No device on port %d", port
);
2195 switch (phy_ent
.Attached
.bTargetPortProtocol
) {
2196 case CSMI_SAS_PROTOCOL_SATA
:
2197 case CSMI_SAS_PROTOCOL_STP
:
2200 return set_err(ENOENT
, "No SATA device on port %d (protocol: %d)",
2201 port
, phy_ent
.Attached
.bTargetPortProtocol
);
2204 m_phy_ent
= phy_ent
;
2209 //////////////////////////////////////////////////////////////////////
2212 class csmi_ata_device
2213 : virtual public /*extends*/ csmi_device
,
2214 virtual public /*implements*/ ata_device
2217 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
2221 : smart_device(never_called
) { }
2225 //////////////////////////////////////////////////////////////////////
2227 bool csmi_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2229 if (!ata_cmd_is_supported(in
,
2230 ata_device::supports_data_out
|
2231 ata_device::supports_output_regs
|
2232 ata_device::supports_multi_sector
|
2233 ata_device::supports_48bit
,
2238 // Create buffer with appropriate size
2239 raw_buffer
pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER
) + in
.size
);
2240 CSMI_SAS_STP_PASSTHRU_BUFFER
* pthru_buf
= (CSMI_SAS_STP_PASSTHRU_BUFFER
*)pthru_raw_buf
.data();
2242 // Set addresses from Phy info
2243 CSMI_SAS_STP_PASSTHRU
& pthru
= pthru_buf
->Parameters
;
2244 const CSMI_SAS_PHY_ENTITY
& phy_ent
= get_phy_ent();
2245 pthru
.bPhyIdentifier
= phy_ent
.Identify
.bPhyIdentifier
;
2246 pthru
.bPortIdentifier
= phy_ent
.bPortIdentifier
;
2247 memcpy(pthru
.bDestinationSASAddress
, phy_ent
.Attached
.bSASAddress
,
2248 sizeof(pthru
.bDestinationSASAddress
));
2249 pthru
.bConnectionRate
= CSMI_SAS_LINK_RATE_NEGOTIATED
;
2251 // Set transfer mode
2252 switch (in
.direction
) {
2253 case ata_cmd_in::no_data
:
2254 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_UNSPECIFIED
;
2256 case ata_cmd_in::data_in
:
2257 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_READ
;
2258 pthru
.uDataLength
= in
.size
;
2260 case ata_cmd_in::data_out
:
2261 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_WRITE
;
2262 pthru
.uDataLength
= in
.size
;
2263 memcpy(pthru_buf
->bDataBuffer
, in
.buffer
, in
.size
);
2266 return set_err(EINVAL
, "csmi_ata_device::ata_pass_through: invalid direction=%d",
2270 // Set host-to-device FIS
2272 unsigned char * fis
= pthru
.bCommandFIS
;
2273 const ata_in_regs
& lo
= in
.in_regs
;
2274 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2275 fis
[ 0] = 0x27; // Type: host-to-device FIS
2276 fis
[ 1] = 0x80; // Bit7: Update command register
2277 fis
[ 2] = lo
.command
;
2278 fis
[ 3] = lo
.features
;
2279 fis
[ 4] = lo
.lba_low
;
2280 fis
[ 5] = lo
.lba_mid
;
2281 fis
[ 6] = lo
.lba_high
;
2282 fis
[ 7] = lo
.device
;
2283 fis
[ 8] = hi
.lba_low
;
2284 fis
[ 9] = hi
.lba_mid
;
2285 fis
[10] = hi
.lba_high
;
2286 fis
[11] = hi
.features
;
2287 fis
[12] = lo
.sector_count
;
2288 fis
[13] = hi
.sector_count
;
2292 if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU
, &pthru_buf
->IoctlHeader
, pthru_raw_buf
.size())) {
2296 // Get device-to-host FIS
2298 const unsigned char * fis
= pthru_buf
->Status
.bStatusFIS
;
2299 ata_out_regs
& lo
= out
.out_regs
;
2300 lo
.status
= fis
[ 2];
2302 lo
.lba_low
= fis
[ 4];
2303 lo
.lba_mid
= fis
[ 5];
2304 lo
.lba_high
= fis
[ 6];
2305 lo
.device
= fis
[ 7];
2306 lo
.sector_count
= fis
[12];
2307 if (in
.in_regs
.is_48bit_cmd()) {
2308 ata_out_regs
& hi
= out
.out_regs
.prev
;
2309 hi
.lba_low
= fis
[ 8];
2310 hi
.lba_mid
= fis
[ 9];
2311 hi
.lba_high
= fis
[10];
2312 hi
.sector_count
= fis
[13];
2317 if (in
.direction
== ata_cmd_in::data_in
)
2318 // TODO: Check ptru_buf->Status.uDataBytes
2319 memcpy(in
.buffer
, pthru_buf
->bDataBuffer
, in
.size
);
2325 //////////////////////////////////////////////////////////////////////
2328 class win_csmi_device
2329 : public /*implements*/ csmi_ata_device
2332 win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2333 const char * req_type
);
2335 virtual ~win_csmi_device() throw();
2337 virtual bool open();
2339 virtual bool close();
2341 virtual bool is_open() const;
2346 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2347 unsigned csmi_bufsiz
);
2350 HANDLE m_fh
; ///< Controller device handle
2351 int m_port
; ///< Port number
2355 //////////////////////////////////////////////////////////////////////
2357 win_csmi_device::win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2358 const char * req_type
)
2359 : smart_device(intf
, dev_name
, "ata", req_type
),
2360 m_fh(INVALID_HANDLE_VALUE
), m_port(-1)
2364 win_csmi_device::~win_csmi_device() throw()
2366 if (m_fh
!= INVALID_HANDLE_VALUE
)
2370 bool win_csmi_device::is_open() const
2372 return (m_fh
!= INVALID_HANDLE_VALUE
);
2375 bool win_csmi_device::close()
2377 if (m_fh
== INVALID_HANDLE_VALUE
)
2379 BOOL rc
= CloseHandle(m_fh
);
2380 m_fh
= INVALID_HANDLE_VALUE
;
2385 bool win_csmi_device::open_scsi()
2388 unsigned contr_no
= ~0, port
= ~0; int nc
= -1;
2389 const char * name
= skipdev(get_dev_name());
2390 if (!( sscanf(name
, "csmi%u,%u%n", &contr_no
, &port
, &nc
) >= 0
2391 && nc
== (int)strlen(name
) && contr_no
<= 9 && port
< 32) )
2392 return set_err(EINVAL
);
2394 // Open controller handle
2396 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%u:", contr_no
);
2398 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2399 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2400 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
2402 if (h
== INVALID_HANDLE_VALUE
) {
2403 long err
= GetLastError();
2404 if (err
== ERROR_FILE_NOT_FOUND
)
2405 set_err(ENOENT
, "%s: not found", devpath
);
2406 else if (err
== ERROR_ACCESS_DENIED
)
2407 set_err(EACCES
, "%s: access denied", devpath
);
2409 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2413 if (scsi_debugmode
> 1)
2414 pout(" %s: successfully opened\n", devpath
);
2422 bool win_csmi_device::open()
2427 // Get Phy info for this drive
2428 if (!select_port(m_port
)) {
2437 bool win_csmi_device::csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2438 unsigned csmi_bufsiz
)
2440 // Determine signature
2443 case CC_CSMI_SAS_GET_DRIVER_INFO
:
2444 sig
= CSMI_ALL_SIGNATURE
; break;
2445 case CC_CSMI_SAS_GET_PHY_INFO
:
2446 case CC_CSMI_SAS_STP_PASSTHRU
:
2447 sig
= CSMI_SAS_SIGNATURE
; break;
2449 return set_err(ENOSYS
, "Unknown CSMI code=%u", code
);
2453 csmi_buffer
->HeaderLength
= sizeof(IOCTL_HEADER
);
2454 strncpy((char *)csmi_buffer
->Signature
, sig
, sizeof(csmi_buffer
->Signature
));
2455 csmi_buffer
->Timeout
= CSMI_SAS_TIMEOUT
;
2456 csmi_buffer
->ControlCode
= code
;
2457 csmi_buffer
->ReturnCode
= 0;
2458 csmi_buffer
->Length
= csmi_bufsiz
- sizeof(IOCTL_HEADER
);
2462 if (!DeviceIoControl(m_fh
, IOCTL_SCSI_MINIPORT
,
2463 csmi_buffer
, csmi_bufsiz
, csmi_buffer
, csmi_bufsiz
, &num_out
, (OVERLAPPED
*)0)) {
2464 long err
= GetLastError();
2466 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code
, err
);
2467 if ( err
== ERROR_INVALID_FUNCTION
2468 || err
== ERROR_NOT_SUPPORTED
2469 || err
== ERROR_DEV_NOT_EXIST
)
2470 return set_err(ENOSYS
, "CSMI is not supported (Error=%ld)", err
);
2472 return set_err(EIO
, "CSMI(%u) failed with Error=%ld", code
, err
);
2476 if (csmi_buffer
->ReturnCode
) {
2477 if (scsi_debugmode
) {
2478 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
2479 code
, (unsigned)csmi_buffer
->ReturnCode
);
2481 return set_err(EIO
, "CSMI(%u) failed with ReturnCode=%u", code
, (unsigned)csmi_buffer
->ReturnCode
);
2484 if (scsi_debugmode
> 1)
2485 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code
, (unsigned)num_out
);
2491 //////////////////////////////////////////////////////////////////////
2492 // win_tw_cli_device
2494 // Routines for pseudo device /dev/tw_cli/*
2495 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
2496 // TODO: This is OS independent
2498 class win_tw_cli_device
2499 : public /*implements*/ ata_device_with_command_set
2502 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2504 virtual bool is_open() const;
2506 virtual bool open();
2508 virtual bool close();
2511 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
2514 bool m_ident_valid
, m_smart_valid
;
2515 ata_identify_device m_ident_buf
;
2516 ata_smart_values m_smart_buf
;
2520 /////////////////////////////////////////////////////////////////////////////
2522 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2523 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
2524 m_ident_valid(false), m_smart_valid(false)
2526 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
2527 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
2531 bool win_tw_cli_device::is_open() const
2533 return (m_ident_valid
|| m_smart_valid
);
2537 // Get clipboard data
2539 static int get_clipboard(char * data
, int datasize
)
2541 if (!OpenClipboard(NULL
))
2543 HANDLE h
= GetClipboardData(CF_TEXT
);
2548 const void * p
= GlobalLock(h
);
2549 int n
= GlobalSize(h
);
2559 static const char * findstr(const char * str
, const char * sub
)
2561 const char * s
= strstr(str
, sub
);
2562 return (s
? s
+strlen(sub
) : "");
2566 bool win_tw_cli_device::open()
2568 m_ident_valid
= m_smart_valid
= false;
2569 const char * name
= skipdev(get_dev_name());
2570 // Read tw_cli or 3DM browser output into buffer
2572 int size
= -1, n1
= -1, n2
= -1;
2573 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
2574 size
= get_clipboard(buffer
, sizeof(buffer
));
2576 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
2577 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
2579 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
2580 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
2582 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
2583 if (ata_debugmode
> 1)
2584 pout("%s: Run: \"%s\"\n", name
, cmd
);
2585 FILE * f
= popen(cmd
, "rb");
2587 size
= fread(buffer
, 1, sizeof(buffer
), f
);
2592 return set_err(EINVAL
);
2595 if (ata_debugmode
> 1)
2596 pout("%s: Read %d bytes\n", name
, size
);
2598 return set_err(ENOENT
);
2599 if (size
>= (int)sizeof(buffer
))
2600 return set_err(EIO
);
2603 if (ata_debugmode
> 1)
2604 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
2606 // Fake identify sector
2607 ASSERT_SIZEOF(ata_identify_device
, 512);
2608 ata_identify_device
* id
= &m_ident_buf
;
2609 memset(id
, 0, sizeof(*id
));
2610 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
2611 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
2612 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
2613 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
2614 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
2616 id
->words047_079
[49-47] = 0x0200; // size valid
2617 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
2618 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
2620 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2621 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2623 // Parse smart data hex dump
2624 const char * s
= findstr(buffer
, "Drive Smart Data:");
2626 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
2628 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
2630 const char * s1
= findstr(s
, "<td class"); // html version
2633 s
+= strcspn(s
, "\r\n");
2636 s
= buffer
; // try raw hex dump without header
2638 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
2641 unsigned x
= ~0; int n
= -1;
2642 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
2644 sd
[i
] = (unsigned char)x
;
2645 if (!(++i
< 512 && n
> 0))
2648 if (*s
== '<') // "<br>"
2649 s
+= strcspn(s
, "\r\n");
2652 if (!id
->model
[1]) {
2653 // No useful data found
2654 char * err
= strstr(buffer
, "Error:");
2656 err
= strstr(buffer
, "error :");
2657 if (err
&& (err
= strchr(err
, ':'))) {
2658 // Show tw_cli error message
2660 err
[strcspn(err
, "\r\n")] = 0;
2661 return set_err(EIO
, "%s", err
);
2663 return set_err(EIO
);
2668 m_ident_valid
= true;
2669 m_smart_valid
= !!sd
;
2674 bool win_tw_cli_device::close()
2676 m_ident_valid
= m_smart_valid
= false;
2681 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
2687 memcpy(data
, &m_ident_buf
, 512);
2692 memcpy(data
, &m_smart_buf
, 512);
2696 case STATUS_CHECK
: // Fake "good" SMART status
2701 // Arrive here for all unsupported commands
2707 /////////////////////////////////////////////////////////////////////////////
2709 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
2711 class win_scsi_device
2712 : public /*implements*/ scsi_device
,
2713 virtual public /*extends*/ win_smart_device
2716 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2718 virtual bool open();
2720 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
2723 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
2727 /////////////////////////////////////////////////////////////////////////////
2729 win_scsi_device::win_scsi_device(smart_interface
* intf
,
2730 const char * dev_name
, const char * req_type
)
2731 : smart_device(intf
, dev_name
, "scsi", req_type
)
2735 bool win_scsi_device::open()
2737 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2738 // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
2739 char drive
[2+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
2740 if ( sscanf(name
, "sd%2[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
2741 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
2742 return open(sdxy_to_phydrive(drive
), -1, -1, sub_addr
);
2744 // pd<m>,N => Physical drive <m>, RAID port N
2745 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
2746 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
2747 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
2748 return open(pd_num
, -1, -1, sub_addr
);
2750 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2751 int logdrive
= drive_letter(name
);
2752 if (logdrive
>= 0) {
2753 return open(-1, logdrive
, -1, -1);
2755 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
2756 int tape_num
= -1; n1
= -1;
2757 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2758 return open(-1, -1, tape_num
, -1);
2760 tape_num
= -1; n1
= -1;
2761 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2762 return open(-1, -1, tape_num
, -1);
2764 // tape<m> => tape drive <m>
2765 tape_num
= -1; n1
= -1;
2766 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2767 return open(-1, -1, tape_num
, -1);
2770 return set_err(EINVAL
);
2773 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
2776 b
[sizeof(b
) - 1] = '\0';
2778 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
2779 else if (ld_num
>= 0)
2780 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
2781 else if (tape_num
>= 0)
2782 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
2789 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
2790 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
2791 OPEN_EXISTING
, 0, 0);
2792 if (h
== INVALID_HANDLE_VALUE
) {
2793 set_err(ENODEV
, "%s: Open failed, Error=%u", b
, (unsigned)GetLastError());
2802 SCSI_PASS_THROUGH_DIRECT spt
;
2804 UCHAR ucSenseBuf
[64];
2805 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
2808 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
2809 // Used if DataTransferLength not supported by *_DIRECT.
2810 static long scsi_pass_through_indirect(HANDLE h
,
2811 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
2813 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
2814 SCSI_PASS_THROUGH spt
;
2816 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
2817 UCHAR ucDataBuf
[512];
2820 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
2821 memset(&sb
, 0, sizeof(sb
));
2823 // DATA_OUT not implemented yet
2824 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
2825 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
2826 return ERROR_INVALID_PARAMETER
;
2828 sb
.spt
.Length
= sizeof(sb
.spt
);
2829 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
2830 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
2831 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2832 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
2833 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
2834 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
2835 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
2836 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
2839 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
2840 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2841 return GetLastError();
2843 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
2844 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
2845 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
2847 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
2848 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
2849 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
2854 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
2855 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
2857 int report
= scsi_debugmode
; // TODO
2861 const unsigned char * ucp
= iop
->cmnd
;
2864 const int sz
= (int)sizeof(buff
);
2866 np
= scsi_get_opcode_name(ucp
[0]);
2867 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
2868 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
2869 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
2871 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
2872 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2874 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
2875 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
2876 (trunc
? " [only first 256 bytes shown]" : ""));
2877 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2880 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
2884 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
2885 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
2886 set_err(EINVAL
, "cmnd_len too large");
2890 memset(&sb
, 0, sizeof(sb
));
2891 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
2892 sb
.spt
.CdbLength
= iop
->cmnd_len
;
2893 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
2894 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2895 sb
.spt
.SenseInfoOffset
=
2896 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
2897 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
2900 switch (iop
->dxfer_dir
) {
2902 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
2904 case DXFER_FROM_DEVICE
:
2905 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
2906 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2907 sb
.spt
.DataBuffer
= iop
->dxferp
;
2908 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
2909 // transfers (needed for SMART STATUS check of JMicron USB bridges)
2910 if (sb
.spt
.DataTransferLength
== 1)
2913 case DXFER_TO_DEVICE
:
2914 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
2915 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2916 sb
.spt
.DataBuffer
= iop
->dxferp
;
2919 set_err(EINVAL
, "bad dxfer_dir");
2926 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
2927 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2928 err
= GetLastError();
2931 err
= scsi_pass_through_indirect(get_fh(), &sb
);
2934 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
2935 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
2936 (direct
? "_DIRECT" : ""), err
);
2938 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
2939 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
2940 int slen
= sb
.ucSenseBuf
[7] + 8;
2942 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
2943 slen
= sizeof(sb
.ucSenseBuf
);
2944 if (slen
> (int)iop
->max_sense_len
)
2945 slen
= iop
->max_sense_len
;
2946 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
2947 iop
->resp_sense_len
= slen
;
2950 pout(" >>> Sense buffer, len=%d:\n", slen
);
2951 dStrHex(iop
->sensep
, slen
, 1);
2953 if ((iop
->sensep
[0] & 0x7f) > 0x71)
2954 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
2955 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
2956 iop
->sensep
[2], iop
->sensep
[3]);
2958 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
2959 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
2960 iop
->sensep
[12], iop
->sensep
[13]);
2963 iop
->resp_sense_len
= 0;
2965 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
2966 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
2970 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
2971 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2972 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
2973 (trunc
? " [only first 256 bytes shown]" : ""));
2974 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2980 /////////////////////////////////////////////////////////////////////////////
2981 /// Areca RAID support
2983 // TODO: combine with above scsi_pass_through_direct()
2984 static long scsi_pass_through_direct(HANDLE fd
, UCHAR targetid
, struct scsi_cmnd_io
* iop
)
2986 int report
= scsi_debugmode
; // TODO
2990 const unsigned char * ucp
= iop
->cmnd
;
2993 const int sz
= (int)sizeof(buff
);
2995 np
= scsi_get_opcode_name(ucp
[0]);
2996 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
2997 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
2998 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3000 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3001 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3003 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3004 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3005 (trunc
? " [only first 256 bytes shown]" : ""));
3006 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3009 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3013 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
3014 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
3018 memset(&sb
, 0, sizeof(sb
));
3019 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
3020 //sb.spt.PathId = 0;
3021 sb
.spt
.TargetId
= targetid
;
3023 sb
.spt
.CdbLength
= iop
->cmnd_len
;
3024 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
3025 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3026 sb
.spt
.SenseInfoOffset
=
3027 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
3028 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
3031 switch (iop
->dxfer_dir
) {
3033 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
3035 case DXFER_FROM_DEVICE
:
3036 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
3037 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3038 sb
.spt
.DataBuffer
= iop
->dxferp
;
3039 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
3040 // transfers (needed for SMART STATUS check of JMicron USB bridges)
3041 if (sb
.spt
.DataTransferLength
== 1)
3044 case DXFER_TO_DEVICE
:
3045 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
3046 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3047 sb
.spt
.DataBuffer
= iop
->dxferp
;
3056 if (!DeviceIoControl(fd
, IOCTL_SCSI_PASS_THROUGH_DIRECT
,
3057 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3058 err
= GetLastError();
3061 err
= scsi_pass_through_indirect(fd
, &sb
);
3068 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
3069 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3070 int slen
= sb
.ucSenseBuf
[7] + 8;
3072 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
3073 slen
= sizeof(sb
.ucSenseBuf
);
3074 if (slen
> (int)iop
->max_sense_len
)
3075 slen
= iop
->max_sense_len
;
3076 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3077 iop
->resp_sense_len
= slen
;
3080 pout(" >>> Sense buffer, len=%d:\n", slen
);
3081 dStrHex(iop
->sensep
, slen
, 1);
3083 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3084 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3085 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3086 iop
->sensep
[2], iop
->sensep
[3]);
3088 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3089 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3090 iop
->sensep
[12], iop
->sensep
[13]);
3093 iop
->resp_sense_len
= 0;
3095 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
3096 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3100 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3101 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3102 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3103 (trunc
? " [only first 256 bytes shown]" : ""));
3104 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3111 /////////////////////////////////////////////////////////////////////////////
3112 // win_areca_scsi_device
3113 // SAS(SCSI) device behind Areca RAID Controller
3115 class win_areca_scsi_device
3116 : public /*implements*/ areca_scsi_device
,
3117 public /*extends*/ win_smart_device
3120 win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3121 virtual bool open();
3122 virtual smart_device
* autodetect_open();
3123 virtual bool arcmsr_lock();
3124 virtual bool arcmsr_unlock();
3125 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3132 /////////////////////////////////////////////////////////////////////////////
3134 win_areca_scsi_device::win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3135 : smart_device(intf
, dev_name
, "areca", "areca")
3137 set_fh(INVALID_HANDLE_VALUE
);
3138 set_disknum(disknum
);
3140 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3143 bool win_areca_scsi_device::open()
3151 hFh
= CreateFile( get_dev_name(),
3152 GENERIC_READ
|GENERIC_WRITE
,
3153 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3158 if(hFh
== INVALID_HANDLE_VALUE
)
3167 smart_device
* win_areca_scsi_device::autodetect_open()
3172 int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3174 int ioctlreturn
= 0;
3176 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3177 if ( ioctlreturn
|| iop
->scsi_status
)
3179 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3180 if ( ioctlreturn
|| iop
->scsi_status
)
3190 bool win_areca_scsi_device::arcmsr_lock()
3192 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3196 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3197 return set_err(EINVAL
, "unable to parse device name");
3199 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3200 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3201 if ( m_mutex
== NULL
)
3203 return set_err(EIO
, "CreateMutex failed");
3206 // atomic access to driver
3207 WaitForSingleObject(m_mutex
, INFINITE
);
3213 bool win_areca_scsi_device::arcmsr_unlock()
3215 if( m_mutex
!= NULL
)
3217 ReleaseMutex(m_mutex
);
3218 CloseHandle(m_mutex
);
3225 /////////////////////////////////////////////////////////////////////////////
3226 // win_areca_ata_device
3227 // SATA(ATA) device behind Areca RAID Controller
3229 class win_areca_ata_device
3230 : public /*implements*/ areca_ata_device
,
3231 public /*extends*/ win_smart_device
3234 win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3235 virtual bool open();
3236 virtual smart_device
* autodetect_open();
3237 virtual bool arcmsr_lock();
3238 virtual bool arcmsr_unlock();
3239 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3246 /////////////////////////////////////////////////////////////////////////////
3248 win_areca_ata_device::win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3249 : smart_device(intf
, dev_name
, "areca", "areca")
3251 set_fh(INVALID_HANDLE_VALUE
);
3252 set_disknum(disknum
);
3254 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3257 bool win_areca_ata_device::open()
3265 hFh
= CreateFile( get_dev_name(),
3266 GENERIC_READ
|GENERIC_WRITE
,
3267 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3272 if(hFh
== INVALID_HANDLE_VALUE
)
3281 smart_device
* win_areca_ata_device::autodetect_open()
3283 // autodetect device type
3284 int is_ata
= arcmsr_get_dev_type();
3298 smart_device_auto_ptr
newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
3301 newdev
->open(); // TODO: Can possibly pass open fd
3303 return newdev
.release();
3306 int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3308 int ioctlreturn
= 0;
3310 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3311 if ( ioctlreturn
|| iop
->scsi_status
)
3313 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3314 if ( ioctlreturn
|| iop
->scsi_status
)
3324 bool win_areca_ata_device::arcmsr_lock()
3326 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3330 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3331 return set_err(EINVAL
, "unable to parse device name");
3333 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3334 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3335 if ( m_mutex
== NULL
)
3337 return set_err(EIO
, "CreateMutex failed");
3340 // atomic access to driver
3341 WaitForSingleObject(m_mutex
, INFINITE
);
3347 bool win_areca_ata_device::arcmsr_unlock()
3349 if( m_mutex
!= NULL
)
3351 ReleaseMutex(m_mutex
);
3352 CloseHandle(m_mutex
);
3359 /////////////////////////////////////////////////////////////////////////////
3360 // win_aacraid_device
3361 // PMC aacraid Support
3363 class win_aacraid_device
3364 :public /*implements*/ scsi_device
,
3365 public /*extends*/ win_smart_device
3368 win_aacraid_device(smart_interface
*intf
, const char *dev_name
,unsigned int ctrnum
, unsigned int target
, unsigned int lun
);
3370 virtual ~win_aacraid_device() throw();
3372 virtual bool open();
3374 virtual bool scsi_pass_through(struct scsi_cmnd_io
*iop
);
3377 //Device Host number
3380 //Channel(Lun) of the device
3388 /////////////////////////////////////////////////////////////////////////////
3390 win_aacraid_device::win_aacraid_device(smart_interface
* intf
,
3391 const char *dev_name
, unsigned ctrnum
, unsigned target
, unsigned lun
)
3392 : smart_device(intf
, dev_name
, "aacraid", "aacraid"),
3393 m_ctrnum(ctrnum
), m_lun(lun
), m_target(target
)
3395 set_info().info_name
= strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name
, m_ctrnum
, m_lun
, m_target
);
3396 set_info().dev_type
= strprintf("aacraid,%d,%d,%d", m_ctrnum
, m_lun
, m_target
);
3399 win_aacraid_device::~win_aacraid_device() throw()
3403 bool win_aacraid_device::open()
3408 HANDLE hFh
= CreateFile( get_dev_name(),
3409 GENERIC_READ
|GENERIC_WRITE
,
3410 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3415 if (hFh
== INVALID_HANDLE_VALUE
)
3416 return set_err(ENODEV
, "Open failed, Error=%u", (unsigned)GetLastError());
3422 bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io
*iop
)
3424 int report
= scsi_debugmode
;
3428 const unsigned char * ucp
= iop
->cmnd
;
3431 const int sz
= (int)sizeof(buff
);
3432 np
= scsi_get_opcode_name(ucp
[0]);
3433 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3434 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3435 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3437 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3438 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3440 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3441 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3442 (trunc
? " [only first 256 bytes shown]" : ""));
3443 dStrHex(iop
->dxferp
, (trunc
? 256 : (int)iop
->dxfer_len
) , 1);
3446 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3447 pout("buff %s\n",buff
);
3450 char ioBuffer
[1000];
3451 SRB_IO_CONTROL
* pSrbIO
= (SRB_IO_CONTROL
*) ioBuffer
;
3452 SCSI_REQUEST_BLOCK
* pScsiIO
= (SCSI_REQUEST_BLOCK
*) (ioBuffer
+ sizeof(SRB_IO_CONTROL
));
3453 DWORD scsiRequestBlockSize
= sizeof(SCSI_REQUEST_BLOCK
);
3454 char *pRequestSenseIO
= (char *) (ioBuffer
+ sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
);
3455 DWORD dataOffset
= (sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
+ 7) & 0xfffffff8;
3456 char *pDataIO
= (char *) (ioBuffer
+ dataOffset
);
3457 memset(pScsiIO
, 0, scsiRequestBlockSize
);
3458 pScsiIO
->Length
= (USHORT
) scsiRequestBlockSize
;
3459 pScsiIO
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
3460 pScsiIO
->PathId
= 0;
3461 pScsiIO
->TargetId
= m_target
;
3462 pScsiIO
->Lun
= m_lun
;
3463 pScsiIO
->CdbLength
= (int)iop
->cmnd_len
;
3464 switch(iop
->dxfer_dir
){
3466 pScsiIO
->SrbFlags
= SRB_NoDataXfer
;
3468 case DXFER_FROM_DEVICE
:
3469 pScsiIO
->SrbFlags
|= SRB_DataIn
;
3471 case DXFER_TO_DEVICE
:
3472 pScsiIO
->SrbFlags
|= SRB_DataOut
;
3475 pout("aacraid: bad dxfer_dir\n");
3476 return set_err(EINVAL
, "aacraid: bad dxfer_dir\n");
3478 pScsiIO
->DataTransferLength
= (ULONG
)iop
->dxfer_len
;
3479 pScsiIO
->TimeOutValue
= iop
->timeout
;
3480 UCHAR
*pCdb
= (UCHAR
*) pScsiIO
->Cdb
;
3481 memcpy(pCdb
, iop
->cmnd
, 16);
3482 if (iop
->max_sense_len
){
3483 memset(pRequestSenseIO
, 0, iop
->max_sense_len
);
3485 if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_OUT
){
3486 memcpy(pDataIO
, iop
->dxferp
, iop
->dxfer_len
);
3488 else if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_IN
){
3489 memset(pDataIO
, 0, iop
->dxfer_len
);
3492 DWORD bytesReturned
= 0;
3493 memset(pSrbIO
, 0, sizeof(SRB_IO_CONTROL
));
3494 pSrbIO
->HeaderLength
= sizeof(SRB_IO_CONTROL
);
3495 memcpy(pSrbIO
->Signature
, "AACAPI", 7);
3496 pSrbIO
->ControlCode
= ARCIOCTL_SEND_RAW_SRB
;
3497 pSrbIO
->Length
= (dataOffset
+ iop
->dxfer_len
- sizeof(SRB_IO_CONTROL
) + 7) & 0xfffffff8;
3498 pSrbIO
->Timeout
= 3*60;
3500 if (!DeviceIoControl(
3502 IOCTL_SCSI_MINIPORT
,
3504 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3506 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3510 return set_err(EIO
, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
3513 iop
->scsi_status
= pScsiIO
->ScsiStatus
;
3514 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3515 int slen
= sizeof(pRequestSenseIO
) + 8;
3516 if (slen
> (int)sizeof(pRequestSenseIO
))
3517 slen
= sizeof(pRequestSenseIO
);
3518 if (slen
> (int)iop
->max_sense_len
)
3519 slen
= (int)iop
->max_sense_len
;
3520 memcpy(iop
->sensep
, pRequestSenseIO
, slen
);
3521 iop
->resp_sense_len
= slen
;
3524 pout(" >>> Sense buffer, len=%d:\n", slen
);
3525 dStrHex(iop
->sensep
, slen
, 1);
3527 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3528 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3529 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3530 iop
->sensep
[2], iop
->sensep
[3]);
3532 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3533 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3534 iop
->sensep
[12], iop
->sensep
[13]);
3538 iop
->resp_sense_len
= 0;
3541 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
){
3542 memcpy(iop
->dxferp
,pDataIO
, iop
->dxfer_len
);
3544 if((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)){
3545 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3546 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3547 (trunc
? " [only first 256 bytes shown]" : ""));
3548 dStrHex((const uint8_t *)pDataIO
, (trunc
? 256 : (int)(iop
->dxfer_len
)) , 1);
3554 /////////////////////////////////////////////////////////////////////////////
3557 class win_nvme_device
3558 : public /*implements*/ nvme_device
,
3559 public /*extends*/ win_smart_device
3562 win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3563 const char * req_type
, unsigned nsid
);
3565 virtual bool open();
3567 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
3569 bool open_scsi(int n
);
3578 /////////////////////////////////////////////////////////////////////////////
3580 win_nvme_device::win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3581 const char * req_type
, unsigned nsid
)
3582 : smart_device(intf
, dev_name
, "nvme", req_type
),
3588 bool win_nvme_device::open_scsi(int n
)
3590 // TODO: Use common open function for all devices using "\\.\ScsiN:"
3592 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%d:", n
);
3594 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
3595 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3596 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
3598 if (h
== INVALID_HANDLE_VALUE
) {
3599 long err
= GetLastError();
3600 if (nvme_debugmode
> 1)
3601 pout(" %s: Open failed, Error=%ld\n", devpath
, err
);
3602 if (err
== ERROR_FILE_NOT_FOUND
)
3603 set_err(ENOENT
, "%s: not found", devpath
);
3604 else if (err
== ERROR_ACCESS_DENIED
)
3605 set_err(EACCES
, "%s: access denied", devpath
);
3607 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3611 if (nvme_debugmode
> 1)
3612 pout(" %s: successfully opened\n", devpath
);
3618 // Check if NVMe DeviceIoControl(IOCTL_SCSI_MINIPORT) pass-through works.
3619 // On Win10 and later that returns false with an errorNumber of 1
3620 // ("Incorrect function"). Win10 has new pass-through:
3621 // DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND). However for commonly
3622 // requested NVMe commands like Identify and Get Features Microsoft want
3623 // "Protocol specific queries" sent.
3624 bool win_nvme_device::probe()
3626 smartmontools::nvme_id_ctrl id_ctrl
;
3628 in
.set_data_in(smartmontools::nvme_admin_identify
, &id_ctrl
, sizeof(id_ctrl
));
3633 bool ok
= nvme_pass_through(in
, out
);
3634 if (!ok
&& nvme_debugmode
> 1)
3635 pout(" nvme probe failed: %s\n", get_errmsg());
3639 bool win_nvme_device::open()
3641 if (m_scsi_no
< 0) {
3642 // First open -> search of NVMe devices
3643 const char * name
= skipdev(get_dev_name());
3644 char s
[2+1] = ""; int n1
= -1, n2
= -1, len
= strlen(name
);
3645 unsigned no
= ~0, nsid
= 0xffffffff;
3646 sscanf(name
, "nvm%2[es]%u%nn%u%n", s
, &no
, &n1
, &nsid
, &n2
);
3648 if (!( (n1
== len
|| (n2
== len
&& nsid
> 0))
3649 && s
[0] == 'e' && (!s
[1] || s
[1] == 's') ))
3650 return set_err(EINVAL
);
3653 // /dev/nvmeN* -> search for nth NVMe device
3654 unsigned nvme_cnt
= 0;
3655 for (int i
= 0; i
< 32; i
++) {
3656 if (!open_scsi(i
)) {
3657 if (get_errno() == EACCES
)
3661 // Done if pass-through works and correct number
3663 if (nvme_cnt
== no
) {
3673 return set_err(ENOENT
);
3677 // /dev/nvmesN* -> use "\\.\ScsiN:"
3687 // Reopen same "\\.\ScsiN:"
3688 if (!open_scsi(m_scsi_no
))
3695 bool win_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
3697 // Create buffer with appropriate size
3698 raw_buffer
pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
) + in
.size
);
3699 NVME_PASS_THROUGH_IOCTL
* pthru
=
3700 reinterpret_cast<NVME_PASS_THROUGH_IOCTL
*>(pthru_raw_buf
.data());
3703 pthru
->SrbIoCtrl
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
3704 memcpy(pthru
->SrbIoCtrl
.Signature
, NVME_SIG_STR
, sizeof(NVME_SIG_STR
)-1);
3705 pthru
->SrbIoCtrl
.Timeout
= 60;
3706 pthru
->SrbIoCtrl
.ControlCode
= NVME_PASS_THROUGH_SRB_IO_CODE
;
3707 pthru
->SrbIoCtrl
.ReturnCode
= 0;
3708 pthru
->SrbIoCtrl
.Length
= pthru_raw_buf
.size() - sizeof(SRB_IO_CONTROL
);
3710 pthru
->NVMeCmd
[0] = in
.opcode
;
3711 pthru
->NVMeCmd
[1] = in
.nsid
;
3712 pthru
->NVMeCmd
[10] = in
.cdw10
;
3713 pthru
->NVMeCmd
[11] = in
.cdw11
;
3714 pthru
->NVMeCmd
[12] = in
.cdw12
;
3715 pthru
->NVMeCmd
[13] = in
.cdw13
;
3716 pthru
->NVMeCmd
[14] = in
.cdw14
;
3717 pthru
->NVMeCmd
[15] = in
.cdw15
;
3719 pthru
->Direction
= in
.direction();
3720 // pthru->QueueId = 0; // AdminQ
3721 // pthru->DataBufferLen = 0;
3722 if (in
.direction() & nvme_cmd_in::data_out
) {
3723 pthru
->DataBufferLen
= in
.size
;
3724 memcpy(pthru
->DataBuffer
, in
.buffer
, in
.size
);
3726 // pthru->MetaDataLen = 0;
3727 pthru
->ReturnBufferLen
= pthru_raw_buf
.size();
3729 // Call NVME_PASS_THROUGH
3731 BOOL ok
= DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT
,
3732 pthru
, pthru_raw_buf
.size(), pthru
, pthru_raw_buf
.size(),
3733 &num_out
, (OVERLAPPED
*)0);
3736 unsigned status
= pthru
->CplEntry
[3] >> 17;
3738 return set_nvme_err(out
, status
);
3741 return set_err(EIO
, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError());
3743 if (in
.direction() & nvme_cmd_in::data_in
)
3744 memcpy(in
.buffer
, pthru
->DataBuffer
, in
.size
);
3746 out
.result
= pthru
->CplEntry
[0];
3751 /////////////////////////////////////////////////////////////////////////////
3752 // win10_nvme_device
3754 class win10_nvme_device
3755 : public /*implements*/ nvme_device
,
3756 public /*extends*/ win_smart_device
3759 win10_nvme_device(smart_interface
* intf
, const char * dev_name
,
3760 const char * req_type
, unsigned nsid
);
3762 virtual bool open();
3764 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
3767 bool open(int phydrive
);
3771 /////////////////////////////////////////////////////////////////////////////
3773 win10_nvme_device::win10_nvme_device(smart_interface
* intf
, const char * dev_name
,
3774 const char * req_type
, unsigned nsid
)
3775 : smart_device(intf
, dev_name
, "nvme", req_type
),
3780 bool win10_nvme_device::open()
3782 // TODO: Use common /dev/ parsing functions
3783 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
3784 // sd[a-z]([a-z])? => Physical drive 0-701
3785 char drive
[2 + 1] = ""; int n
= -1;
3786 if (sscanf(name
, "sd%2[a-z]%n", drive
, &n
) == 1 && n
== len
)
3787 return open(sdxy_to_phydrive(drive
));
3789 // pdN => Physical drive N
3790 int phydrive
= -1; n
= -1;
3791 if (sscanf(name
, "pd%d%n", &phydrive
, &n
) == 1 && phydrive
>= 0 && n
== len
)
3792 return open(phydrive
);
3794 return set_err(EINVAL
);
3797 bool win10_nvme_device::open(int phydrive
)
3799 // TODO: Use common open function for all devices using "\\.\PhysicalDriveN"
3801 snprintf(devpath
, sizeof(devpath
) - 1, "\\\\.\\PhysicalDrive%d", phydrive
);
3803 // No GENERIC_READ/WRITE access required, this works without admin rights
3804 HANDLE h
= CreateFileA(devpath
, 0, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
3805 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, (HANDLE
)0);
3807 if (h
== INVALID_HANDLE_VALUE
) {
3808 long err
= GetLastError();
3809 if (nvme_debugmode
> 1)
3810 pout(" %s: Open failed, Error=%ld\n", devpath
, err
);
3811 if (err
== ERROR_FILE_NOT_FOUND
)
3812 set_err(ENOENT
, "%s: not found", devpath
);
3813 else if (err
== ERROR_ACCESS_DENIED
)
3814 set_err(EACCES
, "%s: access denied", devpath
);
3816 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3820 if (nvme_debugmode
> 1)
3821 pout(" %s: successfully opened\n", devpath
);
3825 // Use broadcast namespace if no NSID specified
3826 // TODO: Get NSID of current device
3828 set_nsid(0xffffffff);
3832 struct STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
3834 struct { // STORAGE_PROPERTY_QUERY without AdditionalsParameters[1]
3835 STORAGE_PROPERTY_ID PropertyId
;
3836 STORAGE_QUERY_TYPE QueryType
;
3838 win10::STORAGE_PROTOCOL_SPECIFIC_DATA ProtocolSpecific
;
3842 bool win10_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
3844 // Create buffer with appropriate size
3845 raw_buffer
spsq_raw_buf(offsetof(STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
, DataBuffer
) + in
.size
);
3846 STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
* spsq
=
3847 reinterpret_cast<STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER
*>(spsq_raw_buf
.data());
3849 // Set NVMe specific STORAGE_PROPERTY_QUERY
3850 spsq
->PropertyQuery
.QueryType
= PropertyStandardQuery
;
3851 spsq
->ProtocolSpecific
.ProtocolType
= win10::ProtocolTypeNvme
;
3853 switch (in
.opcode
) {
3854 case smartmontools::nvme_admin_identify
:
3855 if (!in
.nsid
) // Identify controller
3856 spsq
->PropertyQuery
.PropertyId
= win10::StorageAdapterProtocolSpecificProperty
;
3858 spsq
->PropertyQuery
.PropertyId
= win10::StorageDeviceProtocolSpecificProperty
;
3859 spsq
->ProtocolSpecific
.DataType
= win10::NVMeDataTypeIdentify
;
3860 spsq
->ProtocolSpecific
.ProtocolDataRequestValue
= in
.cdw10
;
3862 case smartmontools::nvme_admin_get_log_page
:
3863 spsq
->PropertyQuery
.PropertyId
= win10::StorageDeviceProtocolSpecificProperty
;
3864 spsq
->ProtocolSpecific
.DataType
= win10::NVMeDataTypeLogPage
;
3865 spsq
->ProtocolSpecific
.ProtocolDataRequestValue
= in
.cdw10
& 0xff; // LID only ?
3867 // TODO: nvme_admin_get_features
3869 return set_err(ENOSYS
, "NVMe admin command 0x%02x not supported", in
.opcode
);
3872 spsq
->ProtocolSpecific
.ProtocolDataRequestSubValue
= in
.nsid
; // ?
3873 spsq
->ProtocolSpecific
.ProtocolDataOffset
= sizeof(spsq
->ProtocolSpecific
);
3874 spsq
->ProtocolSpecific
.ProtocolDataLength
= in
.size
;
3876 if (in
.direction() & nvme_cmd_in::data_out
)
3877 memcpy(spsq
->DataBuffer
, in
.buffer
, in
.size
);
3879 if (nvme_debugmode
> 1)
3880 pout(" [STORAGE_QUERY_PROPERTY: Id=%u, Type=%u, Value=0x%08x, SubVal=0x%08x]\n",
3881 (unsigned)spsq
->PropertyQuery
.PropertyId
,
3882 (unsigned)spsq
->ProtocolSpecific
.DataType
,
3883 (unsigned)spsq
->ProtocolSpecific
.ProtocolDataRequestValue
,
3884 (unsigned)spsq
->ProtocolSpecific
.ProtocolDataRequestSubValue
);
3886 // Call IOCTL_STORAGE_QUERY_PROPERTY
3889 if (!DeviceIoControl(get_fh(), IOCTL_STORAGE_QUERY_PROPERTY
,
3890 spsq
, spsq_raw_buf
.size(), spsq
, spsq_raw_buf
.size(),
3891 &num_out
, (OVERLAPPED
*)0)) {
3892 err
= GetLastError();
3895 if (nvme_debugmode
> 1)
3896 pout(" [STORAGE_QUERY_PROPERTY: ReturnData=0x%08x, Reserved[3]={0x%x, 0x%x, 0x%x}]\n",
3897 (unsigned)spsq
->ProtocolSpecific
.FixedProtocolReturnData
,
3898 (unsigned)spsq
->ProtocolSpecific
.Reserved
[0],
3899 (unsigned)spsq
->ProtocolSpecific
.Reserved
[1],
3900 (unsigned)spsq
->ProtocolSpecific
.Reserved
[2]);
3902 // NVMe status is checked by IOCTL
3904 return set_err(EIO
, "IOCTL_STORAGE_QUERY_PROPERTY(NVMe) failed, Error=%ld", err
);
3906 if (in
.direction() & nvme_cmd_in::data_in
)
3907 memcpy(in
.buffer
, spsq
->DataBuffer
, in
.size
);
3909 out
.result
= spsq
->ProtocolSpecific
.FixedProtocolReturnData
; // Completion DW0 ?
3914 /////////////////////////////////////////////////////////////////////////////
3915 // win_smart_interface
3916 // Platform specific interface
3918 class win_smart_interface
3919 : public /*implements*/ smart_interface
3922 virtual std::string
get_os_version_str();
3924 virtual std::string
get_app_examples(const char * appname
);
3927 virtual int64_t get_timer_usec();
3930 virtual bool disable_system_auto_standby(bool disable
);
3932 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
3933 const char * pattern
= 0);
3936 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
3938 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
3940 virtual nvme_device
* get_nvme_device(const char * name
, const char * type
, unsigned nsid
);
3942 virtual smart_device
* autodetect_smart_device(const char * name
);
3944 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
3946 virtual std::string
get_valid_custom_dev_types_str();
3949 smart_device
* get_usb_device(const char * name
, int phydrive
, int logdrive
= -1);
3953 /////////////////////////////////////////////////////////////////////////////
3956 // Running on 64-bit Windows as 32-bit app ?
3957 static bool is_wow64()
3959 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
3960 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))
3961 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
3962 if (!IsWow64Process_p
)
3965 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
3971 // Return info string about build host and OS version
3972 std::string
win_smart_interface::get_os_version_str()
3974 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
3975 = SMARTMONTOOLS_BUILD_HOST
;
3978 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
3979 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
3980 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
3982 // Starting with Windows 8.1, GetVersionEx() does no longer report the
3983 // actual OS version. RtlGetVersion() is not affected.
3984 LONG
/*NTSTATUS*/ (WINAPI
/*NTAPI*/ * RtlGetVersion_p
)(LPOSVERSIONINFOEXW
) =
3985 (LONG (WINAPI
*)(LPOSVERSIONINFOEXW
))
3986 GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
3988 OSVERSIONINFOEXW vi
; memset(&vi
, 0, sizeof(vi
));
3989 vi
.dwOSVersionInfoSize
= sizeof(vi
);
3990 if (!RtlGetVersion_p
|| RtlGetVersion_p(&vi
)) {
3991 if (!GetVersionExW((OSVERSIONINFOW
*)&vi
))
3997 if ( vi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
3998 && vi
.dwMajorVersion
<= 0xf && vi
.dwMinorVersion
<= 0xf) {
3999 switch ( (vi
.dwMajorVersion
<< 4 | vi
.dwMinorVersion
) << 1
4000 | (vi
.wProductType
> VER_NT_WORKSTATION
? 1 : 0) ) {
4002 case 0x50<<1 | 1: w
= "2000"; break;
4003 case 0x51<<1 : w
= "xp"; break;
4004 case 0x52<<1 : w
= "xp64"; break;
4005 case 0x52<<1 | 1: w
= (!GetSystemMetrics(89/*SM_SERVERR2*/)
4008 case 0x60<<1 : w
= "vista"; break;
4009 case 0x60<<1 | 1: w
= "2008"; break;
4010 case 0x61<<1 : w
= "win7"; break;
4011 case 0x61<<1 | 1: w
= "2008r2"; break;
4012 case 0x62<<1 : w
= "win8"; break;
4013 case 0x62<<1 | 1: w
= "2012"; break;
4014 case 0x63<<1 : w
= "win8.1"; break;
4015 case 0x63<<1 | 1: w
= "2012r2"; break;
4017 switch (vi
.dwBuildNumber
) {
4018 case 10240: w
= "w10-1507"; break;
4019 case 10586: w
= "w10-1511"; break;
4020 case 14393: w
= "w10-1607"; break;
4021 case 15063: w
= "w10-1703"; break;
4022 case 16299: w
= "w10-1709"; break;
4023 case 17134: w
= "w10-1803"; break;
4024 case 17763: w
= "w10-1809"; break;
4026 build
= vi
.dwBuildNumber
; break;
4029 switch (vi
.dwBuildNumber
) {
4030 case 14393: w
= "2016"; break;
4031 case 16299: w
= "2016-1709"; break;
4032 case 17134: w
= "2016-1803"; break;
4033 case 17763: w
= "2019"; break;
4034 default: w
= (vi
.dwBuildNumber
< 17763
4037 build
= vi
.dwBuildNumber
; break;
4042 const char * w64
= "";
4049 snprintf(vptr
, vlen
, "-%s%u.%u%s",
4050 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "??"),
4051 (unsigned)vi
.dwMajorVersion
, (unsigned)vi
.dwMinorVersion
, w64
);
4053 snprintf(vptr
, vlen
, "-%s-b%u%s", w
, build
, w64
);
4054 else if (vi
.wServicePackMinor
)
4055 snprintf(vptr
, vlen
, "-%s-sp%u.%u%s", w
, vi
.wServicePackMajor
, vi
.wServicePackMinor
, w64
);
4056 else if (vi
.wServicePackMajor
)
4057 snprintf(vptr
, vlen
, "-%s-sp%u%s", w
, vi
.wServicePackMajor
, w64
);
4059 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
4064 // MSVCRT only provides ftime() which uses GetSystemTime()
4065 // This provides only ~15ms resolution by default.
4066 // Use QueryPerformanceCounter instead (~300ns).
4067 // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
4068 int64_t win_smart_interface::get_timer_usec()
4070 static int64_t freq
= 0;
4074 freq
= (QueryPerformanceFrequency(&t
) ? t
.QuadPart
: -1);
4076 return smart_interface::get_timer_usec();
4078 if (!QueryPerformanceCounter(&t
))
4080 if (!(0 <= t
.QuadPart
&& t
.QuadPart
<= (int64_t)(~(uint64_t)0 >> 1)/1000000))
4083 return (t
.QuadPart
* 1000000LL) / freq
;
4085 #endif // __CYGWIN__
4088 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
4090 const char * testname
= skipdev(name
);
4091 if (!strncmp(testname
, "csmi", 4))
4092 return new win_csmi_device(this, name
, type
);
4093 if (!strncmp(testname
, "tw_cli", 6))
4094 return new win_tw_cli_device(this, name
, type
);
4095 return new win_ata_device(this, name
, type
);
4098 scsi_device
* win_smart_interface::get_scsi_device(const char * name
, const char * type
)
4100 return new win_scsi_device(this, name
, type
);
4103 nvme_device
* win_smart_interface::get_nvme_device(const char * name
, const char * type
,
4106 if (str_starts_with(skipdev(name
), "nvme"))
4107 return new win_nvme_device(this, name
, type
, nsid
);
4108 return new win10_nvme_device(this, name
, type
, nsid
);
4112 smart_device
* win_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
4115 int disknum
= -1, n1
= -1, n2
= -1;
4119 if (sscanf(type
, "areca,%n%d/%d%n", &n1
, &disknum
, &encnum
, &n2
) >= 1 || n1
== 6) {
4120 if (!(1 <= disknum
&& disknum
<= 128)) {
4121 set_err(EINVAL
, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum
);
4124 if (!(1 <= encnum
&& encnum
<= 8)) {
4125 set_err(EINVAL
, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum
);
4129 name
= skipdev(name
);
4130 #define ARECA_MAX_CTLR_NUM 16
4133 if (sscanf(name
, "arcmsr%d%n", &ctlrindex
, &n1
) >= 1 && n1
== (int)strlen(name
)) {
4135 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
4136 2. map arcmsrX into "\\\\.\\scsiX"
4138 for (int idx
= 0; idx
< ARECA_MAX_CTLR_NUM
; idx
++) {
4139 memset(devpath
, 0, sizeof(devpath
));
4140 snprintf(devpath
, sizeof(devpath
), "\\\\.\\scsi%d:", idx
);
4141 win_areca_ata_device
*arcdev
= new win_areca_ata_device(this, devpath
, disknum
, encnum
);
4142 if(arcdev
->arcmsr_probe()) {
4143 if(ctlrindex
-- == 0) {
4149 set_err(ENOENT
, "No Areca controller found");
4152 set_err(EINVAL
, "Option -d areca,N/E requires device name /dev/arcmsrX");
4157 unsigned ctrnum
, lun
, target
;
4160 if ( sscanf(type
, "aacraid,%u,%u,%u%n", &ctrnum
, &lun
, &target
, &n1
) >= 3
4161 && n1
== (int)strlen(type
)) {
4162 #define aacraid_MAX_CTLR_NUM 16
4163 if (ctrnum
>= aacraid_MAX_CTLR_NUM
) {
4164 set_err(EINVAL
, "aacraid: invalid host number %u", ctrnum
);
4169 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and
4170 2. map ARCX into "\\\\.\\scsiX"
4172 memset(devpath
, 0, sizeof(devpath
));
4173 unsigned ctlrindex
= 0;
4174 for (int portNum
= 0; portNum
< aacraid_MAX_CTLR_NUM
; portNum
++){
4176 snprintf(subKey
, sizeof(subKey
), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum
);
4178 long regStatus
= RegOpenKeyExA(HKEY_LOCAL_MACHINE
, subKey
, 0, KEY_READ
, &hScsiKey
);
4179 if (regStatus
== ERROR_SUCCESS
){
4180 char driverName
[20];
4181 DWORD driverNameSize
= sizeof(driverName
);
4183 regStatus
= RegQueryValueExA(hScsiKey
, "Driver", NULL
, ®Type
, (LPBYTE
) driverName
, &driverNameSize
);
4184 if (regStatus
== ERROR_SUCCESS
){
4185 if (regType
== REG_SZ
){
4186 if (stricmp(driverName
, "arcsas") == 0){
4187 if(ctrnum
== ctlrindex
){
4188 snprintf(devpath
, sizeof(devpath
), "\\\\.\\Scsi%d:", portNum
);
4189 return get_sat_device("sat,auto",
4190 new win_aacraid_device(this, devpath
, ctrnum
, target
, lun
));
4196 RegCloseKey(hScsiKey
);
4200 set_err(EINVAL
, "aacraid: host %u not found", ctrnum
);
4207 std::string
win_smart_interface::get_valid_custom_dev_types_str()
4209 return "aacraid,H,L,ID, areca,N[/E]";
4213 // Return value for device detection functions
4214 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_SAT
, DEV_USB
, DEV_NVME
};
4216 // Return true if ATA drive behind a SAT layer
4217 static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
4219 if (!data
->desc
.VendorIdOffset
)
4221 if (strcmp(data
->raw
+ data
->desc
.VendorIdOffset
, "ATA "))
4226 // Return true if Intel ICHxR RAID volume
4227 static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
4229 if (!(data
->desc
.VendorIdOffset
&& data
->desc
.ProductIdOffset
))
4231 const char * vendor
= data
->raw
+ data
->desc
.VendorIdOffset
;
4232 if (!(!strnicmp(vendor
, "Intel", 5) && strspn(vendor
+5, " ") == strlen(vendor
+5)))
4234 if (strnicmp(data
->raw
+ data
->desc
.ProductIdOffset
, "Raid ", 5))
4239 // get DEV_* for open handle
4240 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4242 // Get BusType from device descriptor
4243 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
4244 if (storage_query_property_ioctl(hdevice
, &data
))
4247 // Newer BusType* values are missing in older includes
4248 switch ((int)data
.desc
.BusType
) {
4250 case 0x0b: // BusTypeSata
4251 // Certain Intel AHCI drivers (C600+/C220+) have broken
4252 // IOCTL_ATA_PASS_THROUGH support and a working SAT layer
4257 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
4265 // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
4266 if (is_intel_raid_volume(&data
))
4268 // LSI/3ware RAID volume: supports SMART_*
4269 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
4274 case 0x09: // BusTypeiScsi
4275 case 0x0a: // BusTypeSas
4284 case 0x11: // BusTypeNvme
4287 case 0x12: //BusTypeSCM
4288 case 0x13: //BusTypeUfs
4289 case 0x14: //BusTypeMax,
4296 // get DEV_* for device path
4297 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
4300 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
4301 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4302 if (h
== INVALID_HANDLE_VALUE
) {
4304 h
= CreateFileA(path
, 0,
4305 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4306 if (h
== INVALID_HANDLE_VALUE
)
4309 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
4310 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
4311 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
4316 // get DEV_* for physical drive number
4317 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4320 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
4321 return get_controller_type(path
, ata_version_ex
);
4324 static win_dev_type
get_phy_drive_type(int drive
)
4326 return get_phy_drive_type(drive
, 0);
4329 // get DEV_* for logical drive number
4330 static win_dev_type
get_log_drive_type(int drive
)
4333 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
4334 return get_controller_type(path
);
4337 static win_dev_type
get_dev_type(const char * name
, int & phydrive
, int & logdrive
)
4339 phydrive
= logdrive
= -1;
4341 name
= skipdev(name
);
4342 if (!strncmp(name
, "st", 2))
4344 if (!strncmp(name
, "nst", 3))
4346 if (!strncmp(name
, "tape", 4))
4349 logdrive
= drive_letter(name
);
4350 if (logdrive
>= 0) {
4351 win_dev_type type
= get_log_drive_type(logdrive
);
4352 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
4355 char drive
[2+1] = "";
4356 if (sscanf(name
, "sd%2[a-z]", drive
) == 1) {
4357 phydrive
= sdxy_to_phydrive(drive
);
4358 return get_phy_drive_type(phydrive
);
4361 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
4362 return get_phy_drive_type(phydrive
);
4368 smart_device
* win_smart_interface::get_usb_device(const char * name
,
4369 int phydrive
, int logdrive
/* = -1 */)
4371 // Get USB bridge ID
4372 unsigned short vendor_id
= 0, product_id
= 0;
4373 if (!get_usb_id(phydrive
, logdrive
, vendor_id
, product_id
)) {
4374 set_err(EINVAL
, "Unable to read USB device ID");
4378 // Get type name for this ID
4379 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
4383 // Return SAT/USB device for this type
4384 return get_scsi_passthrough_device(usbtype
, new win_scsi_device(this, name
, ""));
4387 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
4389 const char * testname
= skipdev(name
);
4390 if (str_starts_with(testname
, "hd"))
4391 return new win_ata_device(this, name
, "");
4393 if (str_starts_with(testname
, "tw_cli"))
4394 return new win_tw_cli_device(this, name
, "");
4396 if (str_starts_with(testname
, "csmi"))
4397 return new win_csmi_device(this, name
, "");
4399 if (str_starts_with(testname
, "nvme"))
4400 return new win_nvme_device(this, name
, "", 0 /* use default nsid */);
4402 int phydrive
= -1, logdrive
= -1;
4403 win_dev_type type
= get_dev_type(name
, phydrive
, logdrive
);
4405 if (type
== DEV_ATA
)
4406 return new win_ata_device(this, name
, "");
4408 if (type
== DEV_SCSI
)
4409 return new win_scsi_device(this, name
, "");
4411 if (type
== DEV_SAT
)
4412 return get_sat_device("sat", new win_scsi_device(this, name
, ""));
4414 if (type
== DEV_USB
)
4415 return get_usb_device(name
, phydrive
, logdrive
);
4417 if (type
== DEV_NVME
)
4418 return new win10_nvme_device(this, name
, "", 0 /* use default nsid */);
4425 bool win_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
4426 const char * type
, const char * pattern
/* = 0*/)
4429 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
4433 // Check for "[*,]pd" type
4435 char type2
[16+1] = "";
4438 if (!strcmp(type
, "pd")) {
4442 else if (sscanf(type
, "%16[^,],pd%n", type2
, &nc
) == 1 &&
4443 nc
== (int)strlen(type
)) {
4450 bool ata
, scsi
, sat
, usb
, csmi
, nvme
;
4452 ata
= scsi
= usb
= sat
= csmi
= true;
4453 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
4460 ata
= scsi
= usb
= sat
= csmi
= nvme
= false;
4461 if (!strcmp(type
, "ata"))
4463 else if (!strcmp(type
, "scsi"))
4465 else if (!strcmp(type
, "sat"))
4467 else if (!strcmp(type
, "usb"))
4469 else if (!strcmp(type
, "csmi"))
4471 else if (!strcmp(type
, "nvme"))
4475 "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], "
4476 "sat[,pd], usb[,pd], csmi, nvme, pd", type
);
4483 if (ata
|| scsi
|| sat
|| usb
|| nvme
) {
4484 // Scan up to 128 drives and 2 3ware controllers
4485 const int max_raid
= 2;
4486 bool raid_seen
[max_raid
] = {false, false};
4488 for (int i
= 0; i
< 128; i
++) {
4490 snprintf(name
, sizeof(name
), "/dev/pd%d", i
);
4491 else if (i
+ 'a' <= 'z')
4492 snprintf(name
, sizeof(name
), "/dev/sd%c", i
+ 'a');
4494 snprintf(name
, sizeof(name
), "/dev/sd%c%c",
4495 i
/ ('z'-'a'+1) - 1 + 'a',
4496 i
% ('z'-'a'+1) + 'a');
4498 smart_device
* dev
= 0;
4499 GETVERSIONINPARAMS_EX vers_ex
;
4501 switch (get_phy_drive_type(i
, (ata
? &vers_ex
: 0))) {
4503 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
4507 // Interpret RAID drive map if present
4508 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
4509 // Skip if too many controllers or logical drive from this controller already seen
4510 if (!(vers_ex
.wControllerId
< max_raid
&& !raid_seen
[vers_ex
.wControllerId
]))
4512 raid_seen
[vers_ex
.wControllerId
] = true;
4513 // Add physical drives
4514 int len
= strlen(name
);
4515 for (unsigned int pi
= 0; pi
< 32; pi
++) {
4516 if (vers_ex
.dwDeviceMapEx
& (1U << pi
)) {
4517 snprintf(name
+len
, sizeof(name
)-1-len
, ",%u", pi
);
4518 devlist
.push_back( new win_ata_device(this, name
, "ata") );
4524 dev
= new win_ata_device(this, name
, "ata");
4528 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
4531 dev
= new win_scsi_device(this, name
, "scsi");
4535 // STORAGE_QUERY_PROPERTY returned VendorId "ATA "
4538 dev
= get_sat_device("sat", new win_scsi_device(this, name
, ""));
4542 // STORAGE_QUERY_PROPERTY returned USB
4545 dev
= get_usb_device(name
, i
);
4547 // Unknown or unsupported USB ID, return as SCSI
4548 dev
= new win_scsi_device(this, name
, "");
4552 // STORAGE_QUERY_PROPERTY returned NVMe
4555 dev
= new win10_nvme_device(this, name
, "", 0 /* use default nsid */);
4563 devlist
.push_back(dev
);
4568 // Scan CSMI devices
4569 for (int i
= 0; i
<= 9; i
++) {
4570 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,0", i
);
4571 win_csmi_device
test_dev(this, name
, "");
4572 if (!test_dev
.open_scsi())
4575 unsigned ports_used
= test_dev
.get_ports_used();
4579 for (int pi
= 0; pi
< 32; pi
++) {
4580 if (!(ports_used
& (1U << pi
)))
4582 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,%d", i
, pi
);
4583 devlist
.push_back( new win_csmi_device(this, name
, "ata") );
4589 // Scan \\.\Scsi[0-31] for up to 10 NVMe devices
4591 for (int i
= 0; i
< 32; i
++) {
4592 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4593 win_nvme_device
test_dev(this, name
, "", 0);
4594 if (!test_dev
.open_scsi(i
)) {
4595 if (test_dev
.get_errno() == EACCES
)
4600 if (!test_dev
.probe())
4602 if (++nvme_cnt
>= 10)
4606 for (int i
= 0; i
< nvme_cnt
; i
++) {
4607 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4608 devlist
.push_back( new win_nvme_device(this, name
, "nvme", 0) );
4615 // get examples for smartctl
4616 std::string
win_smart_interface::get_app_examples(const char * appname
)
4618 if (strcmp(appname
, "smartctl"))
4620 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
4621 " smartctl -a /dev/sda (Prints all SMART information)\n\n"
4622 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
4623 " (Enables SMART on first disk)\n\n"
4624 " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n"
4625 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
4626 " (Prints Self-Test & Attribute errors)\n"
4627 " smartctl -a /dev/sda\n"
4628 " (Prints all information for disk on PhysicalDrive 0)\n"
4629 " smartctl -a /dev/pd3\n"
4630 " (Prints all information for disk on PhysicalDrive 3)\n"
4631 " smartctl -a /dev/tape1\n"
4632 " (Prints all information for SCSI tape on Tape 1)\n"
4633 " smartctl -A /dev/hdb,3\n"
4634 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
4635 " smartctl -A /dev/tw_cli/c0/p1\n"
4636 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
4637 " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
4638 " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
4639 " on 1st Areca RAID controller)\n"
4641 " ATA SMART access methods and ordering may be specified by modifiers\n"
4642 " following the device name: /dev/hdX:[saicm], where\n"
4643 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
4644 " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
4645 " 'm': IOCTL_SCSI_MINIPORT_*.\n"
4647 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
4652 bool win_smart_interface::disable_system_auto_standby(bool disable
)
4655 SYSTEM_POWER_STATUS ps
;
4656 if (!GetSystemPowerStatus(&ps
))
4657 return set_err(ENOSYS
, "Unknown power status");
4658 if (ps
.ACLineStatus
!= 1) {
4659 SetThreadExecutionState(ES_CONTINUOUS
);
4660 if (ps
.ACLineStatus
== 0)
4661 set_err(EIO
, "AC offline");
4663 set_err(EIO
, "Unknown AC line status");
4668 if (!SetThreadExecutionState(ES_CONTINUOUS
| (disable
? ES_SYSTEM_REQUIRED
: 0)))
4669 return set_err(ENOSYS
);
4676 /////////////////////////////////////////////////////////////////////////////
4678 // Initialize platform interface and register with smi()
4679 void smart_interface::init()
4682 // Remove "." from DLL search path if supported
4683 // to prevent DLL preloading attacks
4684 BOOL (WINAPI
* SetDllDirectoryA_p
)(LPCSTR
) = (BOOL (WINAPI
*)(LPCSTR
))
4685 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4686 if (SetDllDirectoryA_p
)
4687 SetDllDirectoryA_p("");
4690 static os_win32::win_smart_interface the_win_interface
;
4691 smart_interface::set(&the_win_interface
);
4697 // Get exe directory
4698 // (prototype in utiliy.h)
4699 std::string
get_exe_dir()
4701 char path
[MAX_PATH
];
4702 // Get path of this exe
4703 if (!GetModuleFileNameA(GetModuleHandleA(0), path
, sizeof(path
)))
4704 throw std::runtime_error("GetModuleFileName() failed");
4705 // Replace backslash by slash
4707 for (int i
= 0; path
[i
]; i
++)
4708 if (path
[i
] == '\\') {
4709 path
[i
] = '/'; sl
= i
;