4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-16 Christian Franke
8 * Original AACRaid code:
9 * Copyright (C) 2015 Nidhi Malhotra <nidhi.malhotra@pmcs.com>
11 * Original Areca code:
12 * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2, or (at your option)
19 * You should have received a copy of the GNU General Public License
20 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
26 #define _WIN32_WINNT WINVER
33 #include "smartctl.h" // TODO: Do not use smartctl only variables here
35 #include "dev_interface.h"
36 #include "dev_ata_cmd_set.h"
37 #include "dev_areca.h"
39 #include "os_win32/wmiquery.h"
47 #define assert(x) /* */
50 #include <stddef.h> // offsetof()
51 #include <io.h> // access()
53 // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
54 #define WIN32_LEAN_AND_MEAN
58 // i686-pc-cygwin, i686-w64-mingw32, x86_64-w64-mingw32
59 // (Missing: FILE_DEVICE_SCSI)
64 #elif HAVE_DDK_NTDDDISK_H
65 // older i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
66 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
67 #include <ddk/ntdddisk.h>
68 #include <ddk/ntddscsi.h>
69 #include <ddk/ntddstor.h>
71 // MSVC10, older MinGW
72 // (Missing: IOCTL_SCSI_MINIPORT_*)
78 // csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
79 // (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
89 // Silence -Wunused-local-typedefs warning from g++ >= 4.8
91 #define ATTR_UNUSED __attribute__((unused))
93 #define ATTR_UNUSED /**/
96 // Macro to check constants at compile time using a dummy typedef
97 #define ASSERT_CONST(c, n) \
98 typedef char assert_const_##c[((c) == (n)) ? 1 : -1] ATTR_UNUSED
99 #define ASSERT_SIZEOF(t, n) \
100 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1] ATTR_UNUSED
103 #define SELECT_WIN_32_64(x32, x64) (x32)
105 #define SELECT_WIN_32_64(x32, x64) (x64)
108 // Cygwin does no longer provide strn?icmp() compatibility macros
109 // MSVCRT does not provide strn?casecmp()
110 #if defined(__CYGWIN__) && !defined(stricmp)
111 #define stricmp strcasecmp
112 #define strnicmp strncasecmp
115 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 4293 2016-04-14 19:33:05Z chrfranke $";
117 /////////////////////////////////////////////////////////////////////////////
118 // Windows I/O-controls, some declarations are missing in the include files
122 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
124 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
125 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
126 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
127 ASSERT_SIZEOF(GETVERSIONINPARAMS
, 24);
128 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
129 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
132 // IDE PASS THROUGH (2000, XP, undocumented)
134 #ifndef IOCTL_IDE_PASS_THROUGH
136 #define IOCTL_IDE_PASS_THROUGH \
137 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
139 #endif // IOCTL_IDE_PASS_THROUGH
145 ULONG DataBufferSize
;
151 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
152 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
155 // ATA PASS THROUGH (Win2003, XP SP2)
157 #ifndef IOCTL_ATA_PASS_THROUGH
159 #define IOCTL_ATA_PASS_THROUGH \
160 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
162 typedef struct _ATA_PASS_THROUGH_EX
{
168 UCHAR ReservedAsUchar
;
169 ULONG DataTransferLength
;
171 ULONG ReservedAsUlong
;
172 ULONG_PTR DataBufferOffset
;
173 UCHAR PreviousTaskFile
[8];
174 UCHAR CurrentTaskFile
[8];
175 } ATA_PASS_THROUGH_EX
;
177 #define ATA_FLAGS_DRDY_REQUIRED 0x01
178 #define ATA_FLAGS_DATA_IN 0x02
179 #define ATA_FLAGS_DATA_OUT 0x04
180 #define ATA_FLAGS_48BIT_COMMAND 0x08
181 #define ATA_FLAGS_USE_DMA 0x10
182 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
184 #endif // IOCTL_ATA_PASS_THROUGH
186 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
187 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, SELECT_WIN_32_64(40, 48));
190 // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
192 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
193 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT
, 0x04d014);
194 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, SELECT_WIN_32_64(44, 56));
195 ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT
, SELECT_WIN_32_64(44, 56));
198 // SMART IOCTL via SCSI MINIPORT ioctl
200 #ifndef FILE_DEVICE_SCSI
201 #define FILE_DEVICE_SCSI 0x001b
204 #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
206 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
207 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
208 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
209 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
210 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
211 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
212 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
213 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
214 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
215 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
216 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
217 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
218 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
220 #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
222 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
223 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
226 // IOCTL_STORAGE_QUERY_PROPERTY
228 #ifndef IOCTL_STORAGE_QUERY_PROPERTY
230 #define IOCTL_STORAGE_QUERY_PROPERTY \
231 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
233 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
237 UCHAR DeviceTypeModifier
;
238 BOOLEAN RemovableMedia
;
239 BOOLEAN CommandQueueing
;
240 ULONG VendorIdOffset
;
241 ULONG ProductIdOffset
;
242 ULONG ProductRevisionOffset
;
243 ULONG SerialNumberOffset
;
244 STORAGE_BUS_TYPE BusType
;
245 ULONG RawPropertiesLength
;
246 UCHAR RawDeviceProperties
[1];
247 } STORAGE_DEVICE_DESCRIPTOR
;
249 typedef enum _STORAGE_QUERY_TYPE
{
250 PropertyStandardQuery
= 0,
253 PropertyQueryMaxDefined
254 } STORAGE_QUERY_TYPE
;
256 typedef enum _STORAGE_PROPERTY_ID
{
257 StorageDeviceProperty
= 0,
258 StorageAdapterProperty
,
259 StorageDeviceIdProperty
,
260 StorageDeviceUniqueIdProperty
,
261 StorageDeviceWriteCacheProperty
,
262 StorageMiniportProperty
,
263 StorageAccessAlignmentProperty
264 } STORAGE_PROPERTY_ID
;
266 typedef struct _STORAGE_PROPERTY_QUERY
{
267 STORAGE_PROPERTY_ID PropertyId
;
268 STORAGE_QUERY_TYPE QueryType
;
269 UCHAR AdditionalParameters
[1];
270 } STORAGE_PROPERTY_QUERY
;
272 #endif // IOCTL_STORAGE_QUERY_PROPERTY
274 ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY
, 0x002d1400);
275 ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR
, 36+1+3);
276 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY
, 8+1+3);
279 // IOCTL_STORAGE_PREDICT_FAILURE
281 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE
, 0x002d1100);
282 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
285 // 3ware specific versions of SMART ioctl structs
287 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
291 typedef struct _GETVERSIONINPARAMS_EX
{
297 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
298 WORD wIdentifier
; // Vendor specific identifier
299 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
301 } GETVERSIONINPARAMS_EX
;
303 typedef struct _SENDCMDINPARAMS_EX
{
307 BYTE bPortNumber
; // 3ware specific: port number
308 WORD wIdentifier
; // Vendor specific identifier
311 } SENDCMDINPARAMS_EX
;
315 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONINPARAMS
));
316 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
321 #ifndef NVME_PASS_THROUGH_SRB_IO_CODE
323 #define NVME_SIG_STR "NvmeMini"
324 #define NVME_STORPORT_DRIVER 0xe000
326 #define NVME_PASS_THROUGH_SRB_IO_CODE \
327 CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)
330 typedef struct _NVME_PASS_THROUGH_IOCTL
332 SRB_IO_CONTROL SrbIoCtrl
;
333 ULONG VendorSpecific
[6];
334 ULONG NVMeCmd
[16]; // Command DW[0...15]
335 ULONG CplEntry
[4]; // Completion DW[0...3]
336 ULONG Direction
; // 0=No, 1=Out, 2=In, 3=I/O
337 ULONG QueueId
; // 0=AdminQ
338 ULONG DataBufferLen
; // sizeof(DataBuffer) if Data In
340 ULONG ReturnBufferLen
; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
342 } NVME_PASS_THROUGH_IOCTL
;
345 #endif // NVME_PASS_THROUGH_SRB_IO_CODE
347 ASSERT_CONST(NVME_PASS_THROUGH_SRB_IO_CODE
, (int)0xe0002000);
348 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, 152+1);
349 ASSERT_SIZEOF(NVME_PASS_THROUGH_IOCTL
, offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
)+1);
354 ASSERT_SIZEOF(IOCTL_HEADER
, sizeof(SRB_IO_CONTROL
));
355 ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER
, 204);
356 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER
, 2080);
357 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER
, 168);
361 ASSERT_SIZEOF(SCSI_REQUEST_BLOCK
, SELECT_WIN_32_64(64, 88));
365 /////////////////////////////////////////////////////////////////////////////
367 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
370 #pragma warning(disable:4250)
373 static int is_permissive()
375 if (!failuretest_permissive
) {
376 pout("To continue, add one or more '-T permissive' options.\n");
379 failuretest_permissive
--;
383 // return number for drive letter, -1 on error
384 // "[A-Za-z]:([/\\][.]?)?" => 0-25
385 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
386 static int drive_letter(const char * s
)
388 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
390 && (!s
[2] || ( strchr("/\\\"", s
[2])
391 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
392 (s
[0] & 0x1f) - 1 : -1);
395 // Skip trailing "/dev/", do not allow "/dev/X:"
396 static const char * skipdev(const char * s
)
398 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
401 // "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
402 static int sdxy_to_phydrive(const char (& xy
)[2+1])
404 int phydrive
= xy
[0] - 'a';
406 phydrive
= (phydrive
+ 1) * ('z' - 'a' + 1) + (xy
[1] - 'a');
410 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
412 int srclen
= strcspn(src
, "\r\n");
414 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
415 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
417 if (i
< destsize
-1 && i
< srclen
)
422 /////////////////////////////////////////////////////////////////////////////
425 class win_smart_device
426 : virtual public /*implements*/ smart_device
430 : smart_device(never_called
),
431 m_fh(INVALID_HANDLE_VALUE
)
434 virtual ~win_smart_device() throw();
436 virtual bool is_open() const;
438 virtual bool close();
441 /// Set handle for open() in derived classes.
442 void set_fh(HANDLE fh
)
445 /// Return handle for derived classes.
446 HANDLE
get_fh() const
450 HANDLE m_fh
; ///< File handle
454 // Common routines for devices with HANDLEs
456 win_smart_device::~win_smart_device() throw()
458 if (m_fh
!= INVALID_HANDLE_VALUE
)
462 bool win_smart_device::is_open() const
464 return (m_fh
!= INVALID_HANDLE_VALUE
);
467 bool win_smart_device::close()
469 if (m_fh
== INVALID_HANDLE_VALUE
)
471 BOOL rc
= ::CloseHandle(m_fh
);
472 m_fh
= INVALID_HANDLE_VALUE
;
477 /////////////////////////////////////////////////////////////////////////////
479 #define SMART_CYL_LOW 0x4F
480 #define SMART_CYL_HI 0xC2
482 static void print_ide_regs(const IDEREGS
* r
, int out
)
484 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
485 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
486 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
489 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
491 pout(" Input : "); print_ide_regs(ri
, 0);
493 pout(" Output: "); print_ide_regs(ro
, 1);
497 /////////////////////////////////////////////////////////////////////////////
499 // call SMART_GET_VERSION, return device map or -1 on error
501 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
503 GETVERSIONINPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
504 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
507 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
508 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
510 pout(" SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
514 assert(num_out
== sizeof(GETVERSIONINPARAMS
));
516 if (ata_debugmode
> 1) {
517 pout(" SMART_GET_VERSION suceeded, bytes returned: %u\n"
518 " Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
519 (unsigned)num_out
, vers
.bVersion
, vers
.bRevision
,
520 (unsigned)vers
.fCapabilities
, vers
.bIDEDeviceMap
);
521 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
522 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
523 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, (unsigned)vers_ex
.dwDeviceMapEx
);
527 *ata_version_ex
= vers_ex
;
529 // TODO: Check vers.fCapabilities here?
530 return vers
.bIDEDeviceMap
;
534 // call SMART_* ioctl
536 static int smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
538 SENDCMDINPARAMS inpar
;
539 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
541 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
542 const SENDCMDOUTPARAMS
* outpar
;
544 unsigned int size_out
;
547 memset(&inpar
, 0, sizeof(inpar
));
548 inpar
.irDriveRegs
= *regs
;
550 // Older drivers may require bits 5 and 7 set
551 // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
552 inpar
.irDriveRegs
.bDriveHeadReg
|= 0xa0;
554 // Drive number 0-3 was required on Win9x/ME only
555 //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
556 //inpar.bDriveNumber = drive;
560 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
561 inpar_ex
.bPortNumber
= port
;
564 if (datasize
== 512) {
565 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
566 inpar
.cBufferSize
= size_out
= 512;
568 else if (datasize
== 0) {
569 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
570 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
571 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
572 // Note: cBufferSize must be 0 on Win9x
581 memset(&outbuf
, 0, sizeof(outbuf
));
583 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
584 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
585 // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
586 long err
= GetLastError();
587 if (ata_debugmode
&& (err
!= ERROR_INVALID_PARAMETER
|| ata_debugmode
> 1)) {
588 pout(" %s failed, Error=%ld\n", name
, err
);
589 print_ide_regs_io(regs
, NULL
);
591 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
592 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
593 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
596 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
598 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
600 if (outpar
->DriverStatus
.bDriverError
) {
602 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
603 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
604 print_ide_regs_io(regs
, NULL
);
606 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
610 if (ata_debugmode
> 1) {
611 pout(" %s suceeded, bytes returned: %u (buffer %u)\n", name
,
612 (unsigned)num_out
, (unsigned)outpar
->cBufferSize
);
613 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
614 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
618 memcpy(data
, outpar
->bBuffer
, 512);
619 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
620 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
621 memcpy(regs
, outpar
->bBuffer
, sizeof(IDEREGS
));
622 else { // Workaround for driver not returning regs
624 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
625 *regs
= inpar
.irDriveRegs
;
633 /////////////////////////////////////////////////////////////////////////////
634 // IDE PASS THROUGH (2000, XP, undocumented)
636 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
637 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
639 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
641 if (datasize
> 512) {
645 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
646 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
648 const unsigned char magic
= 0xcf;
656 buf
->DataBufferSize
= datasize
;
658 buf
->DataBuffer
[0] = magic
;
660 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
661 buf
, size
, buf
, size
, &num_out
, NULL
)) {
662 long err
= GetLastError();
664 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
665 print_ide_regs_io(regs
, NULL
);
667 VirtualFree(buf
, 0, MEM_RELEASE
);
668 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
673 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
675 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
676 print_ide_regs_io(regs
, &buf
->IdeReg
);
678 VirtualFree(buf
, 0, MEM_RELEASE
);
683 // Check and copy data
686 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
688 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
689 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
690 print_ide_regs_io(regs
, &buf
->IdeReg
);
692 VirtualFree(buf
, 0, MEM_RELEASE
);
696 memcpy(data
, buf
->DataBuffer
, datasize
);
699 if (ata_debugmode
> 1) {
700 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
701 (unsigned)num_out
, (unsigned)buf
->DataBufferSize
);
702 print_ide_regs_io(regs
, &buf
->IdeReg
);
706 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
707 VirtualFree(buf
, 0, MEM_RELEASE
);
712 /////////////////////////////////////////////////////////////////////////////
713 // ATA PASS THROUGH (Win2003, XP SP2)
716 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
717 // transfer per command. Therefore, multi-sector transfers are only supported
718 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
719 // or READ/WRITE LOG EXT work only with single sector transfers.
720 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
722 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
724 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
726 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
729 ATA_PASS_THROUGH_EX apt
;
731 UCHAR ucDataBuf
[max_sectors
* 512];
732 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
734 const unsigned char magic
= 0xcf;
736 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
737 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
739 //ab.apt.TargetId = 0;
741 ab
.apt
.TimeOutValue
= 10;
742 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
743 ab
.apt
.DataBufferOffset
= size
;
746 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
750 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
751 ab
.apt
.DataTransferLength
= datasize
;
753 ab
.ucDataBuf
[0] = magic
;
755 else if (datasize
< 0) {
756 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
760 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
761 ab
.apt
.DataTransferLength
= -datasize
;
763 memcpy(ab
.ucDataBuf
, data
, -datasize
);
766 assert(ab
.apt
.AtaFlags
== 0);
767 assert(ab
.apt
.DataTransferLength
== 0);
770 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
771 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
772 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
776 *ptfregs
= *prev_regs
;
777 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
781 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
782 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
783 long err
= GetLastError();
785 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
786 print_ide_regs_io(regs
, NULL
);
788 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
793 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
795 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
796 print_ide_regs_io(regs
, ctfregs
);
802 // Check and copy data
805 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
807 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out
);
808 print_ide_regs_io(regs
, ctfregs
);
813 memcpy(data
, ab
.ucDataBuf
, datasize
);
816 if (ata_debugmode
> 1) {
817 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out
);
818 print_ide_regs_io(regs
, ctfregs
);
822 *prev_regs
= *ptfregs
;
828 /////////////////////////////////////////////////////////////////////////////
829 // SMART IOCTL via SCSI MINIPORT ioctl
831 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
832 // miniport driver (via SCSI port driver scsiport.sys).
833 // It can be used to skip the missing or broken handling of some SMART
834 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
836 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
839 DWORD code
= 0; const char * name
= 0;
840 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
841 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
843 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
844 case ATA_SMART_READ_VALUES
:
845 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
846 case ATA_SMART_READ_THRESHOLDS
:
847 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
848 case ATA_SMART_ENABLE
:
849 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
850 case ATA_SMART_DISABLE
:
851 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
852 case ATA_SMART_STATUS
:
853 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
854 case ATA_SMART_AUTOSAVE
:
855 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
856 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
857 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
858 case ATA_SMART_IMMEDIATE_OFFLINE
:
859 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
860 case ATA_SMART_AUTO_OFFLINE
:
861 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
862 case ATA_SMART_READ_LOG_SECTOR
:
863 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
864 case ATA_SMART_WRITE_LOG_SECTOR
:
865 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
877 SENDCMDOUTPARAMS out
;
881 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
882 memset(&sb
, 0, sizeof(sb
));
886 if (datasize
> (int)sizeof(sb
.space
)+1) {
892 else if (datasize
< 0) {
893 if (-datasize
> (int)sizeof(sb
.space
)+1) {
898 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
900 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
901 size
= sizeof(IDEREGS
);
904 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
905 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
906 sb
.srbc
.Timeout
= 60; // seconds
907 sb
.srbc
.ControlCode
= code
;
908 //sb.srbc.ReturnCode = 0;
909 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
910 sb
.params
.in
.irDriveRegs
= *regs
;
911 sb
.params
.in
.cBufferSize
= size
;
913 // Call miniport ioctl
914 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
916 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
917 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
918 long err
= GetLastError();
920 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
921 print_ide_regs_io(regs
, NULL
);
923 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
928 if (sb
.srbc
.ReturnCode
) {
930 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name
, (unsigned)sb
.srbc
.ReturnCode
);
931 print_ide_regs_io(regs
, NULL
);
937 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
939 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
940 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
941 print_ide_regs_io(regs
, NULL
);
943 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
947 if (ata_debugmode
> 1) {
948 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name
,
949 (unsigned)num_out
, (unsigned)sb
.params
.out
.cBufferSize
);
950 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
951 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
955 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
956 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
957 memcpy(regs
, sb
.params
.out
.bBuffer
, sizeof(IDEREGS
));
963 /////////////////////////////////////////////////////////////////////////////
964 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
966 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
973 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
975 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
979 memset(&sb
, 0, sizeof(sb
));
980 strncpy((char *)sb
.srbc
.Signature
, "<3ware>", sizeof(sb
.srbc
.Signature
));
981 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
982 sb
.srbc
.Timeout
= 60; // seconds
983 sb
.srbc
.ControlCode
= 0xA0000000;
984 sb
.srbc
.ReturnCode
= 0;
985 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
987 sb
.regs
.bReserved
= port
;
990 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
991 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
992 long err
= GetLastError();
994 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
995 print_ide_regs_io(regs
, NULL
);
997 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1001 if (sb
.srbc
.ReturnCode
) {
1002 if (ata_debugmode
) {
1003 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb
.srbc
.ReturnCode
);
1004 print_ide_regs_io(regs
, NULL
);
1012 memcpy(data
, sb
.buffer
, datasize
);
1014 if (ata_debugmode
> 1) {
1015 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out
);
1016 print_ide_regs_io(regs
, &sb
.regs
);
1024 /////////////////////////////////////////////////////////////////////////////
1026 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1027 // 3DM/CLI "Rescan Controller" function does not to always update it.
1029 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1031 SRB_IO_CONTROL srbc
;
1032 memset(&srbc
, 0, sizeof(srbc
));
1033 strncpy((char *)srbc
.Signature
, "<3ware>", sizeof(srbc
.Signature
));
1034 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1035 srbc
.Timeout
= 60; // seconds
1036 srbc
.ControlCode
= 0xCC010014;
1037 srbc
.ReturnCode
= 0;
1041 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1042 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1043 long err
= GetLastError();
1045 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1046 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1049 if (srbc
.ReturnCode
) {
1051 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc
.ReturnCode
);
1055 if (ata_debugmode
> 1)
1056 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1061 /////////////////////////////////////////////////////////////////////////////
1062 // IOCTL_STORAGE_QUERY_PROPERTY
1064 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1065 STORAGE_DEVICE_DESCRIPTOR desc
;
1069 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1070 // (This works without admin rights)
1072 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1074 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
1075 memset(data
, 0, sizeof(*data
));
1078 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1079 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1080 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
1081 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%u\n", (unsigned)GetLastError());
1086 if (ata_debugmode
> 1 || scsi_debugmode
> 1) {
1087 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1089 " Product: \"%s\"\n"
1090 " Revision: \"%s\"\n"
1092 " BusType: 0x%02x\n",
1093 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: "(null)"),
1094 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: "(null)"),
1095 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: "(null)"),
1096 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1103 /////////////////////////////////////////////////////////////////////////////
1104 // IOCTL_STORAGE_PREDICT_FAILURE
1106 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1107 // or -1 on error, opionally return VendorSpecific data.
1108 // (This works without admin rights)
1110 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1112 STORAGE_PREDICT_FAILURE pred
;
1113 memset(&pred
, 0, sizeof(pred
));
1116 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
1117 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
1118 if (ata_debugmode
> 1)
1119 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%u\n", (unsigned)GetLastError());
1124 if (ata_debugmode
> 1) {
1125 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1126 " PredictFailure: 0x%08x\n"
1127 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1128 (unsigned)pred
.PredictFailure
,
1129 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
1130 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
1134 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
1135 return (!pred
.PredictFailure
? 0 : 1);
1139 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1140 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
1142 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1143 if (storage_query_property_ioctl(hdevice
, &data
))
1146 memset(id
, 0, sizeof(*id
));
1148 // Some drivers split ATA model string into VendorId and ProductId,
1149 // others return it as ProductId only.
1150 char model
[sizeof(id
->model
) + 1] = "";
1153 if (data
.desc
.VendorIdOffset
) {
1154 for ( ;i
< sizeof(model
)-1 && data
.raw
[data
.desc
.VendorIdOffset
+i
]; i
++)
1155 model
[i
] = data
.raw
[data
.desc
.VendorIdOffset
+i
];
1158 if (data
.desc
.ProductIdOffset
) {
1159 while (i
> 1 && model
[i
-2] == ' ') // Keep last blank from VendorId
1161 // Ignore VendorId "ATA"
1162 if (i
<= 4 && !strncmp(model
, "ATA", 3) && (i
== 3 || model
[3] == ' '))
1164 for (unsigned j
= 0; i
< sizeof(model
)-1 && data
.raw
[data
.desc
.ProductIdOffset
+j
]; i
++, j
++)
1165 model
[i
] = data
.raw
[data
.desc
.ProductIdOffset
+j
];
1168 while (i
> 0 && model
[i
-1] == ' ')
1171 copy_swapped(id
->model
, model
, sizeof(id
->model
));
1173 if (data
.desc
.ProductRevisionOffset
)
1174 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
1176 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1177 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1181 // Get Serial Number in IDENTIFY from WMI
1182 static bool get_serial_from_wmi(int drive
, ata_identify_device
* id
)
1184 bool debug
= (ata_debugmode
> 1);
1187 if (!ws
.connect()) {
1189 pout("WMI connect failed\n");
1194 if (!ws
.query1(wo
, "SELECT Model,SerialNumber FROM Win32_DiskDrive WHERE "
1195 "DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive
))
1198 std::string serial
= wo
.get_str("SerialNumber");
1200 pout(" WMI:PhysicalDrive%d: \"%s\", S/N:\"%s\"\n", drive
, wo
.get_str("Model").c_str(), serial
.c_str());
1202 copy_swapped(id
->serial_no
, serial
.c_str(), sizeof(id
->serial_no
));
1207 /////////////////////////////////////////////////////////////////////////////
1208 // USB ID detection using WMI
1210 // Get USB ID for a physical or logical drive number
1211 static bool get_usb_id(int phydrive
, int logdrive
,
1212 unsigned short & vendor_id
,
1213 unsigned short & product_id
)
1215 bool debug
= (scsi_debugmode
> 1);
1218 if (!ws
.connect()) {
1220 pout("WMI connect failed\n");
1228 if (0 <= logdrive
&& logdrive
<= 'Z'-'A') {
1229 // Drive letter -> Partition info
1230 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_LogicalDisk.DeviceID=\"%c:\"} WHERE ResultClass = Win32_DiskPartition",
1234 std::string partid
= wo
.get_str("DeviceID");
1236 pout("%c: --> \"%s\" -->\n", 'A'+logdrive
, partid
.c_str());
1238 // Partition ID -> Physical drive info
1239 if (!ws
.query1(wo
, "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"%s\"} WHERE ResultClass = Win32_DiskDrive",
1243 name
= wo
.get_str("Model");
1245 pout("%s --> \"%s\":\n", wo
.get_str("DeviceID").c_str(), name
.c_str());
1248 else if (phydrive
>= 0) {
1249 // Physical drive number -> Physical drive info
1250 if (!ws
.query1(wo
, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", phydrive
))
1253 name
= wo
.get_str("Model");
1255 pout("\\.\\\\PHYSICALDRIVE%d --> \"%s\":\n", phydrive
, name
.c_str());
1261 // Get USB_CONTROLLER -> DEVICE associations
1263 if (!ws
.query(we
, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
1266 unsigned short usb_venid
= 0, prev_usb_venid
= 0;
1267 unsigned short usb_proid
= 0, prev_usb_proid
= 0;
1268 std::string prev_usb_ant
;
1269 std::string prev_ant
, ant
, dep
;
1271 const regular_expression
regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED
);
1273 while (we
.next(wo
)) {
1275 // Find next 'USB_CONTROLLER, DEVICE' pair
1276 ant
= wo
.get_str("Antecedent");
1277 dep
= wo
.get_str("Dependent");
1279 if (debug
&& ant
!= prev_ant
)
1280 pout(" %s:\n", ant
.c_str());
1283 regmatch_t match
[2];
1284 if (!(regex
.execute(dep
.c_str(), 2, match
) && match
[1].rm_so
>= 0)) {
1286 pout(" | (\"%s\")\n", dep
.c_str());
1290 std::string
devid(dep
.c_str()+match
[1].rm_so
, match
[1].rm_eo
-match
[1].rm_so
);
1292 if (str_starts_with(devid
, "USB\\\\VID_")) {
1293 // USB bridge entry, save CONTROLLER, ID
1295 if (!(sscanf(devid
.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
1296 &prev_usb_venid
, &prev_usb_proid
, &nc
) == 2 && nc
== 9+4+5+4)) {
1297 prev_usb_venid
= prev_usb_proid
= 0;
1301 pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid
.c_str(), prev_usb_venid
, prev_usb_proid
);
1303 else if (str_starts_with(devid
, "USBSTOR\\\\") || str_starts_with(devid
, "SCSI\\\\")) {
1304 // USBSTORage or SCSI device found
1306 pout(" +--> \"%s\"\n", devid
.c_str());
1310 if (!ws
.query1(wo2
, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid
.c_str()))
1312 std::string name2
= wo2
.get_str("Name");
1314 // Continue if not name of physical disk drive
1315 if (name2
!= name
) {
1317 pout(" +---> (\"%s\")\n", name2
.c_str());
1321 // Fail if previous USB bridge is associated to other controller or ID is unknown
1322 if (!(ant
== prev_usb_ant
&& prev_usb_venid
)) {
1324 pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2
.c_str());
1328 // Handle multiple devices with same name
1330 // Fail if multiple devices with same name have different USB bridge types
1331 if (!(usb_venid
== prev_usb_venid
&& usb_proid
== prev_usb_proid
)) {
1333 pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2
.c_str());
1339 usb_venid
= prev_usb_venid
;
1340 usb_proid
= prev_usb_proid
;
1342 pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2
.c_str(), usb_venid
, usb_proid
);
1344 // Continue to check for duplicate names ...
1348 pout(" | \"%s\"\n", devid
.c_str());
1355 vendor_id
= usb_venid
;
1356 product_id
= usb_proid
;
1362 /////////////////////////////////////////////////////////////////////////////
1364 // Call GetDevicePowerState()
1365 // returns: 1=active, 0=standby, -1=error
1366 // (This would also work for SCSI drives)
1368 static int get_device_power_state(HANDLE hdevice
)
1371 if (!GetDevicePowerState(hdevice
, &state
)) {
1372 long err
= GetLastError();
1374 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
1375 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1376 // TODO: This may not work as expected on transient errors,
1377 // because smartd interprets -1 as SLEEP mode regardless of errno.
1381 if (ata_debugmode
> 1)
1382 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
1387 /////////////////////////////////////////////////////////////////////////////
1390 class win_ata_device
1391 : public /*implements*/ ata_device
,
1392 public /*extends*/ win_smart_device
1395 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
1397 virtual ~win_ata_device() throw();
1399 virtual bool open();
1401 virtual bool is_powered_down();
1403 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
1405 virtual bool ata_identify_is_cached() const;
1408 bool open(bool query_device
);
1410 bool open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
);
1412 std::string m_options
;
1413 bool m_usr_options
; // options set by user?
1414 bool m_admin
; // open with admin access?
1415 int m_phydrive
; // PhysicalDriveN or -1
1416 bool m_id_is_cached
; // ata_identify_is_cached() return value.
1417 bool m_is_3ware
; // LSI/3ware controller detected?
1418 int m_port
; // LSI/3ware port
1419 int m_smartver_state
;
1423 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1424 : smart_device(intf
, dev_name
, "ata", req_type
),
1425 m_usr_options(false),
1428 m_id_is_cached(false),
1435 win_ata_device::~win_ata_device() throw()
1439 // Get default ATA device options
1441 static const char * ata_get_def_options()
1443 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
1444 // STORAGE_*, SCSI_MINIPORT_*
1449 bool win_ata_device::open()
1451 // Open device for r/w operations
1455 bool win_ata_device::open(bool query_device
)
1457 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
1458 // [sh]d[a-z]([a-z])?(:[saicmfp]+)? => Physical drive 0-701, with options
1459 char drive
[2+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
1460 if ( sscanf(name
, "%*[sh]d%2[a-z]%n:%6[saimfp]%n", drive
, &n1
, options
, &n2
) >= 1
1461 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1462 return open(sdxy_to_phydrive(drive
), -1, options
, -1, query_device
);
1464 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-701, RAID port N, with options
1465 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
1467 if ( sscanf(name
, "%*[sh]d%2[a-z],%u%n:%7[saimfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
1468 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
1469 return open(sdxy_to_phydrive(drive
), -1, options
, port
, query_device
);
1471 // pd<m>,N => Physical drive <m>, RAID port N
1472 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
1473 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
1474 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
1475 return open(phydrive
, -1, "", (int)port
, query_device
);
1477 // [a-zA-Z]: => Physical drive behind logical drive 0-25
1478 int logdrive
= drive_letter(name
);
1479 if (logdrive
>= 0) {
1480 return open(-1, logdrive
, "", -1, query_device
);
1483 return set_err(EINVAL
);
1487 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
, bool query_device
)
1491 if (0 <= phydrive
&& phydrive
<= 255)
1492 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", (m_phydrive
= phydrive
));
1493 else if (0 <= logdrive
&& logdrive
<= 'Z'-'A')
1494 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
1496 return set_err(ENOENT
);
1499 HANDLE h
= INVALID_HANDLE_VALUE
;
1500 if (!(*options
&& !options
[strspn(options
, "fp")]) && !query_device
) {
1501 // Open with admin rights
1503 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
1504 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1505 NULL
, OPEN_EXISTING
, 0, 0);
1507 if (h
== INVALID_HANDLE_VALUE
) {
1508 // Open without admin rights
1510 h
= CreateFileA(devpath
, 0,
1511 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1512 NULL
, OPEN_EXISTING
, 0, 0);
1514 if (h
== INVALID_HANDLE_VALUE
) {
1515 long err
= GetLastError();
1516 if (err
== ERROR_FILE_NOT_FOUND
)
1517 set_err(ENOENT
, "%s: not found", devpath
);
1518 else if (err
== ERROR_ACCESS_DENIED
)
1519 set_err(EACCES
, "%s: access denied", devpath
);
1521 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
1526 // Warn once if admin rights are missing
1527 if (!m_admin
&& !query_device
) {
1528 static bool noadmin_warning
= false;
1529 if (!noadmin_warning
) {
1530 pout("Warning: Limited functionality due to missing admin rights\n");
1531 noadmin_warning
= true;
1535 if (ata_debugmode
> 1)
1536 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
1538 m_usr_options
= false;
1540 // Save user options
1541 m_options
= options
; m_usr_options
= true;
1544 // RAID: SMART_* and SCSI_MINIPORT
1547 // Set default options according to Windows version
1548 static const char * def_options
= ata_get_def_options();
1549 m_options
= def_options
;
1552 // SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
1557 // 3ware RAID: Get port map
1558 GETVERSIONINPARAMS_EX vers_ex
;
1559 int devmap
= smart_get_version(h
, &vers_ex
);
1561 // 3ware RAID if vendor id present
1562 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1564 unsigned long portmap
= 0;
1565 if (port
>= 0 && devmap
>= 0) {
1566 // 3ware RAID: check vendor id
1568 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
1569 "This is no 3ware 9000 controller or driver has no SMART support.\n",
1570 vers_ex
.wIdentifier
);
1574 portmap
= vers_ex
.dwDeviceMapEx
;
1577 pout("%s: ATA driver has no SMART support\n", devpath
);
1578 if (!is_permissive()) {
1580 return set_err(ENOSYS
);
1583 m_smartver_state
= 1;
1586 // 3ware RAID: update devicemap first
1588 if (!update_3ware_devicemap_ioctl(h
)) {
1589 if ( smart_get_version(h
, &vers_ex
) >= 0
1590 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
1591 portmap
= vers_ex
.dwDeviceMapEx
;
1593 // Check port existence
1594 if (!(portmap
& (1L << port
))) {
1595 if (!is_permissive()) {
1597 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
1606 /////////////////////////////////////////////////////////////////////////////
1608 // Query OS if device is powered up or down.
1609 bool win_ata_device::is_powered_down()
1611 // To check power mode, we open device for query operations only.
1612 // Opening for SMART r/w operations can already spin up the disk.
1613 bool self_open
= !is_open();
1617 int rc
= get_device_power_state(get_fh());
1623 /////////////////////////////////////////////////////////////////////////////
1625 // Interface to ATA devices
1626 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
1628 // No multi-sector support for now, see above
1629 // warning about IOCTL_ATA_PASS_THROUGH
1630 if (!ata_cmd_is_supported(in
,
1631 ata_device::supports_data_out
|
1632 ata_device::supports_output_regs
|
1633 ata_device::supports_48bit
)
1637 // 3ware RAID: SMART DISABLE without port number disables SMART functions
1638 if ( m_is_3ware
&& m_port
< 0
1639 && in
.in_regs
.command
== ATA_SMART_CMD
1640 && in
.in_regs
.features
== ATA_SMART_DISABLE
)
1641 return set_err(ENOSYS
, "SMART DISABLE requires 3ware port number");
1643 // Determine ioctl functions valid for this ATA cmd
1644 const char * valid_options
= 0;
1646 switch (in
.in_regs
.command
) {
1647 case ATA_IDENTIFY_DEVICE
:
1648 case ATA_IDENTIFY_PACKET_DEVICE
:
1649 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1650 // and SCSI_MINIPORT_* if requested by user
1651 valid_options
= (m_usr_options
? "saimf" : "saif");
1654 case ATA_CHECK_POWER_MODE
:
1655 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
1656 valid_options
= "pai3";
1660 switch (in
.in_regs
.features
) {
1661 case ATA_SMART_READ_VALUES
:
1662 case ATA_SMART_READ_THRESHOLDS
:
1663 case ATA_SMART_AUTOSAVE
:
1664 case ATA_SMART_ENABLE
:
1665 case ATA_SMART_DISABLE
:
1666 case ATA_SMART_AUTO_OFFLINE
:
1667 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
1668 // and SCSI_MINIPORT_* if requested by user
1669 valid_options
= (m_usr_options
? "saimf" : "saif");
1672 case ATA_SMART_IMMEDIATE_OFFLINE
:
1673 // SMART_SEND_DRIVE_COMMAND does not support ABORT_SELF_TEST
1674 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ ?
1678 case ATA_SMART_READ_LOG_SECTOR
:
1679 // SMART_RCV_DRIVE_DATA does not support READ_LOG
1680 // Try SCSI_MINIPORT also to skip buggy class driver
1681 // SMART functions do not support multi sector I/O.
1683 valid_options
= (m_usr_options
? "saim3" : "aim3");
1685 valid_options
= "a";
1688 case ATA_SMART_WRITE_LOG_SECTOR
:
1689 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
1690 // but SCSI_MINIPORT_* only if requested by user and single sector.
1691 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
1694 case ATA_SMART_STATUS
:
1695 valid_options
= (m_usr_options
? "saimf" : "saif");
1699 // Unknown SMART command, handle below
1705 // Other ATA command, handle below
1709 if (!valid_options
) {
1710 // No special ATA command found above, select a generic pass through ioctl.
1711 if (!( in
.direction
== ata_cmd_in::no_data
1712 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
1713 || in
.in_regs
.is_48bit_cmd() )
1714 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
1715 valid_options
= "a";
1717 // ATA/IDE_PASS_THROUGH
1718 valid_options
= "ai";
1722 // Restrict to IOCTL_STORAGE_*
1723 if (strchr(valid_options
, 'f'))
1724 valid_options
= "f";
1725 else if (strchr(valid_options
, 'p'))
1726 valid_options
= "p";
1728 return set_err(ENOSYS
, "Function requires admin rights");
1732 IDEREGS regs
, prev_regs
;
1734 const ata_in_regs
& lo
= in
.in_regs
;
1735 regs
.bFeaturesReg
= lo
.features
;
1736 regs
.bSectorCountReg
= lo
.sector_count
;
1737 regs
.bSectorNumberReg
= lo
.lba_low
;
1738 regs
.bCylLowReg
= lo
.lba_mid
;
1739 regs
.bCylHighReg
= lo
.lba_high
;
1740 regs
.bDriveHeadReg
= lo
.device
;
1741 regs
.bCommandReg
= lo
.command
;
1744 if (in
.in_regs
.is_48bit_cmd()) {
1745 const ata_in_regs
& hi
= in
.in_regs
.prev
;
1746 prev_regs
.bFeaturesReg
= hi
.features
;
1747 prev_regs
.bSectorCountReg
= hi
.sector_count
;
1748 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
1749 prev_regs
.bCylLowReg
= hi
.lba_mid
;
1750 prev_regs
.bCylHighReg
= hi
.lba_high
;
1751 prev_regs
.bDriveHeadReg
= hi
.device
;
1752 prev_regs
.bCommandReg
= hi
.command
;
1753 prev_regs
.bReserved
= 0;
1756 // Set data direction
1759 switch (in
.direction
) {
1760 case ata_cmd_in::no_data
:
1762 case ata_cmd_in::data_in
:
1763 datasize
= (int)in
.size
;
1764 data
= (char *)in
.buffer
;
1766 case ata_cmd_in::data_out
:
1767 datasize
= -(int)in
.size
;
1768 data
= (char *)in
.buffer
;
1771 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
1776 // Try all valid ioctls in the order specified in m_options
1777 bool powered_up
= false;
1778 bool out_regs_set
= false;
1779 bool id_is_cached
= false;
1780 const char * options
= m_options
.c_str();
1782 for (int i
= 0; ; i
++) {
1783 char opt
= options
[i
];
1786 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
1787 // Power up reported by GetDevicePowerState() and no ioctl available
1788 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
1789 regs
.bSectorCountReg
= 0xff;
1790 out_regs_set
= true;
1794 return set_err(ENOSYS
);
1796 if (!strchr(valid_options
, opt
))
1797 // Invalid for this command
1801 assert( datasize
== 0 || datasize
== 512
1802 || (datasize
== -512 && strchr("am", opt
))
1803 || (datasize
> 512 && opt
== 'a'));
1808 // call SMART_GET_VERSION once for each drive
1809 if (m_smartver_state
> 1) {
1810 rc
= -1; errno
= ENOSYS
;
1813 if (!m_smartver_state
) {
1814 assert(m_port
== -1);
1815 GETVERSIONINPARAMS_EX vers_ex
;
1816 if (smart_get_version(get_fh(), &vers_ex
) < 0) {
1817 if (!failuretest_permissive
) {
1818 m_smartver_state
= 2;
1819 rc
= -1; errno
= ENOSYS
;
1822 failuretest_permissive
--;
1825 // 3ware RAID if vendor id present
1826 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
1829 m_smartver_state
= 1;
1831 rc
= smart_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1832 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
1833 id_is_cached
= (m_port
< 0); // Not cached by 3ware driver
1836 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
1837 id_is_cached
= (m_port
< 0);
1840 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
1841 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
1843 out_regs_set
= true;
1846 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
1847 out_regs_set
= true;
1850 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
1851 ata_identify_device
* id
= reinterpret_cast<ata_identify_device
*>(data
);
1852 rc
= get_identify_from_device_property(get_fh(), id
);
1853 if (rc
== 0 && m_phydrive
>= 0)
1854 get_serial_from_wmi(m_phydrive
, id
);
1855 id_is_cached
= true;
1857 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
1858 case ATA_SMART_READ_VALUES
:
1859 rc
= storage_predict_failure_ioctl(get_fh(), data
);
1863 case ATA_SMART_ENABLE
:
1866 case ATA_SMART_STATUS
:
1867 rc
= storage_predict_failure_ioctl(get_fh());
1869 // Good SMART status
1870 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
1874 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
1879 errno
= ENOSYS
; rc
= -1;
1882 errno
= ENOSYS
; rc
= -1;
1886 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
1887 out_regs_set
= true;
1890 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
1891 rc
= get_device_power_state(get_fh());
1893 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
1894 // spin up the drive => simulate ATA result STANDBY.
1895 regs
.bSectorCountReg
= 0x00;
1896 out_regs_set
= true;
1899 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
1900 // only if it is selected by the device driver => try a passthrough ioctl to get the
1901 // actual mode, if none available simulate ACTIVE/IDLE.
1903 rc
= -1; errno
= ENOSYS
;
1909 // Working ioctl found
1912 if (errno
!= ENOSYS
)
1913 // Abort on I/O error
1914 return set_err(errno
);
1916 out_regs_set
= false;
1917 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
1920 // Return IDEREGS if set
1922 ata_out_regs
& lo
= out
.out_regs
;
1923 lo
.error
= regs
.bFeaturesReg
;
1924 lo
.sector_count
= regs
.bSectorCountReg
;
1925 lo
.lba_low
= regs
.bSectorNumberReg
;
1926 lo
.lba_mid
= regs
.bCylLowReg
;
1927 lo
.lba_high
= regs
.bCylHighReg
;
1928 lo
.device
= regs
.bDriveHeadReg
;
1929 lo
.status
= regs
.bCommandReg
;
1930 if (in
.in_regs
.is_48bit_cmd()) {
1931 ata_out_regs
& hi
= out
.out_regs
.prev
;
1932 hi
.sector_count
= prev_regs
.bSectorCountReg
;
1933 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
1934 hi
.lba_mid
= prev_regs
.bCylLowReg
;
1935 hi
.lba_high
= prev_regs
.bCylHighReg
;
1939 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
1940 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
1941 // Update ata_identify_is_cached() result according to ioctl used.
1942 m_id_is_cached
= id_is_cached
;
1947 // Return true if OS caches the ATA identify sector
1948 bool win_ata_device::ata_identify_is_cached() const
1950 return m_id_is_cached
;
1954 //////////////////////////////////////////////////////////////////////
1958 : virtual public /*extends*/ smart_device
1961 /// Get bitmask of used ports
1962 unsigned get_ports_used();
1966 : smart_device(never_called
)
1967 { memset(&m_phy_ent
, 0, sizeof(m_phy_ent
)); }
1970 bool get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
);
1972 /// Select physical drive
1973 bool select_port(int port
);
1975 /// Get info for selected physical drive
1976 const CSMI_SAS_PHY_ENTITY
& get_phy_ent() const
1977 { return m_phy_ent
; }
1979 /// Call platform-specific CSMI ioctl
1980 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
1981 unsigned csmi_bufsiz
) = 0;
1984 CSMI_SAS_PHY_ENTITY m_phy_ent
; ///< CSMI info for this phy
1988 /////////////////////////////////////////////////////////////////////////////
1990 bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
)
1992 // Get driver info to check CSMI support
1993 CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf
;
1994 memset(&driver_info_buf
, 0, sizeof(driver_info_buf
));
1995 if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO
, &driver_info_buf
.IoctlHeader
, sizeof(driver_info_buf
)))
1998 if (scsi_debugmode
> 1) {
1999 const CSMI_SAS_DRIVER_INFO
& driver_info
= driver_info_buf
.Information
;
2000 pout("CSMI_SAS_DRIVER_INFO:\n");
2001 pout(" Name: \"%.81s\"\n", driver_info
.szName
);
2002 pout(" Description: \"%.81s\"\n", driver_info
.szDescription
);
2003 pout(" Revision: %d.%d\n", driver_info
.usMajorRevision
, driver_info
.usMinorRevision
);
2007 CSMI_SAS_PHY_INFO_BUFFER phy_info_buf
;
2008 memset(&phy_info_buf
, 0, sizeof(phy_info_buf
));
2009 if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO
, &phy_info_buf
.IoctlHeader
, sizeof(phy_info_buf
)))
2012 phy_info
= phy_info_buf
.Information
;
2014 const int max_number_of_phys
= sizeof(phy_info
.Phy
) / sizeof(phy_info
.Phy
[0]);
2015 if (phy_info
.bNumberOfPhys
> max_number_of_phys
)
2016 return set_err(EIO
, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info
.bNumberOfPhys
);
2018 if (scsi_debugmode
> 1) {
2019 pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info
.bNumberOfPhys
);
2020 for (int i
= 0; i
< max_number_of_phys
; i
++) {
2021 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2022 const CSMI_SAS_IDENTIFY
& id
= pe
.Identify
, & at
= pe
.Attached
;
2023 if (id
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2026 pout("Phy[%d] Port: 0x%02x\n", i
, pe
.bPortIdentifier
);
2027 pout(" Type: 0x%02x, 0x%02x\n", id
.bDeviceType
, at
.bDeviceType
);
2028 pout(" InitProto: 0x%02x, 0x%02x\n", id
.bInitiatorPortProtocol
, at
.bInitiatorPortProtocol
);
2029 pout(" TargetProto: 0x%02x, 0x%02x\n", id
.bTargetPortProtocol
, at
.bTargetPortProtocol
);
2030 pout(" PhyIdent: 0x%02x, 0x%02x\n", id
.bPhyIdentifier
, at
.bPhyIdentifier
);
2031 const unsigned char * b
= id
.bSASAddress
;
2032 pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ",
2033 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2035 pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
2036 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
2043 unsigned csmi_device::get_ports_used()
2045 CSMI_SAS_PHY_INFO phy_info
;
2046 if (!get_phy_info(phy_info
))
2049 unsigned ports_used
= 0;
2050 for (unsigned i
= 0; i
< sizeof(phy_info
.Phy
) / sizeof(phy_info
.Phy
[0]); i
++) {
2051 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2052 if (pe
.Identify
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2054 if (pe
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2056 switch (pe
.Attached
.bTargetPortProtocol
) {
2057 case CSMI_SAS_PROTOCOL_SATA
:
2058 case CSMI_SAS_PROTOCOL_STP
:
2064 if (pe
.bPortIdentifier
== 0xff)
2065 // Older (<= 9.*) Intel RST driver
2066 ports_used
|= (1 << i
);
2068 ports_used
|= (1 << pe
.bPortIdentifier
);
2075 bool csmi_device::select_port(int port
)
2077 CSMI_SAS_PHY_INFO phy_info
;
2078 if (!get_phy_info(phy_info
))
2082 int max_port
= -1, port_index
= -1;
2083 for (unsigned i
= 0; i
< sizeof(phy_info
.Phy
) / sizeof(phy_info
.Phy
[0]); i
++) {
2084 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
2085 if (pe
.Identify
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2088 if (pe
.bPortIdentifier
== 0xff) {
2089 // Older (<= 9.*) Intel RST driver
2090 max_port
= phy_info
.bNumberOfPhys
- 1;
2091 if (i
>= phy_info
.bNumberOfPhys
)
2097 if (pe
.bPortIdentifier
> max_port
)
2098 max_port
= pe
.bPortIdentifier
;
2099 if (pe
.bPortIdentifier
!= port
)
2107 if (port_index
< 0) {
2108 if (port
<= max_port
)
2109 return set_err(ENOENT
, "Port %d is disabled", port
);
2111 return set_err(ENOENT
, "Port %d does not exist (#ports: %d)", port
,
2115 const CSMI_SAS_PHY_ENTITY
& phy_ent
= phy_info
.Phy
[port_index
];
2116 if (phy_ent
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
2117 return set_err(ENOENT
, "No device on port %d", port
);
2119 switch (phy_ent
.Attached
.bTargetPortProtocol
) {
2120 case CSMI_SAS_PROTOCOL_SATA
:
2121 case CSMI_SAS_PROTOCOL_STP
:
2124 return set_err(ENOENT
, "No SATA device on port %d (protocol: %d)",
2125 port
, phy_ent
.Attached
.bTargetPortProtocol
);
2128 m_phy_ent
= phy_ent
;
2133 //////////////////////////////////////////////////////////////////////
2136 class csmi_ata_device
2137 : virtual public /*extends*/ csmi_device
,
2138 virtual public /*implements*/ ata_device
2141 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
2145 : smart_device(never_called
) { }
2149 //////////////////////////////////////////////////////////////////////
2151 bool csmi_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2153 if (!ata_cmd_is_supported(in
,
2154 ata_device::supports_data_out
|
2155 ata_device::supports_output_regs
|
2156 ata_device::supports_multi_sector
|
2157 ata_device::supports_48bit
,
2162 // Create buffer with appropriate size
2163 raw_buffer
pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER
) + in
.size
);
2164 CSMI_SAS_STP_PASSTHRU_BUFFER
* pthru_buf
= (CSMI_SAS_STP_PASSTHRU_BUFFER
*)pthru_raw_buf
.data();
2166 // Set addresses from Phy info
2167 CSMI_SAS_STP_PASSTHRU
& pthru
= pthru_buf
->Parameters
;
2168 const CSMI_SAS_PHY_ENTITY
& phy_ent
= get_phy_ent();
2169 pthru
.bPhyIdentifier
= phy_ent
.Identify
.bPhyIdentifier
;
2170 pthru
.bPortIdentifier
= phy_ent
.bPortIdentifier
;
2171 memcpy(pthru
.bDestinationSASAddress
, phy_ent
.Attached
.bSASAddress
,
2172 sizeof(pthru
.bDestinationSASAddress
));
2173 pthru
.bConnectionRate
= CSMI_SAS_LINK_RATE_NEGOTIATED
;
2175 // Set transfer mode
2176 switch (in
.direction
) {
2177 case ata_cmd_in::no_data
:
2178 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_UNSPECIFIED
;
2180 case ata_cmd_in::data_in
:
2181 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_READ
;
2182 pthru
.uDataLength
= in
.size
;
2184 case ata_cmd_in::data_out
:
2185 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_WRITE
;
2186 pthru
.uDataLength
= in
.size
;
2187 memcpy(pthru_buf
->bDataBuffer
, in
.buffer
, in
.size
);
2190 return set_err(EINVAL
, "csmi_ata_device::ata_pass_through: invalid direction=%d",
2194 // Set host-to-device FIS
2196 unsigned char * fis
= pthru
.bCommandFIS
;
2197 const ata_in_regs
& lo
= in
.in_regs
;
2198 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2199 fis
[ 0] = 0x27; // Type: host-to-device FIS
2200 fis
[ 1] = 0x80; // Bit7: Update command register
2201 fis
[ 2] = lo
.command
;
2202 fis
[ 3] = lo
.features
;
2203 fis
[ 4] = lo
.lba_low
;
2204 fis
[ 5] = lo
.lba_mid
;
2205 fis
[ 6] = lo
.lba_high
;
2206 fis
[ 7] = lo
.device
;
2207 fis
[ 8] = hi
.lba_low
;
2208 fis
[ 9] = hi
.lba_mid
;
2209 fis
[10] = hi
.lba_high
;
2210 fis
[11] = hi
.features
;
2211 fis
[12] = lo
.sector_count
;
2212 fis
[13] = hi
.sector_count
;
2216 if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU
, &pthru_buf
->IoctlHeader
, pthru_raw_buf
.size())) {
2220 // Get device-to-host FIS
2222 const unsigned char * fis
= pthru_buf
->Status
.bStatusFIS
;
2223 ata_out_regs
& lo
= out
.out_regs
;
2224 lo
.status
= fis
[ 2];
2226 lo
.lba_low
= fis
[ 4];
2227 lo
.lba_mid
= fis
[ 5];
2228 lo
.lba_high
= fis
[ 6];
2229 lo
.device
= fis
[ 7];
2230 lo
.sector_count
= fis
[12];
2231 if (in
.in_regs
.is_48bit_cmd()) {
2232 ata_out_regs
& hi
= out
.out_regs
.prev
;
2233 hi
.lba_low
= fis
[ 8];
2234 hi
.lba_mid
= fis
[ 9];
2235 hi
.lba_high
= fis
[10];
2236 hi
.sector_count
= fis
[13];
2241 if (in
.direction
== ata_cmd_in::data_in
)
2242 // TODO: Check ptru_buf->Status.uDataBytes
2243 memcpy(in
.buffer
, pthru_buf
->bDataBuffer
, in
.size
);
2249 //////////////////////////////////////////////////////////////////////
2252 class win_csmi_device
2253 : public /*implements*/ csmi_ata_device
2256 win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2257 const char * req_type
);
2259 virtual ~win_csmi_device() throw();
2261 virtual bool open();
2263 virtual bool close();
2265 virtual bool is_open() const;
2270 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2271 unsigned csmi_bufsiz
);
2274 HANDLE m_fh
; ///< Controller device handle
2275 int m_port
; ///< Port number
2279 //////////////////////////////////////////////////////////////////////
2281 win_csmi_device::win_csmi_device(smart_interface
* intf
, const char * dev_name
,
2282 const char * req_type
)
2283 : smart_device(intf
, dev_name
, "ata", req_type
),
2284 m_fh(INVALID_HANDLE_VALUE
), m_port(-1)
2288 win_csmi_device::~win_csmi_device() throw()
2290 if (m_fh
!= INVALID_HANDLE_VALUE
)
2294 bool win_csmi_device::is_open() const
2296 return (m_fh
!= INVALID_HANDLE_VALUE
);
2299 bool win_csmi_device::close()
2301 if (m_fh
== INVALID_HANDLE_VALUE
)
2303 BOOL rc
= CloseHandle(m_fh
);
2304 m_fh
= INVALID_HANDLE_VALUE
;
2309 bool win_csmi_device::open_scsi()
2312 unsigned contr_no
= ~0, port
= ~0; int nc
= -1;
2313 const char * name
= skipdev(get_dev_name());
2314 if (!( sscanf(name
, "csmi%u,%u%n", &contr_no
, &port
, &nc
) >= 0
2315 && nc
== (int)strlen(name
) && contr_no
<= 9 && port
< 32) )
2316 return set_err(EINVAL
);
2318 // Open controller handle
2320 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%u:", contr_no
);
2322 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2323 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2324 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
2326 if (h
== INVALID_HANDLE_VALUE
) {
2327 long err
= GetLastError();
2328 if (err
== ERROR_FILE_NOT_FOUND
)
2329 set_err(ENOENT
, "%s: not found", devpath
);
2330 else if (err
== ERROR_ACCESS_DENIED
)
2331 set_err(EACCES
, "%s: access denied", devpath
);
2333 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2337 if (scsi_debugmode
> 1)
2338 pout(" %s: successfully opened\n", devpath
);
2346 bool win_csmi_device::open()
2351 // Get Phy info for this drive
2352 if (!select_port(m_port
)) {
2361 bool win_csmi_device::csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
2362 unsigned csmi_bufsiz
)
2364 // Determine signature
2367 case CC_CSMI_SAS_GET_DRIVER_INFO
:
2368 sig
= CSMI_ALL_SIGNATURE
; break;
2369 case CC_CSMI_SAS_GET_PHY_INFO
:
2370 case CC_CSMI_SAS_STP_PASSTHRU
:
2371 sig
= CSMI_SAS_SIGNATURE
; break;
2373 return set_err(ENOSYS
, "Unknown CSMI code=%u", code
);
2377 csmi_buffer
->HeaderLength
= sizeof(IOCTL_HEADER
);
2378 strncpy((char *)csmi_buffer
->Signature
, sig
, sizeof(csmi_buffer
->Signature
));
2379 csmi_buffer
->Timeout
= CSMI_SAS_TIMEOUT
;
2380 csmi_buffer
->ControlCode
= code
;
2381 csmi_buffer
->ReturnCode
= 0;
2382 csmi_buffer
->Length
= csmi_bufsiz
- sizeof(IOCTL_HEADER
);
2386 if (!DeviceIoControl(m_fh
, IOCTL_SCSI_MINIPORT
,
2387 csmi_buffer
, csmi_bufsiz
, csmi_buffer
, csmi_bufsiz
, &num_out
, (OVERLAPPED
*)0)) {
2388 long err
= GetLastError();
2390 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code
, err
);
2391 if ( err
== ERROR_INVALID_FUNCTION
2392 || err
== ERROR_NOT_SUPPORTED
2393 || err
== ERROR_DEV_NOT_EXIST
)
2394 return set_err(ENOSYS
, "CSMI is not supported (Error=%ld)", err
);
2396 return set_err(EIO
, "CSMI(%u) failed with Error=%ld", code
, err
);
2400 if (csmi_buffer
->ReturnCode
) {
2401 if (scsi_debugmode
) {
2402 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%u\n",
2403 code
, (unsigned)csmi_buffer
->ReturnCode
);
2405 return set_err(EIO
, "CSMI(%u) failed with ReturnCode=%u", code
, (unsigned)csmi_buffer
->ReturnCode
);
2408 if (scsi_debugmode
> 1)
2409 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %u\n", code
, (unsigned)num_out
);
2415 //////////////////////////////////////////////////////////////////////
2416 // win_tw_cli_device
2418 // Routines for pseudo device /dev/tw_cli/*
2419 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
2420 // TODO: This is OS independent
2422 class win_tw_cli_device
2423 : public /*implements*/ ata_device_with_command_set
2426 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2428 virtual bool is_open() const;
2430 virtual bool open();
2432 virtual bool close();
2435 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
2438 bool m_ident_valid
, m_smart_valid
;
2439 ata_identify_device m_ident_buf
;
2440 ata_smart_values m_smart_buf
;
2444 /////////////////////////////////////////////////////////////////////////////
2446 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2447 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
2448 m_ident_valid(false), m_smart_valid(false)
2450 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
2451 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
2455 bool win_tw_cli_device::is_open() const
2457 return (m_ident_valid
|| m_smart_valid
);
2461 // Get clipboard data
2463 static int get_clipboard(char * data
, int datasize
)
2465 if (!OpenClipboard(NULL
))
2467 HANDLE h
= GetClipboardData(CF_TEXT
);
2472 const void * p
= GlobalLock(h
);
2473 int n
= GlobalSize(h
);
2483 // Run a command, write stdout to dataout
2484 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
2486 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
2488 // Create stdout pipe
2489 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
2490 HANDLE pipe_out_w
, h
;
2491 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
2493 HANDLE self
= GetCurrentProcess();
2495 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
2496 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
2497 CloseHandle(pipe_out_w
);
2501 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
2502 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
2503 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
2508 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
2509 si
.hStdInput
= INVALID_HANDLE_VALUE
;
2510 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
2511 si
.dwFlags
= STARTF_USESTDHANDLES
;
2512 PROCESS_INFORMATION pi
;
2514 NULL
, const_cast<char *>(cmd
),
2515 NULL
, NULL
, TRUE
/*inherit*/,
2516 CREATE_NO_WINDOW
/*do not create a new console window*/,
2517 NULL
, NULL
, &si
, &pi
)) {
2518 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
2521 CloseHandle(pi
.hThread
);
2522 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
2524 // Copy stdout to output buffer
2526 while (i
< outsize
) {
2528 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
2532 CloseHandle(pipe_out_r
);
2534 WaitForSingleObject(pi
.hProcess
, INFINITE
);
2535 CloseHandle(pi
.hProcess
);
2540 static const char * findstr(const char * str
, const char * sub
)
2542 const char * s
= strstr(str
, sub
);
2543 return (s
? s
+strlen(sub
) : "");
2547 bool win_tw_cli_device::open()
2549 m_ident_valid
= m_smart_valid
= false;
2550 const char * name
= skipdev(get_dev_name());
2551 // Read tw_cli or 3DM browser output into buffer
2553 int size
= -1, n1
= -1, n2
= -1;
2554 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
2555 size
= get_clipboard(buffer
, sizeof(buffer
));
2557 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
2558 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
2560 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
2561 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
2563 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
2564 if (ata_debugmode
> 1)
2565 pout("%s: Run: \"%s\"\n", name
, cmd
);
2566 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
2569 return set_err(EINVAL
);
2572 if (ata_debugmode
> 1)
2573 pout("%s: Read %d bytes\n", name
, size
);
2575 return set_err(ENOENT
);
2576 if (size
>= (int)sizeof(buffer
))
2577 return set_err(EIO
);
2580 if (ata_debugmode
> 1)
2581 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
2583 // Fake identify sector
2584 ASSERT_SIZEOF(ata_identify_device
, 512);
2585 ata_identify_device
* id
= &m_ident_buf
;
2586 memset(id
, 0, sizeof(*id
));
2587 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
2588 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
2589 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
2590 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
2591 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
2593 id
->words047_079
[49-47] = 0x0200; // size valid
2594 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
2595 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
2597 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2598 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2600 // Parse smart data hex dump
2601 const char * s
= findstr(buffer
, "Drive Smart Data:");
2603 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
2605 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
2607 const char * s1
= findstr(s
, "<td class"); // html version
2610 s
+= strcspn(s
, "\r\n");
2613 s
= buffer
; // try raw hex dump without header
2615 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
2618 unsigned x
= ~0; int n
= -1;
2619 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
2621 sd
[i
] = (unsigned char)x
;
2622 if (!(++i
< 512 && n
> 0))
2625 if (*s
== '<') // "<br>"
2626 s
+= strcspn(s
, "\r\n");
2629 if (!id
->model
[1]) {
2630 // No useful data found
2631 char * err
= strstr(buffer
, "Error:");
2633 err
= strstr(buffer
, "error :");
2634 if (err
&& (err
= strchr(err
, ':'))) {
2635 // Show tw_cli error message
2637 err
[strcspn(err
, "\r\n")] = 0;
2638 return set_err(EIO
, "%s", err
);
2640 return set_err(EIO
);
2645 m_ident_valid
= true;
2646 m_smart_valid
= !!sd
;
2651 bool win_tw_cli_device::close()
2653 m_ident_valid
= m_smart_valid
= false;
2658 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
2664 memcpy(data
, &m_ident_buf
, 512);
2669 memcpy(data
, &m_smart_buf
, 512);
2673 case STATUS_CHECK
: // Fake "good" SMART status
2678 // Arrive here for all unsupported commands
2684 /////////////////////////////////////////////////////////////////////////////
2686 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
2688 class win_scsi_device
2689 : public /*implements*/ scsi_device
,
2690 virtual public /*extends*/ win_smart_device
2693 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
2695 virtual bool open();
2697 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
2700 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
2704 /////////////////////////////////////////////////////////////////////////////
2706 win_scsi_device::win_scsi_device(smart_interface
* intf
,
2707 const char * dev_name
, const char * req_type
)
2708 : smart_device(intf
, dev_name
, "scsi", req_type
)
2712 bool win_scsi_device::open()
2714 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2715 // sd[a-z]([a-z])?,N => Physical drive 0-701, RAID port N
2716 char drive
[2+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
2717 if ( sscanf(name
, "sd%2[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
2718 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
2719 return open(sdxy_to_phydrive(drive
), -1, -1, sub_addr
);
2721 // pd<m>,N => Physical drive <m>, RAID port N
2722 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
2723 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
2724 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
2725 return open(pd_num
, -1, -1, sub_addr
);
2727 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2728 int logdrive
= drive_letter(name
);
2729 if (logdrive
>= 0) {
2730 return open(-1, logdrive
, -1, -1);
2732 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
2733 int tape_num
= -1; n1
= -1;
2734 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2735 return open(-1, -1, tape_num
, -1);
2737 tape_num
= -1; n1
= -1;
2738 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2739 return open(-1, -1, tape_num
, -1);
2741 // tape<m> => tape drive <m>
2742 tape_num
= -1; n1
= -1;
2743 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
2744 return open(-1, -1, tape_num
, -1);
2747 return set_err(EINVAL
);
2750 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
2753 b
[sizeof(b
) - 1] = '\0';
2755 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
2756 else if (ld_num
>= 0)
2757 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
2758 else if (tape_num
>= 0)
2759 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
2766 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
2767 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
2768 OPEN_EXISTING
, 0, 0);
2769 if (h
== INVALID_HANDLE_VALUE
) {
2770 set_err(ENODEV
, "%s: Open failed, Error=%u", b
, (unsigned)GetLastError());
2779 SCSI_PASS_THROUGH_DIRECT spt
;
2781 UCHAR ucSenseBuf
[64];
2782 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
2785 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
2786 // Used if DataTransferLength not supported by *_DIRECT.
2787 static long scsi_pass_through_indirect(HANDLE h
,
2788 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
2790 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
2791 SCSI_PASS_THROUGH spt
;
2793 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
2794 UCHAR ucDataBuf
[512];
2797 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
2798 memset(&sb
, 0, sizeof(sb
));
2800 // DATA_OUT not implemented yet
2801 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
2802 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
2803 return ERROR_INVALID_PARAMETER
;
2805 sb
.spt
.Length
= sizeof(sb
.spt
);
2806 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
2807 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
2808 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2809 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
2810 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
2811 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
2812 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
2813 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
2816 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
2817 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2818 return GetLastError();
2820 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
2821 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
2822 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
2824 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
2825 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
2826 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
2831 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
2832 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
2834 int report
= scsi_debugmode
; // TODO
2838 const unsigned char * ucp
= iop
->cmnd
;
2841 const int sz
= (int)sizeof(buff
);
2843 np
= scsi_get_opcode_name(ucp
[0]);
2844 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
2845 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
2846 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
2848 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
2849 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2851 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
2852 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
2853 (trunc
? " [only first 256 bytes shown]" : ""));
2854 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2857 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
2861 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
2862 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
2863 set_err(EINVAL
, "cmnd_len too large");
2867 memset(&sb
, 0, sizeof(sb
));
2868 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
2869 sb
.spt
.CdbLength
= iop
->cmnd_len
;
2870 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
2871 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
2872 sb
.spt
.SenseInfoOffset
=
2873 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
2874 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
2877 switch (iop
->dxfer_dir
) {
2879 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
2881 case DXFER_FROM_DEVICE
:
2882 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
2883 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2884 sb
.spt
.DataBuffer
= iop
->dxferp
;
2885 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
2886 // transfers (needed for SMART STATUS check of JMicron USB bridges)
2887 if (sb
.spt
.DataTransferLength
== 1)
2890 case DXFER_TO_DEVICE
:
2891 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
2892 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
2893 sb
.spt
.DataBuffer
= iop
->dxferp
;
2896 set_err(EINVAL
, "bad dxfer_dir");
2903 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
2904 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
2905 err
= GetLastError();
2908 err
= scsi_pass_through_indirect(get_fh(), &sb
);
2911 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
2912 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
2913 (direct
? "_DIRECT" : ""), err
);
2915 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
2916 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
2917 int slen
= sb
.ucSenseBuf
[7] + 8;
2919 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
2920 slen
= sizeof(sb
.ucSenseBuf
);
2921 if (slen
> (int)iop
->max_sense_len
)
2922 slen
= iop
->max_sense_len
;
2923 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
2924 iop
->resp_sense_len
= slen
;
2927 pout(" >>> Sense buffer, len=%d:\n", slen
);
2928 dStrHex(iop
->sensep
, slen
, 1);
2930 if ((iop
->sensep
[0] & 0x7f) > 0x71)
2931 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
2932 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
2933 iop
->sensep
[2], iop
->sensep
[3]);
2935 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
2936 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
2937 iop
->sensep
[12], iop
->sensep
[13]);
2940 iop
->resp_sense_len
= 0;
2942 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
2943 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
2947 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
2948 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2949 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
2950 (trunc
? " [only first 256 bytes shown]" : ""));
2951 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2957 /////////////////////////////////////////////////////////////////////////////
2958 /// Areca RAID support
2960 // TODO: combine with above scsi_pass_through_direct()
2961 static long scsi_pass_through_direct(HANDLE fd
, UCHAR targetid
, struct scsi_cmnd_io
* iop
)
2963 int report
= scsi_debugmode
; // TODO
2967 const unsigned char * ucp
= iop
->cmnd
;
2970 const int sz
= (int)sizeof(buff
);
2972 np
= scsi_get_opcode_name(ucp
[0]);
2973 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
2974 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
2975 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
2977 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
2978 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
2980 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
2981 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
2982 (trunc
? " [only first 256 bytes shown]" : ""));
2983 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
2986 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
2990 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
2991 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
2995 memset(&sb
, 0, sizeof(sb
));
2996 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
2997 //sb.spt.PathId = 0;
2998 sb
.spt
.TargetId
= targetid
;
3000 sb
.spt
.CdbLength
= iop
->cmnd_len
;
3001 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
3002 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3003 sb
.spt
.SenseInfoOffset
=
3004 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
3005 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
3008 switch (iop
->dxfer_dir
) {
3010 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
3012 case DXFER_FROM_DEVICE
:
3013 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
3014 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3015 sb
.spt
.DataBuffer
= iop
->dxferp
;
3016 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
3017 // transfers (needed for SMART STATUS check of JMicron USB bridges)
3018 if (sb
.spt
.DataTransferLength
== 1)
3021 case DXFER_TO_DEVICE
:
3022 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
3023 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3024 sb
.spt
.DataBuffer
= iop
->dxferp
;
3033 if (!DeviceIoControl(fd
, IOCTL_SCSI_PASS_THROUGH_DIRECT
,
3034 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3035 err
= GetLastError();
3038 err
= scsi_pass_through_indirect(fd
, &sb
);
3045 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
3046 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3047 int slen
= sb
.ucSenseBuf
[7] + 8;
3049 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
3050 slen
= sizeof(sb
.ucSenseBuf
);
3051 if (slen
> (int)iop
->max_sense_len
)
3052 slen
= iop
->max_sense_len
;
3053 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3054 iop
->resp_sense_len
= slen
;
3057 pout(" >>> Sense buffer, len=%d:\n", slen
);
3058 dStrHex(iop
->sensep
, slen
, 1);
3060 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3061 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3062 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3063 iop
->sensep
[2], iop
->sensep
[3]);
3065 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3066 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3067 iop
->sensep
[12], iop
->sensep
[13]);
3070 iop
->resp_sense_len
= 0;
3072 if (iop
->dxfer_len
> sb
.spt
.DataTransferLength
)
3073 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3077 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3078 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3079 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3080 (trunc
? " [only first 256 bytes shown]" : ""));
3081 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3088 /////////////////////////////////////////////////////////////////////////////
3089 // win_areca_scsi_device
3090 // SAS(SCSI) device behind Areca RAID Controller
3092 class win_areca_scsi_device
3093 : public /*implements*/ areca_scsi_device
,
3094 public /*extends*/ win_smart_device
3097 win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3098 virtual bool open();
3099 virtual smart_device
* autodetect_open();
3100 virtual bool arcmsr_lock();
3101 virtual bool arcmsr_unlock();
3102 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3109 /////////////////////////////////////////////////////////////////////////////
3111 win_areca_scsi_device::win_areca_scsi_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3112 : smart_device(intf
, dev_name
, "areca", "areca")
3114 set_fh(INVALID_HANDLE_VALUE
);
3115 set_disknum(disknum
);
3117 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3120 bool win_areca_scsi_device::open()
3128 hFh
= CreateFile( get_dev_name(),
3129 GENERIC_READ
|GENERIC_WRITE
,
3130 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3135 if(hFh
== INVALID_HANDLE_VALUE
)
3144 smart_device
* win_areca_scsi_device::autodetect_open()
3149 int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3151 int ioctlreturn
= 0;
3153 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3154 if ( ioctlreturn
|| iop
->scsi_status
)
3156 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3157 if ( ioctlreturn
|| iop
->scsi_status
)
3167 bool win_areca_scsi_device::arcmsr_lock()
3169 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3173 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3174 return set_err(EINVAL
, "unable to parse device name");
3176 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3177 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3178 if ( m_mutex
== NULL
)
3180 return set_err(EIO
, "CreateMutex failed");
3183 // atomic access to driver
3184 WaitForSingleObject(m_mutex
, INFINITE
);
3190 bool win_areca_scsi_device::arcmsr_unlock()
3192 if( m_mutex
!= NULL
)
3194 ReleaseMutex(m_mutex
);
3195 CloseHandle(m_mutex
);
3202 /////////////////////////////////////////////////////////////////////////////
3203 // win_areca_ata_device
3204 // SATA(ATA) device behind Areca RAID Controller
3206 class win_areca_ata_device
3207 : public /*implements*/ areca_ata_device
,
3208 public /*extends*/ win_smart_device
3211 win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
= 1);
3212 virtual bool open();
3213 virtual smart_device
* autodetect_open();
3214 virtual bool arcmsr_lock();
3215 virtual bool arcmsr_unlock();
3216 virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
);
3223 /////////////////////////////////////////////////////////////////////////////
3225 win_areca_ata_device::win_areca_ata_device(smart_interface
* intf
, const char * dev_name
, int disknum
, int encnum
)
3226 : smart_device(intf
, dev_name
, "areca", "areca")
3228 set_fh(INVALID_HANDLE_VALUE
);
3229 set_disknum(disknum
);
3231 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
3234 bool win_areca_ata_device::open()
3242 hFh
= CreateFile( get_dev_name(),
3243 GENERIC_READ
|GENERIC_WRITE
,
3244 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3249 if(hFh
== INVALID_HANDLE_VALUE
)
3258 smart_device
* win_areca_ata_device::autodetect_open()
3260 // autodetect device type
3261 int is_ata
= arcmsr_get_dev_type();
3275 smart_device_auto_ptr
newdev(new win_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
3278 newdev
->open(); // TODO: Can possibly pass open fd
3280 return newdev
.release();
3283 int win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io
* iop
)
3285 int ioctlreturn
= 0;
3287 ioctlreturn
= scsi_pass_through_direct(get_fh(), 16, iop
);
3288 if ( ioctlreturn
|| iop
->scsi_status
)
3290 ioctlreturn
= scsi_pass_through_direct(get_fh(), 127, iop
);
3291 if ( ioctlreturn
|| iop
->scsi_status
)
3301 bool win_areca_ata_device::arcmsr_lock()
3303 #define SYNCOBJNAME "Global\\SynIoctlMutex"
3307 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
3308 return set_err(EINVAL
, "unable to parse device name");
3310 snprintf(mutexstr
, sizeof(mutexstr
), "%s%d", SYNCOBJNAME
, ctlrnum
);
3311 m_mutex
= CreateMutex(NULL
, FALSE
, mutexstr
);
3312 if ( m_mutex
== NULL
)
3314 return set_err(EIO
, "CreateMutex failed");
3317 // atomic access to driver
3318 WaitForSingleObject(m_mutex
, INFINITE
);
3324 bool win_areca_ata_device::arcmsr_unlock()
3326 if( m_mutex
!= NULL
)
3328 ReleaseMutex(m_mutex
);
3329 CloseHandle(m_mutex
);
3336 /////////////////////////////////////////////////////////////////////////////
3337 // win_aacraid_device
3338 // PMC aacraid Support
3340 class win_aacraid_device
3341 :public /*implements*/ scsi_device
,
3342 public /*extends*/ win_smart_device
3345 win_aacraid_device(smart_interface
*intf
, const char *dev_name
,unsigned int ctrnum
, unsigned int target
, unsigned int lun
);
3347 virtual ~win_aacraid_device() throw();
3349 virtual bool open();
3351 virtual bool scsi_pass_through(struct scsi_cmnd_io
*iop
);
3354 //Device Host number
3357 //Channel(Lun) of the device
3365 /////////////////////////////////////////////////////////////////////////////
3367 win_aacraid_device::win_aacraid_device(smart_interface
* intf
,
3368 const char *dev_name
, unsigned ctrnum
, unsigned target
, unsigned lun
)
3369 : smart_device(intf
, dev_name
, "aacraid", "aacraid"),
3370 m_ctrnum(ctrnum
), m_lun(lun
), m_target(target
)
3372 set_info().info_name
= strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name
, m_ctrnum
, m_lun
, m_target
);
3373 set_info().dev_type
= strprintf("aacraid,%d,%d,%d", m_ctrnum
, m_lun
, m_target
);
3376 win_aacraid_device::~win_aacraid_device() throw()
3380 bool win_aacraid_device::open()
3385 HANDLE hFh
= CreateFile( get_dev_name(),
3386 GENERIC_READ
|GENERIC_WRITE
,
3387 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3392 if (hFh
== INVALID_HANDLE_VALUE
)
3393 return set_err(ENODEV
, "Open failed, Error=%u", (unsigned)GetLastError());
3399 bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io
*iop
)
3401 int report
= scsi_debugmode
;
3405 const unsigned char * ucp
= iop
->cmnd
;
3408 const int sz
= (int)sizeof(buff
);
3409 np
= scsi_get_opcode_name(ucp
[0]);
3410 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3411 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3412 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3414 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3415 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3417 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3418 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3419 (trunc
? " [only first 256 bytes shown]" : ""));
3420 dStrHex((const char *)iop
->dxferp
,
3421 (trunc
? 256 : (int)iop
->dxfer_len
) , 1);
3424 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3425 pout("buff %s\n",buff
);
3428 char ioBuffer
[1000];
3429 SRB_IO_CONTROL
* pSrbIO
= (SRB_IO_CONTROL
*) ioBuffer
;
3430 SCSI_REQUEST_BLOCK
* pScsiIO
= (SCSI_REQUEST_BLOCK
*) (ioBuffer
+ sizeof(SRB_IO_CONTROL
));
3431 DWORD scsiRequestBlockSize
= sizeof(SCSI_REQUEST_BLOCK
);
3432 char *pRequestSenseIO
= (char *) (ioBuffer
+ sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
);
3433 DWORD dataOffset
= (sizeof(SRB_IO_CONTROL
) + scsiRequestBlockSize
+ 7) & 0xfffffff8;
3434 char *pDataIO
= (char *) (ioBuffer
+ dataOffset
);
3435 memset(pScsiIO
, 0, scsiRequestBlockSize
);
3436 pScsiIO
->Length
= (USHORT
) scsiRequestBlockSize
;
3437 pScsiIO
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
3438 pScsiIO
->PathId
= 0;
3439 pScsiIO
->TargetId
= m_target
;
3440 pScsiIO
->Lun
= m_lun
;
3441 pScsiIO
->CdbLength
= (int)iop
->cmnd_len
;
3442 switch(iop
->dxfer_dir
){
3444 pScsiIO
->SrbFlags
= SRB_NoDataXfer
;
3446 case DXFER_FROM_DEVICE
:
3447 pScsiIO
->SrbFlags
|= SRB_DataIn
;
3449 case DXFER_TO_DEVICE
:
3450 pScsiIO
->SrbFlags
|= SRB_DataOut
;
3453 pout("aacraid: bad dxfer_dir\n");
3454 return set_err(EINVAL
, "aacraid: bad dxfer_dir\n");
3456 pScsiIO
->DataTransferLength
= (ULONG
)iop
->dxfer_len
;
3457 pScsiIO
->TimeOutValue
= iop
->timeout
;
3458 UCHAR
*pCdb
= (UCHAR
*) pScsiIO
->Cdb
;
3459 memcpy(pCdb
, iop
->cmnd
, 16);
3460 if (iop
->max_sense_len
){
3461 memset(pRequestSenseIO
, 0, iop
->max_sense_len
);
3463 if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_OUT
){
3464 memcpy(pDataIO
, iop
->dxferp
, iop
->dxfer_len
);
3466 else if (pScsiIO
->SrbFlags
& SRB_FLAGS_DATA_IN
){
3467 memset(pDataIO
, 0, iop
->dxfer_len
);
3470 DWORD bytesReturned
= 0;
3471 memset(pSrbIO
, 0, sizeof(SRB_IO_CONTROL
));
3472 pSrbIO
->HeaderLength
= sizeof(SRB_IO_CONTROL
);
3473 memcpy(pSrbIO
->Signature
, "AACAPI", 7);
3474 pSrbIO
->ControlCode
= ARCIOCTL_SEND_RAW_SRB
;
3475 pSrbIO
->Length
= (dataOffset
+ iop
->dxfer_len
- sizeof(SRB_IO_CONTROL
) + 7) & 0xfffffff8;
3476 pSrbIO
->Timeout
= 3*60;
3478 if (!DeviceIoControl(
3480 IOCTL_SCSI_MINIPORT
,
3482 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3484 sizeof(SRB_IO_CONTROL
) + pSrbIO
->Length
,
3488 return set_err(EIO
, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
3491 iop
->scsi_status
= pScsiIO
->ScsiStatus
;
3492 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3493 int slen
= sizeof(pRequestSenseIO
) + 8;
3494 if (slen
> (int)sizeof(pRequestSenseIO
))
3495 slen
= sizeof(pRequestSenseIO
);
3496 if (slen
> (int)iop
->max_sense_len
)
3497 slen
= (int)iop
->max_sense_len
;
3498 memcpy(iop
->sensep
, pRequestSenseIO
, slen
);
3499 iop
->resp_sense_len
= slen
;
3502 pout(" >>> Sense buffer, len=%d:\n", slen
);
3503 dStrHex(iop
->sensep
, slen
, 1);
3505 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3506 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3507 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3508 iop
->sensep
[2], iop
->sensep
[3]);
3510 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3511 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3512 iop
->sensep
[12], iop
->sensep
[13]);
3516 iop
->resp_sense_len
= 0;
3519 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
){
3520 memcpy(iop
->dxferp
,pDataIO
, iop
->dxfer_len
);
3522 if((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)){
3523 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3524 pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop
->dxfer_len
, iop
->resid
,
3525 (trunc
? " [only first 256 bytes shown]" : ""));
3526 dStrHex((CHAR
*)pDataIO
, (trunc
? 256 : (int)(iop
->dxfer_len
)) , 1);
3532 /////////////////////////////////////////////////////////////////////////////
3535 class win_nvme_device
3536 : public /*implements*/ nvme_device
,
3537 public /*extends*/ win_smart_device
3540 win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3541 const char * req_type
, unsigned nsid
);
3543 virtual bool open();
3545 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
3547 bool open_scsi(int n
);
3556 /////////////////////////////////////////////////////////////////////////////
3558 win_nvme_device::win_nvme_device(smart_interface
* intf
, const char * dev_name
,
3559 const char * req_type
, unsigned nsid
)
3560 : smart_device(intf
, dev_name
, "nvme", req_type
),
3566 bool win_nvme_device::open_scsi(int n
)
3568 // TODO: Use common open function for all devices using "\\.\ScsiN:"
3570 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%d:", n
);
3572 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
3573 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3574 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
3576 if (h
== INVALID_HANDLE_VALUE
) {
3577 long err
= GetLastError();
3578 if (nvme_debugmode
> 1)
3579 pout(" %s: Open failed, Error=%ld\n", devpath
, err
);
3580 if (err
== ERROR_FILE_NOT_FOUND
)
3581 set_err(ENOENT
, "%s: not found", devpath
);
3582 else if (err
== ERROR_ACCESS_DENIED
)
3583 set_err(EACCES
, "%s: access denied", devpath
);
3585 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3589 if (nvme_debugmode
> 1)
3590 pout(" %s: successfully opened\n", devpath
);
3596 // Check if NVMe pass-through works
3597 bool win_nvme_device::probe()
3599 smartmontools::nvme_id_ctrl id_ctrl
;
3601 in
.set_data_in(smartmontools::nvme_admin_identify
, &id_ctrl
, sizeof(id_ctrl
));
3606 bool ok
= nvme_pass_through(in
, out
);
3607 if (!ok
&& nvme_debugmode
> 1)
3608 pout(" nvme probe failed: %s\n", get_errmsg());
3612 bool win_nvme_device::open()
3614 if (m_scsi_no
< 0) {
3615 // First open -> search of NVMe devices
3616 const char * name
= skipdev(get_dev_name());
3617 char s
[2+1] = ""; int n1
= -1, n2
= -1, len
= strlen(name
);
3618 unsigned no
= ~0, nsid
= 0xffffffff;
3619 sscanf(name
, "nvm%2[es]%u%nn%u%n", s
, &no
, &n1
, &nsid
, &n2
);
3621 if (!( (n1
== len
|| (n2
== len
&& nsid
> 0))
3622 && s
[0] == 'e' && (!s
[1] || s
[1] == 's') ))
3623 return set_err(EINVAL
);
3626 // /dev/nvmeN* -> search for nth NVMe device
3627 unsigned nvme_cnt
= 0;
3628 for (int i
= 0; i
< 32; i
++) {
3629 if (!open_scsi(i
)) {
3630 if (get_errno() == EACCES
)
3634 // Done if pass-through works and correct number
3636 if (nvme_cnt
== no
) {
3646 return set_err(ENOENT
);
3650 // /dev/nvmesN* -> use "\\.\ScsiN:"
3660 // Reopen same "\\.\ScsiN:"
3661 if (!open_scsi(m_scsi_no
))
3668 bool win_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
3670 // Create buffer with appropriate size
3671 raw_buffer
pthru_raw_buf(offsetof(NVME_PASS_THROUGH_IOCTL
, DataBuffer
) + in
.size
);
3672 NVME_PASS_THROUGH_IOCTL
* pthru
=
3673 reinterpret_cast<NVME_PASS_THROUGH_IOCTL
*>(pthru_raw_buf
.data());
3676 pthru
->SrbIoCtrl
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
3677 memcpy(pthru
->SrbIoCtrl
.Signature
, NVME_SIG_STR
, sizeof(NVME_SIG_STR
));
3678 pthru
->SrbIoCtrl
.Timeout
= 60;
3679 pthru
->SrbIoCtrl
.ControlCode
= NVME_PASS_THROUGH_SRB_IO_CODE
;
3680 pthru
->SrbIoCtrl
.ReturnCode
= 0;
3681 pthru
->SrbIoCtrl
.Length
= pthru_raw_buf
.size() - sizeof(SRB_IO_CONTROL
);
3683 pthru
->NVMeCmd
[0] = in
.opcode
;
3684 pthru
->NVMeCmd
[1] = in
.nsid
;
3685 pthru
->NVMeCmd
[10] = in
.cdw10
;
3686 pthru
->NVMeCmd
[11] = in
.cdw11
;
3687 pthru
->NVMeCmd
[12] = in
.cdw12
;
3688 pthru
->NVMeCmd
[13] = in
.cdw13
;
3689 pthru
->NVMeCmd
[14] = in
.cdw14
;
3690 pthru
->NVMeCmd
[15] = in
.cdw15
;
3692 pthru
->Direction
= in
.direction();
3693 // pthru->QueueId = 0; // AdminQ
3694 // pthru->DataBufferLen = 0;
3695 if (in
.direction() & nvme_cmd_in::data_out
) {
3696 pthru
->DataBufferLen
= in
.size
;
3697 memcpy(pthru
->DataBuffer
, in
.buffer
, in
.size
);
3699 // pthru->MetaDataLen = 0;
3700 pthru
->ReturnBufferLen
= pthru_raw_buf
.size();
3702 // Call NVME_PASS_THROUGH
3704 BOOL ok
= DeviceIoControl(get_fh(), IOCTL_SCSI_MINIPORT
,
3705 pthru
, pthru_raw_buf
.size(), pthru
, pthru_raw_buf
.size(),
3706 &num_out
, (OVERLAPPED
*)0);
3709 unsigned status
= pthru
->CplEntry
[3] >> 17;
3711 return set_nvme_err(out
, status
);
3714 return set_err(EIO
, "NVME_PASS_THROUGH failed, Error=%u", (unsigned)GetLastError());
3716 if (in
.direction() & nvme_cmd_in::data_in
)
3717 memcpy(in
.buffer
, pthru
->DataBuffer
, in
.size
);
3719 out
.result
= pthru
->CplEntry
[0];
3724 /////////////////////////////////////////////////////////////////////////////
3725 // win_smart_interface
3726 // Platform specific interface
3728 class win_smart_interface
3729 : public /*implements*/ smart_interface
3732 virtual std::string
get_os_version_str();
3734 virtual std::string
get_app_examples(const char * appname
);
3737 virtual int64_t get_timer_usec();
3740 virtual bool disable_system_auto_standby(bool disable
);
3742 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
3743 const char * pattern
= 0);
3746 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
3748 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
3750 virtual nvme_device
* get_nvme_device(const char * name
, const char * type
, unsigned nsid
);
3752 virtual smart_device
* autodetect_smart_device(const char * name
);
3754 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
3756 virtual std::string
get_valid_custom_dev_types_str();
3759 ata_device
* get_usb_device(const char * name
, int phydrive
, int logdrive
= -1);
3763 /////////////////////////////////////////////////////////////////////////////
3766 // Running on 64-bit Windows as 32-bit app ?
3767 static bool is_wow64()
3769 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
3770 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))
3771 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
3772 if (!IsWow64Process_p
)
3775 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
3781 // Return info string about build host and OS version
3782 std::string
win_smart_interface::get_os_version_str()
3784 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
3785 = SMARTMONTOOLS_BUILD_HOST
;
3788 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
3789 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
3790 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
3792 // Starting with Windows 8.1, GetVersionEx() does no longer report the
3793 // actual OS version, see:
3794 // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
3796 // RtlGetVersion() is not affected
3797 LONG
/*NTSTATUS*/ (WINAPI
/*NTAPI*/ * RtlGetVersion_p
)(LPOSVERSIONINFOEXW
) =
3798 (LONG (WINAPI
*)(LPOSVERSIONINFOEXW
))
3799 GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
3801 OSVERSIONINFOEXW vi
; memset(&vi
, 0, sizeof(vi
));
3802 vi
.dwOSVersionInfoSize
= sizeof(vi
);
3803 if (!RtlGetVersion_p
|| RtlGetVersion_p(&vi
)) {
3804 if (!GetVersionExW((OSVERSIONINFOW
*)&vi
))
3809 if ( vi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
3810 && vi
.dwMajorVersion
<= 0xf && vi
.dwMinorVersion
<= 0xf) {
3811 bool ws
= (vi
.wProductType
<= VER_NT_WORKSTATION
);
3812 switch (vi
.dwMajorVersion
<< 4 | vi
.dwMinorVersion
) {
3813 case 0x50: w
= "2000"; break;
3814 case 0x51: w
= "xp"; break;
3815 case 0x52: w
= (!GetSystemMetrics(89/*SM_SERVERR2*/)
3816 ? "2003" : "2003r2"); break;
3817 case 0x60: w
= (ws
? "vista" : "2008" ); break;
3818 case 0x61: w
= (ws
? "win7" : "2008r2"); break;
3819 case 0x62: w
= (ws
? "win8" : "2012" ); break;
3820 case 0x63: w
= (ws
? "win8.1": "2012r2"); break;
3821 case 0x64: w
= (ws
? "w10tp" : "w10tps"); break; // 6.4 = Win 10 Technical Preview
3822 case 0xa0: w
= (ws
? "win10" : "2016" ); break; // 10.0 = Win 10 : Win Server 2016 (TP only?)
3826 const char * w64
= "";
3833 snprintf(vptr
, vlen
, "-%s%u.%u%s",
3834 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "??"),
3835 (unsigned)vi
.dwMajorVersion
, (unsigned)vi
.dwMinorVersion
, w64
);
3836 else if (vi
.wServicePackMinor
)
3837 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
3838 else if (vi
.wServicePackMajor
)
3839 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
3841 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
3846 // MSVCRT only provides ftime() which uses GetSystemTime()
3847 // This provides only ~15ms resolution by default.
3848 // Use QueryPerformanceCounter instead (~300ns).
3849 // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
3850 int64_t win_smart_interface::get_timer_usec()
3852 static int64_t freq
= 0;
3856 freq
= (QueryPerformanceFrequency(&t
) ? t
.QuadPart
: -1);
3858 return smart_interface::get_timer_usec();
3860 if (!QueryPerformanceCounter(&t
))
3862 if (!(0 <= t
.QuadPart
&& t
.QuadPart
<= (int64_t)(~(uint64_t)0 >> 1)/1000000))
3865 return (t
.QuadPart
* 1000000LL) / freq
;
3867 #endif // __CYGWIN__
3870 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
3872 const char * testname
= skipdev(name
);
3873 if (!strncmp(testname
, "csmi", 4))
3874 return new win_csmi_device(this, name
, type
);
3875 if (!strncmp(testname
, "tw_cli", 6))
3876 return new win_tw_cli_device(this, name
, type
);
3877 return new win_ata_device(this, name
, type
);
3880 scsi_device
* win_smart_interface::get_scsi_device(const char * name
, const char * type
)
3882 return new win_scsi_device(this, name
, type
);
3885 nvme_device
* win_smart_interface::get_nvme_device(const char * name
, const char * type
,
3888 return new win_nvme_device(this, name
, type
, nsid
);
3892 smart_device
* win_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
3895 int disknum
= -1, n1
= -1, n2
= -1;
3899 if (sscanf(type
, "areca,%n%d/%d%n", &n1
, &disknum
, &encnum
, &n2
) >= 1 || n1
== 6) {
3900 if (!(1 <= disknum
&& disknum
<= 128)) {
3901 set_err(EINVAL
, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum
);
3904 if (!(1 <= encnum
&& encnum
<= 8)) {
3905 set_err(EINVAL
, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum
);
3909 name
= skipdev(name
);
3910 #define ARECA_MAX_CTLR_NUM 16
3913 if (sscanf(name
, "arcmsr%d%n", &ctlrindex
, &n1
) >= 1 && n1
== (int)strlen(name
)) {
3915 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
3916 2. map arcmsrX into "\\\\.\\scsiX"
3918 for (int idx
= 0; idx
< ARECA_MAX_CTLR_NUM
; idx
++) {
3919 memset(devpath
, 0, sizeof(devpath
));
3920 snprintf(devpath
, sizeof(devpath
), "\\\\.\\scsi%d:", idx
);
3921 win_areca_ata_device
*arcdev
= new win_areca_ata_device(this, devpath
, disknum
, encnum
);
3922 if(arcdev
->arcmsr_probe()) {
3923 if(ctlrindex
-- == 0) {
3929 set_err(ENOENT
, "No Areca controller found");
3932 set_err(EINVAL
, "Option -d areca,N/E requires device name /dev/arcmsrX");
3937 unsigned ctrnum
, lun
, target
;
3940 if ( sscanf(type
, "aacraid,%u,%u,%u%n", &ctrnum
, &lun
, &target
, &n1
) >= 3
3941 && n1
== (int)strlen(type
)) {
3942 #define aacraid_MAX_CTLR_NUM 16
3943 if (ctrnum
>= aacraid_MAX_CTLR_NUM
) {
3944 set_err(EINVAL
, "aacraid: invalid host number %u", ctrnum
);
3949 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[AACRAID_MAX_CTLR_NUM]:" and
3950 2. map ARCX into "\\\\.\\scsiX"
3952 memset(devpath
, 0, sizeof(devpath
));
3953 unsigned ctlrindex
= 0;
3954 for (int portNum
= 0; portNum
< aacraid_MAX_CTLR_NUM
; portNum
++){
3956 snprintf(subKey
, sizeof(subKey
), "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d", portNum
);
3958 long regStatus
= RegOpenKeyExA(HKEY_LOCAL_MACHINE
, subKey
, 0, KEY_READ
, &hScsiKey
);
3959 if (regStatus
== ERROR_SUCCESS
){
3960 char driverName
[20];
3961 DWORD driverNameSize
= sizeof(driverName
);
3963 regStatus
= RegQueryValueExA(hScsiKey
, "Driver", NULL
, ®Type
, (LPBYTE
) driverName
, &driverNameSize
);
3964 if (regStatus
== ERROR_SUCCESS
){
3965 if (regType
== REG_SZ
){
3966 if (stricmp(driverName
, "arcsas") == 0){
3967 if(ctrnum
== ctlrindex
){
3968 snprintf(devpath
, sizeof(devpath
), "\\\\.\\Scsi%d:", portNum
);
3969 return get_sat_device("sat,auto",
3970 new win_aacraid_device(this, devpath
, ctrnum
, target
, lun
));
3976 RegCloseKey(hScsiKey
);
3980 set_err(EINVAL
, "aacraid: host %u not found", ctrnum
);
3987 std::string
win_smart_interface::get_valid_custom_dev_types_str()
3989 return "aacraid,H,L,ID, areca,N[/E]";
3993 // Return value for device detection functions
3994 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_SAT
, DEV_USB
};
3996 // Return true if ATA drive behind a SAT layer
3997 static bool is_sat(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
3999 if (!data
->desc
.VendorIdOffset
)
4001 if (strcmp(data
->raw
+ data
->desc
.VendorIdOffset
, "ATA "))
4006 // Return true if Intel ICHxR RAID volume
4007 static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
4009 if (!(data
->desc
.VendorIdOffset
&& data
->desc
.ProductIdOffset
))
4011 const char * vendor
= data
->raw
+ data
->desc
.VendorIdOffset
;
4012 if (!(!strnicmp(vendor
, "Intel", 5) && strspn(vendor
+5, " ") == strlen(vendor
+5)))
4014 if (strnicmp(data
->raw
+ data
->desc
.ProductIdOffset
, "Raid ", 5))
4019 // get DEV_* for open handle
4020 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4022 // Get BusType from device descriptor
4023 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
4024 if (storage_query_property_ioctl(hdevice
, &data
))
4027 // Newer BusType* values are missing in older includes
4028 switch ((int)data
.desc
.BusType
) {
4030 case 0x0b: // BusTypeSata
4031 // Certain Intel AHCI drivers (C600+/C220+) have broken
4032 // IOCTL_ATA_PASS_THROUGH support and a working SAT layer
4037 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
4045 // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
4046 if (is_intel_raid_volume(&data
))
4048 // LSI/3ware RAID volume: supports SMART_*
4049 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
4054 case 0x09: // BusTypeiScsi
4055 case 0x0a: // BusTypeSas
4070 // get DEV_* for device path
4071 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
4074 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
4075 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4076 if (h
== INVALID_HANDLE_VALUE
) {
4078 h
= CreateFileA(path
, 0,
4079 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
4080 if (h
== INVALID_HANDLE_VALUE
)
4083 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
4084 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
4085 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
4090 // get DEV_* for physical drive number
4091 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
4094 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
4095 return get_controller_type(path
, ata_version_ex
);
4098 static win_dev_type
get_phy_drive_type(int drive
)
4100 return get_phy_drive_type(drive
, 0);
4103 // get DEV_* for logical drive number
4104 static win_dev_type
get_log_drive_type(int drive
)
4107 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
4108 return get_controller_type(path
);
4111 static win_dev_type
get_dev_type(const char * name
, int & phydrive
, int & logdrive
)
4113 phydrive
= logdrive
= -1;
4115 name
= skipdev(name
);
4116 if (!strncmp(name
, "st", 2))
4118 if (!strncmp(name
, "nst", 3))
4120 if (!strncmp(name
, "tape", 4))
4123 logdrive
= drive_letter(name
);
4124 if (logdrive
>= 0) {
4125 win_dev_type type
= get_log_drive_type(logdrive
);
4126 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
4129 char drive
[2+1] = "";
4130 if (sscanf(name
, "sd%2[a-z]", drive
) == 1) {
4131 phydrive
= sdxy_to_phydrive(drive
);
4132 return get_phy_drive_type(phydrive
);
4135 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
4136 return get_phy_drive_type(phydrive
);
4142 ata_device
* win_smart_interface::get_usb_device(const char * name
,
4143 int phydrive
, int logdrive
/* = -1 */)
4145 // Get USB bridge ID
4146 unsigned short vendor_id
= 0, product_id
= 0;
4147 if (!get_usb_id(phydrive
, logdrive
, vendor_id
, product_id
)) {
4148 set_err(EINVAL
, "Unable to read USB device ID");
4152 // Get type name for this ID
4153 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
4157 // Return SAT/USB device for this type
4158 return get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
4161 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
4163 const char * testname
= skipdev(name
);
4164 if (str_starts_with(testname
, "hd"))
4165 return new win_ata_device(this, name
, "");
4167 if (str_starts_with(testname
, "tw_cli"))
4168 return new win_tw_cli_device(this, name
, "");
4170 if (str_starts_with(testname
, "csmi"))
4171 return new win_csmi_device(this, name
, "");
4173 if (str_starts_with(testname
, "nvme"))
4174 return new win_nvme_device(this, name
, "", 0 /* use default nsid */);
4176 int phydrive
= -1, logdrive
= -1;
4177 win_dev_type type
= get_dev_type(name
, phydrive
, logdrive
);
4179 if (type
== DEV_ATA
)
4180 return new win_ata_device(this, name
, "");
4182 if (type
== DEV_SCSI
)
4183 return new win_scsi_device(this, name
, "");
4185 if (type
== DEV_SAT
)
4186 return get_sat_device("sat", new win_scsi_device(this, name
, ""));
4188 if (type
== DEV_USB
)
4189 return get_usb_device(name
, phydrive
, logdrive
);
4196 bool win_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
4197 const char * type
, const char * pattern
/* = 0*/)
4200 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
4204 // Check for "[*,]pd" type
4206 char type2
[16+1] = "";
4209 if (!strcmp(type
, "pd")) {
4213 else if (sscanf(type
, "%16[^,],pd%n", type2
, &nc
) == 1 &&
4214 nc
== (int)strlen(type
)) {
4221 bool ata
, scsi
, sat
, usb
, csmi
, nvme
;
4223 ata
= scsi
= usb
= sat
= csmi
= true;
4224 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
4231 ata
= scsi
= usb
= sat
= csmi
= nvme
= false;
4232 if (!strcmp(type
, "ata"))
4234 else if (!strcmp(type
, "scsi"))
4236 else if (!strcmp(type
, "sat"))
4238 else if (!strcmp(type
, "usb"))
4240 else if (!strcmp(type
, "csmi"))
4242 else if (!strcmp(type
, "nvme"))
4246 "Invalid type '%s', valid arguments are: ata[,pd], scsi[,pd], "
4247 "sat[,pd], usb[,pd], csmi, nvme, pd", type
);
4254 if (ata
|| scsi
|| sat
|| usb
) {
4255 // Scan up to 128 drives and 2 3ware controllers
4256 const int max_raid
= 2;
4257 bool raid_seen
[max_raid
] = {false, false};
4259 for (int i
= 0; i
< 128; i
++) {
4261 snprintf(name
, sizeof(name
), "/dev/pd%d", i
);
4262 else if (i
+ 'a' <= 'z')
4263 snprintf(name
, sizeof(name
), "/dev/sd%c", i
+ 'a');
4265 snprintf(name
, sizeof(name
), "/dev/sd%c%c",
4266 i
/ ('z'-'a'+1) - 1 + 'a',
4267 i
% ('z'-'a'+1) + 'a');
4269 smart_device
* dev
= 0;
4270 GETVERSIONINPARAMS_EX vers_ex
;
4272 switch (get_phy_drive_type(i
, (ata
? &vers_ex
: 0))) {
4274 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
4278 // Interpret RAID drive map if present
4279 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
4280 // Skip if too many controllers or logical drive from this controller already seen
4281 if (!(vers_ex
.wControllerId
< max_raid
&& !raid_seen
[vers_ex
.wControllerId
]))
4283 raid_seen
[vers_ex
.wControllerId
] = true;
4284 // Add physical drives
4285 int len
= strlen(name
);
4286 for (unsigned int pi
= 0; pi
< 32; pi
++) {
4287 if (vers_ex
.dwDeviceMapEx
& (1L << pi
)) {
4288 snprintf(name
+len
, sizeof(name
)-1-len
, ",%u", pi
);
4289 devlist
.push_back( new win_ata_device(this, name
, "ata") );
4295 dev
= new win_ata_device(this, name
, "ata");
4299 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
4302 dev
= new win_scsi_device(this, name
, "scsi");
4306 // STORAGE_QUERY_PROPERTY returned VendorId "ATA "
4309 dev
= get_sat_device("sat", new win_scsi_device(this, name
, ""));
4313 // STORAGE_QUERY_PROPERTY returned USB
4316 dev
= get_usb_device(name
, i
);
4318 // Unknown or unsupported USB ID, return as SCSI
4319 dev
= new win_scsi_device(this, name
, "");
4327 devlist
.push_back(dev
);
4332 // Scan CSMI devices
4333 for (int i
= 0; i
<= 9; i
++) {
4334 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,0", i
);
4335 win_csmi_device
test_dev(this, name
, "");
4336 if (!test_dev
.open_scsi())
4339 unsigned ports_used
= test_dev
.get_ports_used();
4343 for (int pi
= 0; pi
< 32; pi
++) {
4344 if (!(ports_used
& (1 << pi
)))
4346 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,%d", i
, pi
);
4347 devlist
.push_back( new win_csmi_device(this, name
, "ata") );
4353 // Scan \\.\Scsi[0-31] for up to 10 NVMe devices
4355 for (int i
= 0; i
< 32; i
++) {
4356 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4357 win_nvme_device
test_dev(this, name
, "", 0);
4358 if (!test_dev
.open_scsi(i
)) {
4359 if (test_dev
.get_errno() == EACCES
)
4364 if (!test_dev
.probe())
4366 if (++nvme_cnt
>= 10)
4370 for (int i
= 0; i
< nvme_cnt
; i
++) {
4371 snprintf(name
, sizeof(name
)-1, "/dev/nvme%d", i
);
4372 devlist
.push_back( new win_nvme_device(this, name
, "nvme", 0) );
4379 // get examples for smartctl
4380 std::string
win_smart_interface::get_app_examples(const char * appname
)
4382 if (strcmp(appname
, "smartctl"))
4384 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
4385 " smartctl -a /dev/sda (Prints all SMART information)\n\n"
4386 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/sda\n"
4387 " (Enables SMART on first disk)\n\n"
4388 " smartctl -t long /dev/sda (Executes extended disk self-test)\n\n"
4389 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/sda\n"
4390 " (Prints Self-Test & Attribute errors)\n"
4391 " smartctl -a /dev/sda\n"
4392 " (Prints all information for disk on PhysicalDrive 0)\n"
4393 " smartctl -a /dev/pd3\n"
4394 " (Prints all information for disk on PhysicalDrive 3)\n"
4395 " smartctl -a /dev/tape1\n"
4396 " (Prints all information for SCSI tape on Tape 1)\n"
4397 " smartctl -A /dev/hdb,3\n"
4398 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
4399 " smartctl -A /dev/tw_cli/c0/p1\n"
4400 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
4401 " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
4402 " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
4403 " on 1st Areca RAID controller)\n"
4405 " ATA SMART access methods and ordering may be specified by modifiers\n"
4406 " following the device name: /dev/hdX:[saicm], where\n"
4407 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
4408 " 'i': IOCTL_IDE_PASS_THROUGH, 'f': IOCTL_STORAGE_*,\n"
4409 " 'm': IOCTL_SCSI_MINIPORT_*.\n"
4411 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
4416 bool win_smart_interface::disable_system_auto_standby(bool disable
)
4419 SYSTEM_POWER_STATUS ps
;
4420 if (!GetSystemPowerStatus(&ps
))
4421 return set_err(ENOSYS
, "Unknown power status");
4422 if (ps
.ACLineStatus
!= 1) {
4423 SetThreadExecutionState(ES_CONTINUOUS
);
4424 if (ps
.ACLineStatus
== 0)
4425 set_err(EIO
, "AC offline");
4427 set_err(EIO
, "Unknown AC line status");
4432 if (!SetThreadExecutionState(ES_CONTINUOUS
| (disable
? ES_SYSTEM_REQUIRED
: 0)))
4433 return set_err(ENOSYS
);
4440 /////////////////////////////////////////////////////////////////////////////
4442 // Initialize platform interface and register with smi()
4443 void smart_interface::init()
4446 // Remove "." from DLL search path if supported
4447 // to prevent DLL preloading attacks
4448 BOOL (WINAPI
* SetDllDirectoryA_p
)(LPCSTR
) = (BOOL (WINAPI
*)(LPCSTR
))
4449 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4450 if (SetDllDirectoryA_p
)
4451 SetDllDirectoryA_p("");
4454 static os_win32::win_smart_interface the_win_interface
;
4455 smart_interface::set(&the_win_interface
);
4461 // Get exe directory
4462 // (prototype in utiliy.h)
4463 std::string
get_exe_dir()
4465 char path
[MAX_PATH
];
4466 // Get path of this exe
4467 if (!GetModuleFileNameA(GetModuleHandleA(0), path
, sizeof(path
)))
4468 throw std::runtime_error("GetModuleFileName() failed");
4469 // Replace backslash by slash
4471 for (int i
= 0; path
[i
]; i
++)
4472 if (path
[i
] == '\\') {
4473 path
[i
] = '/'; sl
= i
;