4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
20 #define _WIN32_WINNT WINVER
26 #include "smartctl.h" // TODO: Do not use smartctl only variables here
28 #include "dev_interface.h"
29 #include "dev_ata_cmd_set.h"
31 #include "os_win32/wmiquery.h"
39 #define assert(x) /* */
42 #include <stddef.h> // offsetof()
43 #include <io.h> // access()
45 // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
46 #define WIN32_LEAN_AND_MEAN
50 // i686-w64-mingw32, x86_64-w64-mingw32
51 // (Missing: FILE_DEVICE_SCSI)
56 #elif HAVE_DDK_NTDDDISK_H
57 // i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
58 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
59 #include <ddk/ntdddisk.h>
60 #include <ddk/ntddscsi.h>
61 #include <ddk/ntddstor.h>
64 // (Missing: IOCTL_STORAGE_QUERY_PROPERTY, FILE_DEVICE_SCSI)
73 #include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
76 // Macro to check constants at compile time using a dummy typedef
77 #define ASSERT_CONST(c, n) \
78 typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
79 #define ASSERT_SIZEOF(t, n) \
80 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
83 #define SELECT_WIN_32_64(x32, x64) (x32)
85 #define SELECT_WIN_32_64(x32, x64) (x64)
88 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 3358 2011-06-06 19:04:20Z chrfranke $";
90 // Disable Win9x/ME specific code if no longer supported by compiler.
93 #elif !defined(WIN9X_SUPPORT)
94 #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
95 // Win9x/ME support was dropped in Cygwin 1.7
96 #elif defined(_MSC_VER) && (_MSC_VER >= 1500)
97 // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)
99 #define WIN9X_SUPPORT 1
103 /////////////////////////////////////////////////////////////////////////////
104 // Windows I/O-controls, some declarations are missing in the include files
108 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
110 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
111 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
112 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
113 ASSERT_SIZEOF(GETVERSIONINPARAMS
, 24);
114 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
115 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
118 // IDE PASS THROUGH (2000, XP, undocumented)
120 #ifndef IOCTL_IDE_PASS_THROUGH
122 #define IOCTL_IDE_PASS_THROUGH \
123 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
125 #endif // IOCTL_IDE_PASS_THROUGH
131 ULONG DataBufferSize
;
137 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
138 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
141 // ATA PASS THROUGH (Win2003, XP SP2)
143 #ifndef IOCTL_ATA_PASS_THROUGH
145 #define IOCTL_ATA_PASS_THROUGH \
146 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
148 typedef struct _ATA_PASS_THROUGH_EX
{
154 UCHAR ReservedAsUchar
;
155 ULONG DataTransferLength
;
157 ULONG ReservedAsUlong
;
158 ULONG_PTR DataBufferOffset
;
159 UCHAR PreviousTaskFile
[8];
160 UCHAR CurrentTaskFile
[8];
161 } ATA_PASS_THROUGH_EX
;
163 #define ATA_FLAGS_DRDY_REQUIRED 0x01
164 #define ATA_FLAGS_DATA_IN 0x02
165 #define ATA_FLAGS_DATA_OUT 0x04
166 #define ATA_FLAGS_48BIT_COMMAND 0x08
167 #define ATA_FLAGS_USE_DMA 0x10
168 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
170 #endif // IOCTL_ATA_PASS_THROUGH
172 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
173 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, SELECT_WIN_32_64(40, 48));
176 // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
178 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
179 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT
, 0x04d014);
180 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, SELECT_WIN_32_64(44, 56));
181 ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT
, SELECT_WIN_32_64(44, 56));
184 // SMART IOCTL via SCSI MINIPORT ioctl
186 #ifndef FILE_DEVICE_SCSI
188 #define FILE_DEVICE_SCSI 0x001b
190 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
191 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
192 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
193 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
194 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
195 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
196 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
197 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
198 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
199 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
200 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
201 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
202 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
204 #endif // FILE_DEVICE_SCSI
206 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
207 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
210 // IOCTL_STORAGE_QUERY_PROPERTY
212 #ifndef IOCTL_STORAGE_QUERY_PROPERTY
214 #define IOCTL_STORAGE_QUERY_PROPERTY \
215 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
217 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
221 UCHAR DeviceTypeModifier
;
222 BOOLEAN RemovableMedia
;
223 BOOLEAN CommandQueueing
;
224 ULONG VendorIdOffset
;
225 ULONG ProductIdOffset
;
226 ULONG ProductRevisionOffset
;
227 ULONG SerialNumberOffset
;
228 STORAGE_BUS_TYPE BusType
;
229 ULONG RawPropertiesLength
;
230 UCHAR RawDeviceProperties
[1];
231 } STORAGE_DEVICE_DESCRIPTOR
;
233 typedef enum _STORAGE_QUERY_TYPE
{
234 PropertyStandardQuery
= 0,
237 PropertyQueryMaxDefined
238 } STORAGE_QUERY_TYPE
;
240 typedef enum _STORAGE_PROPERTY_ID
{
241 StorageDeviceProperty
= 0,
242 StorageAdapterProperty
,
243 StorageDeviceIdProperty
,
244 StorageDeviceUniqueIdProperty
,
245 StorageDeviceWriteCacheProperty
,
246 StorageMiniportProperty
,
247 StorageAccessAlignmentProperty
248 } STORAGE_PROPERTY_ID
;
250 typedef struct _STORAGE_PROPERTY_QUERY
{
251 STORAGE_PROPERTY_ID PropertyId
;
252 STORAGE_QUERY_TYPE QueryType
;
253 UCHAR AdditionalParameters
[1];
254 } STORAGE_PROPERTY_QUERY
;
256 #endif // IOCTL_STORAGE_QUERY_PROPERTY
258 ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY
, 0x002d1400);
259 ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR
, 36+1+3);
260 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY
, 8+1+3);
263 // IOCTL_STORAGE_PREDICT_FAILURE
265 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE
, 0x002d1100);
266 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
269 // 3ware specific versions of SMART ioctl structs
271 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
275 typedef struct _GETVERSIONINPARAMS_EX
{
281 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
282 WORD wIdentifier
; // Vendor specific identifier
283 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
285 } GETVERSIONINPARAMS_EX
;
287 typedef struct _SENDCMDINPARAMS_EX
{
291 BYTE bPortNumber
; // 3ware specific: port number
292 WORD wIdentifier
; // Vendor specific identifier
295 } SENDCMDINPARAMS_EX
;
299 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONINPARAMS
));
300 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
305 ASSERT_SIZEOF(IOCTL_HEADER
, sizeof(SRB_IO_CONTROL
));
306 ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER
, 204);
307 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER
, 2080);
308 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER
, 168);
312 /////////////////////////////////////////////////////////////////////////////
314 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
317 #pragma warning(disable:4250)
320 // Running on Win9x/ME ?
322 // Set true in win9x_smart_interface ctor.
323 static bool win9x
= false;
325 // Never true (const allows compiler to remove dead code).
326 const bool win9x
= false;
330 class win_smart_device
331 : virtual public /*implements*/ smart_device
335 : smart_device(never_called
),
336 m_fh(INVALID_HANDLE_VALUE
)
339 virtual ~win_smart_device() throw();
341 virtual bool is_open() const;
343 virtual bool close();
346 /// Set handle for open() in derived classes.
347 void set_fh(HANDLE fh
)
350 /// Return handle for derived classes.
351 HANDLE
get_fh() const
355 HANDLE m_fh
; ///< File handle
359 /////////////////////////////////////////////////////////////////////////////
362 : public /*implements*/ ata_device
,
363 public /*extends*/ win_smart_device
366 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
368 virtual ~win_ata_device() throw();
372 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
374 virtual bool ata_identify_is_cached() const;
377 bool open(int phydrive
, int logdrive
, const char * options
, int port
);
379 std::string m_options
;
380 bool m_usr_options
; // options set by user?
381 bool m_admin
; // open with admin access?
382 bool m_id_is_cached
; // ata_identify_is_cached() return value.
383 bool m_is_3ware
; // AMCC/3ware controller detected?
385 int m_smartver_state
;
389 /////////////////////////////////////////////////////////////////////////////
391 class win_scsi_device
392 : public /*implements*/ scsi_device
,
393 virtual public /*extends*/ win_smart_device
396 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
400 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
403 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
407 /////////////////////////////////////////////////////////////////////////////
411 class win_aspi_device
412 : public /*implements*/ scsi_device
415 win_aspi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
417 virtual bool is_open() const;
421 virtual bool close();
423 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
430 #endif // WIN9X_SUPPORT
433 //////////////////////////////////////////////////////////////////////
436 : virtual public /*extends*/ smart_device
440 bool get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
);
442 /// Check physical drive existence
443 bool check_phy(const CSMI_SAS_PHY_INFO
& phy_info
, unsigned phy_no
);
447 : smart_device(never_called
)
448 { memset(&m_phy_ent
, 0, sizeof(m_phy_ent
)); }
450 /// Select physical drive
451 bool select_phy(unsigned phy_no
);
453 /// Get info for selected physical drive
454 const CSMI_SAS_PHY_ENTITY
& get_phy_ent() const
455 { return m_phy_ent
; }
457 /// Call platform-specific CSMI ioctl
458 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
459 unsigned csmi_bufsiz
) = 0;
462 CSMI_SAS_PHY_ENTITY m_phy_ent
; ///< CSMI info for this phy
466 class csmi_ata_device
467 : virtual public /*extends*/ csmi_device
,
468 virtual public /*implements*/ ata_device
471 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
475 : smart_device(never_called
) { }
479 //////////////////////////////////////////////////////////////////////
481 class win_csmi_device
482 : public /*implements*/ csmi_ata_device
485 win_csmi_device(smart_interface
* intf
, const char * dev_name
,
486 const char * req_type
);
488 virtual ~win_csmi_device() throw();
492 virtual bool close();
494 virtual bool is_open() const;
499 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
500 unsigned csmi_bufsiz
);
503 HANDLE m_fh
; ///< Controller device handle
504 unsigned m_phy_no
; ///< Physical drive number
508 //////////////////////////////////////////////////////////////////////
510 class win_tw_cli_device
511 : public /*implements*/ ata_device_with_command_set
514 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
516 virtual bool is_open() const;
520 virtual bool close();
523 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
526 bool m_ident_valid
, m_smart_valid
;
527 ata_identify_device m_ident_buf
;
528 ata_smart_values m_smart_buf
;
532 //////////////////////////////////////////////////////////////////////
533 // Platform specific interfaces
535 // Common to all windows flavors
536 class win_smart_interface
537 : public /*implements part of*/ smart_interface
540 virtual std::string
get_os_version_str();
542 virtual std::string
get_app_examples(const char * appname
);
544 //virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
545 // const char * pattern = 0);
548 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
550 //virtual scsi_device * get_scsi_device(const char * name, const char * type);
552 virtual smart_device
* autodetect_smart_device(const char * name
);
557 // Win9x/ME reduced functionality
558 class win9x_smart_interface
559 : public /*extends*/ win_smart_interface
562 win9x_smart_interface()
565 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
566 const char * pattern
= 0);
569 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
572 bool ata_scan(smart_device_list
& devlist
);
574 bool scsi_scan(smart_device_list
& devlist
);
577 #endif // WIN9X_SUPPORT
580 class winnt_smart_interface
581 : public /*extends*/ win_smart_interface
584 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
585 const char * pattern
= 0);
588 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
590 virtual smart_device
* autodetect_smart_device(const char * name
);
594 //////////////////////////////////////////////////////////////////////
597 // Running on 64-bit Windows as 32-bit app ?
598 static bool is_wow64()
600 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
601 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))
602 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
603 if (!IsWow64Process_p
)
606 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
612 // Return info string about build host and OS version
613 std::string
win_smart_interface::get_os_version_str()
615 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
616 = SMARTMONTOOLS_BUILD_HOST
;
619 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
620 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
621 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
623 OSVERSIONINFOEXA vi
; memset(&vi
, 0, sizeof(vi
));
624 vi
.dwOSVersionInfoSize
= sizeof(vi
);
625 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
)) {
626 memset(&vi
, 0, sizeof(vi
));
627 vi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
628 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
))
632 if (vi
.dwPlatformId
> 0xff || vi
.dwMajorVersion
> 0xff || vi
.dwMinorVersion
> 0xff)
636 switch (vi
.dwPlatformId
<< 16 | vi
.dwMajorVersion
<< 8 | vi
.dwMinorVersion
) {
637 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400| 0:
638 w
= (vi
.szCSDVersion
[1] == 'B' ||
639 vi
.szCSDVersion
[1] == 'C' ? "95-osr2" : "95"); break;
640 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|10:
641 w
= (vi
.szCSDVersion
[1] == 'A' ? "98se" : "98"); break;
642 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|90: w
= "me"; break;
643 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
644 case VER_PLATFORM_WIN32_NT
<<16|0x0400| 0: w
= "nt4"; break;
645 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 0: w
= "2000"; break;
646 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 1:
647 w
= (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
649 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 2:
650 w
= (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
652 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 0:
653 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "vista"
655 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 1:
656 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "win7"
658 default: w
= 0; break;
661 const char * w64
= "";
668 snprintf(vptr
, vlen
, "-%s%lu.%lu%s",
669 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "9x"),
670 vi
.dwMajorVersion
, vi
.dwMinorVersion
, w64
);
671 else if (vi
.wServicePackMinor
)
672 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
673 else if (vi
.wServicePackMajor
)
674 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
676 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
680 // Return value for device detection functions
681 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_USB
};
683 static win_dev_type
get_phy_drive_type(int drive
);
684 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
);
685 static win_dev_type
get_log_drive_type(int drive
);
686 static bool get_usb_id(int drive
, unsigned short & vendor_id
,
687 unsigned short & product_id
);
689 static const char * ata_get_def_options(void);
692 static int is_permissive()
694 if (!failuretest_permissive
) {
695 pout("To continue, add one or more '-T permissive' options.\n");
698 failuretest_permissive
--;
702 // return number for drive letter, -1 on error
703 // "[A-Za-z]:([/\\][.]?)?" => 0-25
704 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
705 static int drive_letter(const char * s
)
707 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
709 && (!s
[2] || ( strchr("/\\\"", s
[2])
710 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
711 (s
[0] & 0x1f) - 1 : -1);
714 // Skip trailing "/dev/", do not allow "/dev/X:"
715 static const char * skipdev(const char * s
)
717 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
720 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
722 const char * testname
= skipdev(name
);
723 if (!strncmp(testname
, "csmi", 4))
724 return new win_csmi_device(this, name
, type
);
725 if (!strncmp(testname
, "tw_cli", 6))
726 return new win_tw_cli_device(this, name
, type
);
727 return new win_ata_device(this, name
, type
);
732 scsi_device
* win9x_smart_interface::get_scsi_device(const char * name
, const char * type
)
734 return new win_aspi_device(this, name
, type
);
739 scsi_device
* winnt_smart_interface::get_scsi_device(const char * name
, const char * type
)
741 const char * testname
= skipdev(name
);
742 if (!strncmp(testname
, "scsi", 4))
744 return new win_aspi_device(this, name
, type
);
746 return (set_err(EINVAL
, "ASPI interface not supported"), (scsi_device
*)0);
748 return new win_scsi_device(this, name
, type
);
751 static win_dev_type
get_dev_type(const char * name
, int & phydrive
)
754 name
= skipdev(name
);
755 if (!strncmp(name
, "st", 2))
757 if (!strncmp(name
, "nst", 3))
759 if (!strncmp(name
, "tape", 4))
762 int logdrive
= drive_letter(name
);
764 win_dev_type type
= get_log_drive_type(logdrive
);
765 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
768 char drive
[1+1] = "";
769 if (sscanf(name
, "sd%1[a-z]", drive
) == 1) {
770 phydrive
= drive
[0] - 'a';
771 return get_phy_drive_type(phydrive
);
775 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
776 return get_phy_drive_type(phydrive
);
780 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
782 const char * testname
= skipdev(name
);
783 if (!strncmp(testname
, "hd", 2))
784 return new win_ata_device(this, name
, "");
786 if (!strncmp(testname
, "scsi", 4))
787 return new win_aspi_device(this, name
, "");
789 if (!strncmp(testname
, "tw_cli", 6))
790 return new win_tw_cli_device(this, name
, "");
794 smart_device
* winnt_smart_interface::autodetect_smart_device(const char * name
)
796 smart_device
* dev
= win_smart_interface::autodetect_smart_device(name
);
800 if (!strncmp(skipdev(name
), "csmi", 4))
801 return new win_csmi_device(this, name
, "");
804 win_dev_type type
= get_dev_type(name
, phydrive
);
807 return new win_ata_device(this, name
, "");
808 if (type
== DEV_SCSI
)
809 return new win_scsi_device(this, name
, "");
811 if (type
== DEV_USB
) {
813 unsigned short vendor_id
= 0, product_id
= 0;
814 if (!(phydrive
>= 0 && get_usb_id(phydrive
, vendor_id
, product_id
))) {
815 set_err(EINVAL
, "Unable to read USB device ID");
818 // Get type name for this ID
819 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
822 // Return SAT/USB device for this type
823 return get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
832 // Scan for devices on Win9x/ME
834 bool win9x_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
835 const char * type
, const char * pattern
/* = 0*/)
838 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
842 if (!type
|| !strcmp(type
, "ata")) {
843 if (!ata_scan(devlist
))
847 if (!type
|| !strcmp(type
, "scsi")) {
848 if (!scsi_scan(devlist
))
854 #endif // WIN9X_SUPPORT
859 bool winnt_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
860 const char * type
, const char * pattern
/* = 0*/)
863 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
868 bool ata
, scsi
, usb
, csmi
;
870 ata
= scsi
= usb
= csmi
= true;
873 ata
= scsi
= usb
= csmi
= false;
874 if (!strcmp(type
, "ata"))
876 else if (!strcmp(type
, "scsi"))
878 else if (!strcmp(type
, "usb"))
880 else if (!strcmp(type
, "csmi"))
883 set_err(EINVAL
, "Invalid type '%s', valid arguments are: ata, scsi, usb, csmi", type
);
888 // Scan up to 10 drives and 2 3ware controllers
889 const int max_raid
= 2;
890 bool raid_seen
[max_raid
] = {false, false};
893 for (int i
= 0; i
<= 9; i
++) {
894 sprintf(name
, "/dev/sd%c", 'a'+i
);
895 GETVERSIONINPARAMS_EX vers_ex
;
897 switch (get_phy_drive_type(i
, (ata
? &vers_ex
: 0))) {
899 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
903 // Interpret RAID drive map if present
904 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
905 // Skip if too many controllers or logical drive from this controller already seen
906 if (!(vers_ex
.wControllerId
< max_raid
&& !raid_seen
[vers_ex
.wControllerId
]))
908 raid_seen
[vers_ex
.wControllerId
] = true;
909 // Add physical drives
910 int len
= strlen(name
);
911 for (int pi
= 0; pi
< 32; pi
++) {
912 if (vers_ex
.dwDeviceMapEx
& (1L << pi
)) {
913 sprintf(name
+len
, ",%u", pi
);
914 devlist
.push_back( new win_ata_device(this, name
, "ata") );
919 devlist
.push_back( new win_ata_device(this, name
, "ata") );
924 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
927 devlist
.push_back( new win_scsi_device(this, name
, "scsi") );
931 // STORAGE_QUERY_PROPERTY returned USB
935 // TODO: Use common function for this and autodetect_smart_device()
937 unsigned short vendor_id
= 0, product_id
= 0;
938 if (!get_usb_id(i
, vendor_id
, product_id
))
940 // Get type name for this ID
941 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
944 // Return SAT/USB device for this type
945 ata_device
* dev
= get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
948 devlist
.push_back(dev
);
960 for (int i
= 0; i
<= 9; i
++) {
961 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,0", i
);
962 win_csmi_device
test_dev(this, name
, "");
963 if (!test_dev
.open_scsi())
965 CSMI_SAS_PHY_INFO phy_info
;
966 if (!test_dev
.get_phy_info(phy_info
))
969 for (int pi
= 0; pi
< phy_info
.bNumberOfPhys
; pi
++) {
970 if (!test_dev
.check_phy(phy_info
, pi
))
972 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,%d", i
, pi
);
973 devlist
.push_back( new win_csmi_device(this, name
, "ata") );
981 // get examples for smartctl
982 std::string
win_smart_interface::get_app_examples(const char * appname
)
984 if (strcmp(appname
, "smartctl"))
986 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
987 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
988 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
989 " (Enables SMART on first disk)\n\n"
990 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
991 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
992 " (Prints Self-Test & Attribute errors)\n"
994 " smartctl -a /dev/scsi21\n"
995 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
997 " smartctl -a /dev/sda\n"
998 " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
999 " smartctl -a /dev/pd3\n"
1000 " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
1001 " smartctl -a /dev/tape1\n"
1002 " (Prints all information for SCSI tape on Tape 1)\n"
1003 " smartctl -A /dev/hdb,3\n"
1004 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
1005 " smartctl -A /dev/tw_cli/c0/p1\n"
1006 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
1008 " ATA SMART access methods and ordering may be specified by modifiers\n"
1009 " following the device name: /dev/hdX:[saicm], where\n"
1010 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
1011 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
1012 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
1014 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
1019 /////////////////////////////////////////////////////////////////////////////
1021 /////////////////////////////////////////////////////////////////////////////
1023 #define SMART_CYL_LOW 0x4F
1024 #define SMART_CYL_HI 0xC2
1026 static void print_ide_regs(const IDEREGS
* r
, int out
)
1028 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
1029 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
1030 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
1033 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
1035 pout(" Input : "); print_ide_regs(ri
, 0);
1037 pout(" Output: "); print_ide_regs(ro
, 1);
1041 /////////////////////////////////////////////////////////////////////////////
1043 // call SMART_GET_VERSION, return device map or -1 on error
1045 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
1047 GETVERSIONINPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
1048 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
1051 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
1052 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
1054 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
1058 assert(num_out
== sizeof(GETVERSIONINPARAMS
));
1060 if (ata_debugmode
> 1) {
1061 pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
1062 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
1063 num_out
, vers
.bVersion
, vers
.bRevision
,
1064 vers
.fCapabilities
, vers
.bIDEDeviceMap
);
1065 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
1066 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
1067 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, vers_ex
.dwDeviceMapEx
);
1071 *ata_version_ex
= vers_ex
;
1073 // TODO: Check vers.fCapabilities here?
1074 return vers
.bIDEDeviceMap
;
1078 // call SMART_* ioctl
1080 static int smart_ioctl(HANDLE hdevice
, int drive
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
1082 SENDCMDINPARAMS inpar
;
1083 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
1085 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
1086 const SENDCMDOUTPARAMS
* outpar
;
1087 DWORD code
, num_out
;
1088 unsigned int size_out
;
1091 memset(&inpar
, 0, sizeof(inpar
));
1092 inpar
.irDriveRegs
= *regs
;
1093 // drive is set to 0-3 on Win9x only
1094 inpar
.irDriveRegs
.bDriveHeadReg
= 0xA0 | ((drive
& 1) << 4);
1095 inpar
.bDriveNumber
= drive
;
1099 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
1100 inpar_ex
.bPortNumber
= port
;
1103 if (datasize
== 512) {
1104 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
1105 inpar
.cBufferSize
= size_out
= 512;
1107 else if (datasize
== 0) {
1108 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
1109 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
1110 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
1111 // Note: cBufferSize must be 0 on Win9x
1120 memset(&outbuf
, 0, sizeof(outbuf
));
1122 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
1123 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
1124 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
1125 long err
= GetLastError();
1126 if (ata_debugmode
&& (err
!= ERROR_INVALID_PARAMETER
|| ata_debugmode
> 1)) {
1127 pout(" %s failed, Error=%ld\n", name
, err
);
1128 print_ide_regs_io(regs
, NULL
);
1130 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
1131 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
1132 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1135 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
1137 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
1139 if (outpar
->DriverStatus
.bDriverError
) {
1140 if (ata_debugmode
) {
1141 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1142 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
1143 print_ide_regs_io(regs
, NULL
);
1145 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1149 if (ata_debugmode
> 1) {
1150 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1151 num_out
, outpar
->cBufferSize
);
1152 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
1153 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
1157 memcpy(data
, outpar
->bBuffer
, 512);
1158 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
1159 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
1160 memcpy(regs
, outpar
->bBuffer
, sizeof(IDEREGS
));
1161 else { // Workaround for driver not returning regs
1163 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
1164 *regs
= inpar
.irDriveRegs
;
1172 /////////////////////////////////////////////////////////////////////////////
1173 // IDE PASS THROUGH (2000, XP, undocumented)
1175 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
1176 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
1178 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1180 if (datasize
> 512) {
1184 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
1185 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
1187 const unsigned char magic
= 0xcf;
1194 buf
->IdeReg
= *regs
;
1195 buf
->DataBufferSize
= datasize
;
1197 buf
->DataBuffer
[0] = magic
;
1199 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
1200 buf
, size
, buf
, size
, &num_out
, NULL
)) {
1201 long err
= GetLastError();
1202 if (ata_debugmode
) {
1203 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
1204 print_ide_regs_io(regs
, NULL
);
1206 VirtualFree(buf
, 0, MEM_RELEASE
);
1207 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1212 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
1213 if (ata_debugmode
) {
1214 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
1215 print_ide_regs_io(regs
, &buf
->IdeReg
);
1217 VirtualFree(buf
, 0, MEM_RELEASE
);
1222 // Check and copy data
1224 if ( num_out
!= size
1225 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
1226 if (ata_debugmode
) {
1227 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
1228 num_out
, buf
->DataBufferSize
);
1229 print_ide_regs_io(regs
, &buf
->IdeReg
);
1231 VirtualFree(buf
, 0, MEM_RELEASE
);
1235 memcpy(data
, buf
->DataBuffer
, datasize
);
1238 if (ata_debugmode
> 1) {
1239 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
1240 num_out
, buf
->DataBufferSize
);
1241 print_ide_regs_io(regs
, &buf
->IdeReg
);
1243 *regs
= buf
->IdeReg
;
1245 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
1246 VirtualFree(buf
, 0, MEM_RELEASE
);
1251 /////////////////////////////////////////////////////////////////////////////
1252 // ATA PASS THROUGH (Win2003, XP SP2)
1255 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
1256 // transfer per command. Therefore, multi-sector transfers are only supported
1257 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
1258 // or READ/WRITE LOG EXT work only with single sector transfers.
1259 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
1261 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
1263 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
1265 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
1268 ATA_PASS_THROUGH_EX apt
;
1270 UCHAR ucDataBuf
[max_sectors
* 512];
1271 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
1273 const unsigned char magic
= 0xcf;
1275 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
1276 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
1277 //ab.apt.PathId = 0;
1278 //ab.apt.TargetId = 0;
1280 ab
.apt
.TimeOutValue
= 10;
1281 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
1282 ab
.apt
.DataBufferOffset
= size
;
1285 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1289 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
1290 ab
.apt
.DataTransferLength
= datasize
;
1292 ab
.ucDataBuf
[0] = magic
;
1294 else if (datasize
< 0) {
1295 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1299 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
1300 ab
.apt
.DataTransferLength
= -datasize
;
1302 memcpy(ab
.ucDataBuf
, data
, -datasize
);
1305 assert(ab
.apt
.AtaFlags
== 0);
1306 assert(ab
.apt
.DataTransferLength
== 0);
1309 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
1310 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
1311 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
1315 *ptfregs
= *prev_regs
;
1316 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
1320 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
1321 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
1322 long err
= GetLastError();
1323 if (ata_debugmode
) {
1324 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
1325 print_ide_regs_io(regs
, NULL
);
1327 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1332 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
1333 if (ata_debugmode
) {
1334 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
1335 print_ide_regs_io(regs
, ctfregs
);
1341 // Check and copy data
1343 if ( num_out
!= size
1344 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
1345 if (ata_debugmode
) {
1346 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out
);
1347 print_ide_regs_io(regs
, ctfregs
);
1352 memcpy(data
, ab
.ucDataBuf
, datasize
);
1355 if (ata_debugmode
> 1) {
1356 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1357 print_ide_regs_io(regs
, ctfregs
);
1361 *prev_regs
= *ptfregs
;
1367 /////////////////////////////////////////////////////////////////////////////
1368 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
1370 // undocumented SCSI opcode to for ATA passthrough
1371 #define SCSIOP_ATA_PASSTHROUGH 0xCC
1373 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1376 SCSI_PASS_THROUGH spt
;
1378 UCHAR ucSenseBuf
[32];
1379 UCHAR ucDataBuf
[512];
1380 } SCSI_PASS_THROUGH_WITH_BUFFERS
;
1382 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
1386 const unsigned char magic
= 0xcf;
1388 memset(&sb
, 0, sizeof(sb
));
1389 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH
);
1390 //sb.spt.PathId = 0;
1391 sb
.spt
.TargetId
= 1;
1393 sb
.spt
.CdbLength
= 10; sb
.spt
.SenseInfoLength
= 24;
1394 sb
.spt
.TimeOutValue
= 10;
1395 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
1396 size
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
1397 sb
.spt
.DataBufferOffset
= size
;
1400 if (datasize
> sizeof(sb
.ucDataBuf
)) {
1404 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
1405 sb
.spt
.DataTransferLength
= datasize
;
1407 sb
.ucDataBuf
[0] = magic
;
1410 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
1411 //sb.spt.DataTransferLength = 0;
1414 // Use pseudo SCSI command followed by registers
1415 sb
.spt
.Cdb
[0] = SCSIOP_ATA_PASSTHROUGH
;
1416 cdbregs
= (IDEREGS
*)(sb
.spt
.Cdb
+2);
1419 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_PASS_THROUGH
,
1420 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1421 long err
= GetLastError();
1423 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err
);
1424 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1428 // Cannot check ATA status, because command does not return IDEREGS
1430 // Check and copy data
1432 if ( num_out
!= size
1433 || (sb
.ucDataBuf
[0] == magic
&& !nonempty(sb
.ucDataBuf
+1, datasize
-1))) {
1434 if (ata_debugmode
) {
1435 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out
);
1436 print_ide_regs_io(regs
, NULL
);
1441 memcpy(data
, sb
.ucDataBuf
, datasize
);
1444 if (ata_debugmode
> 1) {
1445 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1446 print_ide_regs_io(regs
, NULL
);
1452 /////////////////////////////////////////////////////////////////////////////
1453 // SMART IOCTL via SCSI MINIPORT ioctl
1455 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
1456 // miniport driver (via SCSI port driver scsiport.sys).
1457 // It can be used to skip the missing or broken handling of some SMART
1458 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
1460 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
1463 DWORD code
= 0; const char * name
= 0;
1464 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
1465 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
1467 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
1468 case ATA_SMART_READ_VALUES
:
1469 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
1470 case ATA_SMART_READ_THRESHOLDS
:
1471 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
1472 case ATA_SMART_ENABLE
:
1473 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
1474 case ATA_SMART_DISABLE
:
1475 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
1476 case ATA_SMART_STATUS
:
1477 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
1478 case ATA_SMART_AUTOSAVE
:
1479 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
1480 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
1481 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
1482 case ATA_SMART_IMMEDIATE_OFFLINE
:
1483 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
1484 case ATA_SMART_AUTO_OFFLINE
:
1485 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
1486 case ATA_SMART_READ_LOG_SECTOR
:
1487 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
1488 case ATA_SMART_WRITE_LOG_SECTOR
:
1489 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
1498 SRB_IO_CONTROL srbc
;
1501 SENDCMDOUTPARAMS out
;
1505 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
1506 memset(&sb
, 0, sizeof(sb
));
1510 if (datasize
> (int)sizeof(sb
.space
)+1) {
1516 else if (datasize
< 0) {
1517 if (-datasize
> (int)sizeof(sb
.space
)+1) {
1522 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
1524 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1525 size
= sizeof(IDEREGS
);
1528 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1529 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
1530 sb
.srbc
.Timeout
= 60; // seconds
1531 sb
.srbc
.ControlCode
= code
;
1532 //sb.srbc.ReturnCode = 0;
1533 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
1534 sb
.params
.in
.irDriveRegs
= *regs
;
1535 sb
.params
.in
.cBufferSize
= size
;
1537 // Call miniport ioctl
1538 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
1540 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1541 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1542 long err
= GetLastError();
1543 if (ata_debugmode
) {
1544 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
1545 print_ide_regs_io(regs
, NULL
);
1547 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1552 if (sb
.srbc
.ReturnCode
) {
1553 if (ata_debugmode
) {
1554 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name
, sb
.srbc
.ReturnCode
);
1555 print_ide_regs_io(regs
, NULL
);
1561 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
1562 if (ata_debugmode
) {
1563 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1564 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
1565 print_ide_regs_io(regs
, NULL
);
1567 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1571 if (ata_debugmode
> 1) {
1572 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1573 num_out
, sb
.params
.out
.cBufferSize
);
1574 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
1575 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
1579 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
1580 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1581 memcpy(regs
, sb
.params
.out
.bBuffer
, sizeof(IDEREGS
));
1587 /////////////////////////////////////////////////////////////////////////////
1589 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1591 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1594 SRB_IO_CONTROL srbc
;
1598 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1600 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1604 memset(&sb
, 0, sizeof(sb
));
1605 strcpy((char *)sb
.srbc
.Signature
, "<3ware>");
1606 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1607 sb
.srbc
.Timeout
= 60; // seconds
1608 sb
.srbc
.ControlCode
= 0xA0000000;
1609 sb
.srbc
.ReturnCode
= 0;
1610 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1612 sb
.regs
.bReserved
= port
;
1615 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1616 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1617 long err
= GetLastError();
1618 if (ata_debugmode
) {
1619 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1620 print_ide_regs_io(regs
, NULL
);
1622 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1626 if (sb
.srbc
.ReturnCode
) {
1627 if (ata_debugmode
) {
1628 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb
.srbc
.ReturnCode
);
1629 print_ide_regs_io(regs
, NULL
);
1637 memcpy(data
, sb
.buffer
, datasize
);
1639 if (ata_debugmode
> 1) {
1640 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out
);
1641 print_ide_regs_io(regs
, &sb
.regs
);
1649 /////////////////////////////////////////////////////////////////////////////
1651 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1652 // 3DM/CLI "Rescan Controller" function does not to always update it.
1654 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1656 SRB_IO_CONTROL srbc
;
1657 memset(&srbc
, 0, sizeof(srbc
));
1658 strcpy((char *)srbc
.Signature
, "<3ware>");
1659 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1660 srbc
.Timeout
= 60; // seconds
1661 srbc
.ControlCode
= 0xCC010014;
1662 srbc
.ReturnCode
= 0;
1666 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1667 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1668 long err
= GetLastError();
1670 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1671 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1674 if (srbc
.ReturnCode
) {
1676 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc
.ReturnCode
);
1680 if (ata_debugmode
> 1)
1681 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1687 /////////////////////////////////////////////////////////////////////////////
1689 // Routines for pseudo device /dev/tw_cli/*
1690 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1693 // Get clipboard data
1695 static int get_clipboard(char * data
, int datasize
)
1697 if (!OpenClipboard(NULL
))
1699 HANDLE h
= GetClipboardData(CF_TEXT
);
1704 const void * p
= GlobalLock(h
);
1705 int n
= GlobalSize(h
);
1715 // Run a command, write stdout to dataout
1716 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
1718 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
1720 // Create stdout pipe
1721 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
1722 HANDLE pipe_out_w
, h
;
1723 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
1725 HANDLE self
= GetCurrentProcess();
1727 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
1728 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
1729 CloseHandle(pipe_out_w
);
1733 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
1734 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
1735 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1740 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
1741 si
.hStdInput
= INVALID_HANDLE_VALUE
;
1742 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
1743 si
.dwFlags
= STARTF_USESTDHANDLES
;
1744 PROCESS_INFORMATION pi
;
1746 NULL
, const_cast<char *>(cmd
),
1747 NULL
, NULL
, TRUE
/*inherit*/,
1748 CREATE_NO_WINDOW
/*do not create a new console window*/,
1749 NULL
, NULL
, &si
, &pi
)) {
1750 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1753 CloseHandle(pi
.hThread
);
1754 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
1756 // Copy stdout to output buffer
1758 while (i
< outsize
) {
1760 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
1764 CloseHandle(pipe_out_r
);
1766 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1767 CloseHandle(pi
.hProcess
);
1772 static const char * findstr(const char * str
, const char * sub
)
1774 const char * s
= strstr(str
, sub
);
1775 return (s
? s
+strlen(sub
) : "");
1779 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
1781 int srclen
= strcspn(src
, "\r\n");
1783 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
1784 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
1786 if (i
< destsize
-1 && i
< srclen
)
1791 // TODO: This is OS independent
1793 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1794 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
1795 m_ident_valid(false), m_smart_valid(false)
1797 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
1798 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
1802 bool win_tw_cli_device::is_open() const
1804 return (m_ident_valid
|| m_smart_valid
);
1808 bool win_tw_cli_device::open()
1810 m_ident_valid
= m_smart_valid
= false;
1811 const char * name
= skipdev(get_dev_name());
1812 // Read tw_cli or 3DM browser output into buffer
1814 int size
= -1, n1
= -1, n2
= -1;
1815 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
1816 size
= get_clipboard(buffer
, sizeof(buffer
));
1818 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
1819 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
1821 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
1822 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
1824 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
1825 if (ata_debugmode
> 1)
1826 pout("%s: Run: \"%s\"\n", name
, cmd
);
1827 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
1830 return set_err(EINVAL
);
1833 if (ata_debugmode
> 1)
1834 pout("%s: Read %d bytes\n", name
, size
);
1836 return set_err(ENOENT
);
1837 if (size
>= (int)sizeof(buffer
))
1838 return set_err(EIO
);
1841 if (ata_debugmode
> 1)
1842 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
1844 // Fake identify sector
1845 ASSERT_SIZEOF(ata_identify_device
, 512);
1846 ata_identify_device
* id
= &m_ident_buf
;
1847 memset(id
, 0, sizeof(*id
));
1848 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
1849 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
1850 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
1851 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
1852 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
1854 id
->words047_079
[49-47] = 0x0200; // size valid
1855 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
1856 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
1858 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1859 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1861 // Parse smart data hex dump
1862 const char * s
= findstr(buffer
, "Drive Smart Data:");
1864 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
1866 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
1868 const char * s1
= findstr(s
, "<td class"); // html version
1871 s
+= strcspn(s
, "\r\n");
1874 s
= buffer
; // try raw hex dump without header
1876 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
1879 unsigned x
= ~0; int n
= -1;
1880 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
1882 sd
[i
] = (unsigned char)x
;
1883 if (!(++i
< 512 && n
> 0))
1886 if (*s
== '<') // "<br>"
1887 s
+= strcspn(s
, "\r\n");
1890 if (!id
->model
[1]) {
1891 // No useful data found
1892 char * err
= strstr(buffer
, "Error:");
1894 err
= strstr(buffer
, "error :");
1895 if (err
&& (err
= strchr(err
, ':'))) {
1896 // Show tw_cli error message
1898 err
[strcspn(err
, "\r\n")] = 0;
1899 return set_err(EIO
, err
);
1901 return set_err(EIO
);
1906 m_ident_valid
= true;
1907 m_smart_valid
= !!sd
;
1912 bool win_tw_cli_device::close()
1914 m_ident_valid
= m_smart_valid
= false;
1919 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
1925 memcpy(data
, &m_ident_buf
, 512);
1930 memcpy(data
, &m_smart_buf
, 512);
1934 case STATUS_CHECK
: // Fake "good" SMART status
1939 // Arrive here for all unsupported commands
1945 /////////////////////////////////////////////////////////////////////////////
1946 // IOCTL_STORAGE_QUERY_PROPERTY
1948 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1949 STORAGE_DEVICE_DESCRIPTOR desc
;
1953 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1954 // (This works without admin rights)
1956 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1958 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
1959 memset(data
, 0, sizeof(*data
));
1962 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1963 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1964 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
1965 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
1970 if (ata_debugmode
> 1 || scsi_debugmode
> 1) {
1971 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1973 " Product: \"%s\"\n"
1974 " Revision: \"%s\"\n"
1976 " BusType: 0x%02x\n",
1977 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: "(null)"),
1978 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: "(null)"),
1979 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: "(null)"),
1980 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1987 /////////////////////////////////////////////////////////////////////////////
1988 // IOCTL_STORAGE_PREDICT_FAILURE
1990 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1991 // or -1 on error, opionally return VendorSpecific data.
1992 // (This works without admin rights)
1994 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1996 STORAGE_PREDICT_FAILURE pred
;
1997 memset(&pred
, 0, sizeof(pred
));
2000 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
2001 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
2002 if (ata_debugmode
> 1)
2003 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
2008 if (ata_debugmode
> 1) {
2009 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
2010 " PredictFailure: 0x%08lx\n"
2011 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
2012 pred
.PredictFailure
,
2013 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
2014 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
2018 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
2019 return (!pred
.PredictFailure
? 0 : 1);
2023 /////////////////////////////////////////////////////////////////////////////
2025 // get DEV_* for open handle
2026 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
2028 // Try SMART_GET_VERSION first to detect ATA SMART support
2029 // for drivers reporting BusTypeScsi (3ware)
2030 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
2033 // Get BusType from device descriptor
2034 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
2035 if (storage_query_property_ioctl(hdevice
, &data
))
2038 // Newer BusType* values are missing in older includes
2039 switch ((int)data
.desc
.BusType
) {
2041 case 0x0b: // BusTypeSata
2043 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
2046 case 0x09: // BusTypeiScsi
2047 case 0x0a: // BusTypeSas
2057 // get DEV_* for device path
2058 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
2061 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
2062 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2063 if (h
== INVALID_HANDLE_VALUE
) {
2065 h
= CreateFileA(path
, 0,
2066 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2067 if (h
== INVALID_HANDLE_VALUE
)
2070 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
2071 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
2072 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
2077 // get DEV_* for physical drive number
2078 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
2081 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
2082 return get_controller_type(path
, ata_version_ex
);
2085 static win_dev_type
get_phy_drive_type(int drive
)
2087 return get_phy_drive_type(drive
, 0);
2090 // get DEV_* for logical drive number
2091 static win_dev_type
get_log_drive_type(int drive
)
2094 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
2095 return get_controller_type(path
);
2098 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
2099 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
2101 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
2102 if (storage_query_property_ioctl(hdevice
, &data
))
2105 memset(id
, 0, sizeof(*id
));
2107 // Some drivers split ATA model string into VendorId and ProductId,
2108 // others return it as ProductId only.
2109 char model
[sizeof(id
->model
) + 1] = "";
2112 if (data
.desc
.VendorIdOffset
) {
2113 for ( ;i
< sizeof(model
)-1 && data
.raw
[data
.desc
.VendorIdOffset
+i
]; i
++)
2114 model
[i
] = data
.raw
[data
.desc
.VendorIdOffset
+i
];
2117 if (data
.desc
.ProductIdOffset
) {
2118 while (i
> 1 && model
[i
-2] == ' ') // Keep last blank from VendorId
2120 // Ignore VendorId "ATA"
2121 if (i
<= 4 && !strncmp(model
, "ATA", 3) && (i
== 3 || model
[3] == ' '))
2123 for (unsigned j
= 0; i
< sizeof(model
)-1 && data
.raw
[data
.desc
.ProductIdOffset
+j
]; i
++, j
++)
2124 model
[i
] = data
.raw
[data
.desc
.ProductIdOffset
+j
];
2127 while (i
> 0 && model
[i
-1] == ' ')
2130 copy_swapped(id
->model
, model
, sizeof(id
->model
));
2132 if (data
.desc
.ProductRevisionOffset
)
2133 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
2135 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2136 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2141 /////////////////////////////////////////////////////////////////////////////
2142 // USB ID detection using WMI
2144 // Return true if STR starts with PREFIX.
2145 static inline bool str_starts_with(const std::string
& str
, const char * prefix
)
2147 return !strncmp(str
.c_str(), prefix
, strlen(prefix
));
2150 // Get USB ID for a physical drive number
2151 static bool get_usb_id(int drive
, unsigned short & vendor_id
, unsigned short & product_id
)
2153 bool debug
= (scsi_debugmode
> 1);
2156 if (!ws
.connect()) {
2158 pout("WMI connect failed\n");
2164 if (!ws
.query1(wo
, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive
))
2167 std::string name
= wo
.get_str("Model");
2169 pout("PhysicalDrive%d, \"%s\":\n", drive
, name
.c_str());
2171 // Get USB_CONTROLLER -> DEVICE associations
2173 if (!ws
.query(we
, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
2176 std::string usb_devid
;
2177 std::string prev_usb_ant
, prev_usb_devid
;
2178 std::string prev_ant
, ant
, dep
;
2180 const regular_expression
regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED
);
2182 while (we
.next(wo
)) {
2184 // Find next 'USB_CONTROLLER, DEVICE' pair
2185 ant
= wo
.get_str("Antecedent");
2186 dep
= wo
.get_str("Dependent");
2188 if (debug
&& ant
!= prev_ant
)
2189 pout(" %s:\n", ant
.c_str());
2192 regmatch_t match
[2];
2193 if (!(regex
.execute(dep
.c_str(), 2, match
) && match
[1].rm_so
>= 0)) {
2195 pout(" | (\"%s\")\n", dep
.c_str());
2199 std::string
devid(dep
.c_str()+match
[1].rm_so
, match
[1].rm_eo
-match
[1].rm_so
);
2201 if (str_starts_with(devid
, "USB\\\\VID_")) {
2202 // USB bridge entry, save CONTROLLER, ID
2204 pout(" +-> \"%s\"\n", devid
.c_str());
2206 prev_usb_devid
= devid
;
2209 else if (str_starts_with(devid
, "USBSTOR\\\\")) {
2210 // USBSTOR device found
2212 pout(" +--> \"%s\"\n", devid
.c_str());
2216 if (!ws
.query1(wo2
, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid
.c_str()))
2218 std::string name2
= wo2
.get_str("Name");
2220 // Continue if not name of physical disk drive
2221 if (name2
!= name
) {
2223 pout(" | (Name: \"%s\")\n", name2
.c_str());
2227 pout(" | Name: \"%s\"\n", name2
.c_str());
2229 // Fail if previos USB bridge is associated to other controller
2230 if (ant
!= prev_usb_ant
)
2233 // Handle multiple devices with same name
2234 if (!usb_devid
.empty()) {
2235 // Fail if multiple devices with same name have different USB bridge types
2236 if (usb_devid
!= prev_usb_devid
)
2242 usb_devid
= prev_usb_devid
;
2244 // Continue to check for duplicate names ...
2248 pout(" | \"%s\"\n", devid
.c_str());
2254 if (!(sscanf(usb_devid
.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
2255 &vendor_id
, &product_id
, &nc
) == 2 && nc
== 9+4+5+4))
2259 pout("USB ID = 0x%04x:0x%04x\n", vendor_id
, product_id
);
2264 /////////////////////////////////////////////////////////////////////////////
2266 // Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
2267 // returns: 1=active, 0=standby, -1=error
2268 // (This would also work for SCSI drives)
2270 static int get_device_power_state(HANDLE hdevice
)
2272 static bool unsupported
= false;
2279 static DWORD kernel_dll_pid
= 0;
2281 static BOOL (WINAPI
* GetDevicePowerState_p
)(HANDLE
, BOOL
*) = 0;
2283 if (!GetDevicePowerState_p
2285 || kernel_dll_pid
!= GetCurrentProcessId() // detect fork()
2288 if (!(GetDevicePowerState_p
= (BOOL (WINAPI
*)(HANDLE
, BOOL
*))
2289 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) {
2291 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
2297 kernel_dll_pid
= GetCurrentProcessId();
2302 if (!GetDevicePowerState_p(hdevice
, &state
)) {
2303 long err
= GetLastError();
2305 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
2306 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
2307 // TODO: This may not work as expected on transient errors,
2308 // because smartd interprets -1 as SLEEP mode regardless of errno.
2312 if (ata_debugmode
> 1)
2313 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
2318 /////////////////////////////////////////////////////////////////////////////
2321 // Print SMARTVSD error message, return errno
2323 static int smartvsd_error()
2325 char path
[MAX_PATH
];
2327 if (!(5 <= (len
= GetSystemDirectoryA(path
, MAX_PATH
)) && len
< MAX_PATH
/2))
2329 // SMARTVSD.VXD present?
2330 strcpy(path
+len
, "\\IOSUBSYS\\SMARTVSD.VXD");
2331 if (!access(path
, 0)) {
2332 // Yes, standard IDE driver used?
2334 if ( (h
= CreateFileA("\\\\.\\ESDI_506",
2335 GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2336 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
2337 && GetLastError() == ERROR_FILE_NOT_FOUND
) {
2338 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
2342 if (h
!= INVALID_HANDLE_VALUE
) // should not happen
2344 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
2349 strcpy(path
+len
, "\\SMARTVSD.VXD");
2350 if (!access(path
, 0)) {
2351 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
2352 // (http://support.microsoft.com/kb/265854/en-us).
2354 pout("SMART driver is not properly installed,\n"
2355 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
2356 " and reboot Windows.\n", path
, path
);
2359 // Some Windows versions do not provide SMARTVSD.VXD
2360 // (http://support.microsoft.com/kb/199886/en-us).
2362 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path
);
2368 #endif // WIN9X_SUPPORT
2370 // Get default ATA device options
2372 static const char * ata_get_def_options()
2374 DWORD ver
= GetVersion();
2375 if ((ver
& 0x80000000) || (ver
& 0xff) < 4) // Win9x/ME
2376 return "s"; // SMART_* only
2377 else if ((ver
& 0xff) == 4) // WinNT4
2378 return "sc"; // SMART_*, SCSI_PASS_THROUGH
2379 else // WinXP, 2003, Vista
2380 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
2381 // STORAGE_*, SCSI_MINIPORT_*
2385 // Common routines for devices with HANDLEs
2387 win_smart_device::~win_smart_device() throw()
2389 if (m_fh
!= INVALID_HANDLE_VALUE
)
2390 ::CloseHandle(m_fh
);
2393 bool win_smart_device::is_open() const
2395 return (m_fh
!= INVALID_HANDLE_VALUE
);
2398 bool win_smart_device::close()
2400 if (m_fh
== INVALID_HANDLE_VALUE
)
2402 BOOL rc
= ::CloseHandle(m_fh
);
2403 m_fh
= INVALID_HANDLE_VALUE
;
2409 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2410 : smart_device(intf
, dev_name
, "ata", req_type
),
2411 m_usr_options(false),
2413 m_id_is_cached(false),
2421 win_ata_device::~win_ata_device() throw()
2428 bool win_ata_device::open()
2430 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2431 // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
2432 char drive
[1+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
2433 if ( sscanf(name
, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive
, &n1
, options
, &n2
) >= 1
2434 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2435 return open(drive
[0] - 'a', -1, options
, -1);
2437 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
2438 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
2440 if ( sscanf(name
, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
2441 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2442 return open(drive
[0] - 'a', -1, options
, port
);
2444 // pd<m>,N => Physical drive <m>, RAID port N
2445 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
2446 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
2447 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
2448 return open(phydrive
, -1, "", (int)port
);
2450 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2451 int logdrive
= drive_letter(name
);
2452 if (logdrive
>= 0) {
2453 return open(-1, logdrive
, "", -1);
2456 return set_err(EINVAL
);
2460 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
)
2462 // path depends on Windows Version
2464 if (win9x
&& 0 <= phydrive
&& phydrive
<= 7)
2465 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
2466 strcpy(devpath
, (phydrive
<= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
2467 else if (!win9x
&& 0 <= phydrive
&& phydrive
<= 255)
2468 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", phydrive
);
2469 else if (!win9x
&& 0 <= logdrive
&& logdrive
<= 'Z'-'A')
2470 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
2472 return set_err(ENOENT
);
2475 HANDLE h
= INVALID_HANDLE_VALUE
;
2476 if (win9x
|| !(*options
&& !options
[strspn(options
, "fp")])) {
2477 // Open with admin rights
2479 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2480 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2481 NULL
, OPEN_EXISTING
, 0, 0);
2483 if (!win9x
&& h
== INVALID_HANDLE_VALUE
) {
2484 // Open without admin rights
2486 h
= CreateFileA(devpath
, 0,
2487 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2488 NULL
, OPEN_EXISTING
, 0, 0);
2490 if (h
== INVALID_HANDLE_VALUE
) {
2491 long err
= GetLastError();
2493 if (win9x
&& phydrive
<= 3 && err
== ERROR_FILE_NOT_FOUND
)
2496 if (err
== ERROR_FILE_NOT_FOUND
)
2497 set_err(ENOENT
, "%s: not found", devpath
);
2498 else if (err
== ERROR_ACCESS_DENIED
)
2499 set_err(EACCES
, "%s: access denied", devpath
);
2501 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2506 // Warn once if admin rights are missing
2508 static bool noadmin_warning
= false;
2509 if (!noadmin_warning
) {
2510 pout("Warning: Limited functionality due to missing admin rights\n");
2511 noadmin_warning
= true;
2515 if (ata_debugmode
> 1)
2516 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
2518 m_usr_options
= false;
2520 // Save user options
2521 m_options
= options
; m_usr_options
= true;
2524 // RAID: SMART_* and SCSI_MINIPORT
2527 // Set default options according to Windows version
2528 static const char * def_options
= ata_get_def_options();
2529 m_options
= def_options
;
2532 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
2533 m_drive
= 0; m_port
= port
;
2534 if (!win9x
&& port
< 0)
2537 // Win9X/ME: Get drive map
2538 // RAID: Get port map
2539 GETVERSIONINPARAMS_EX vers_ex
;
2540 int devmap
= smart_get_version(h
, &vers_ex
);
2542 // 3ware RAID if vendor id present
2543 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
2545 unsigned long portmap
= 0;
2546 if (port
>= 0 && devmap
>= 0) {
2547 // 3ware RAID: check vendor id
2549 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
2550 "This is no 3ware 9000 controller or driver has no SMART support.\n",
2551 vers_ex
.wIdentifier
);
2555 portmap
= vers_ex
.dwDeviceMapEx
;
2558 pout("%s: ATA driver has no SMART support\n", devpath
);
2559 if (!is_permissive()) {
2561 return set_err(ENOSYS
);
2565 m_smartver_state
= 1;
2568 // 3ware RAID: update devicemap first
2570 if (!update_3ware_devicemap_ioctl(h
)) {
2571 if ( smart_get_version(h
, &vers_ex
) >= 0
2572 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
2573 portmap
= vers_ex
.dwDeviceMapEx
;
2575 // Check port existence
2576 if (!(portmap
& (1L << port
))) {
2577 if (!is_permissive()) {
2579 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
2585 // Win9x/ME: Check device presence & type
2586 if (((devmap
>> (phydrive
& 0x3)) & 0x11) != 0x01) {
2587 unsigned char atapi
= (devmap
>> (phydrive
& 0x3)) & 0x10;
2588 // Win9x drive existence check may not work as expected
2589 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
2590 // (The related KB Article Q196120 is no longer available)
2591 if (!is_permissive()) {
2593 return set_err((atapi
? ENOSYS
: ENOENT
), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
2594 devpath
, phydrive
, (atapi
?"is an ATAPI device":"does not exist"), devmap
);
2597 // Drive number must be passed to ioctl
2598 m_drive
= (phydrive
& 0x3);
2605 // Scan for ATA drives on Win9x/ME
2607 bool win9x_smart_interface::ata_scan(smart_device_list
& devlist
)
2610 const char devpath
[] = "\\\\.\\SMARTVSD";
2611 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2612 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0);
2613 if (h
== INVALID_HANDLE_VALUE
) {
2614 if (ata_debugmode
> 1)
2615 pout(" %s: Open failed, Error=%ld\n", devpath
, GetLastError());
2616 return true; // SMARTVSD.VXD missing or no ATA devices
2620 int devmap
= smart_get_version(h
);
2623 return true; // Should not happen
2625 // Check ATA device presence, remove ATAPI devices
2626 devmap
= (devmap
& 0xf) & ~((devmap
>> 4) & 0xf);
2628 for (int i
= 0; i
< 4; i
++) {
2629 if (!(devmap
& (1 << i
)))
2631 sprintf(name
, "/dev/hd%c", 'a'+i
);
2632 devlist
.push_back( new win_ata_device(this, name
, "ata") );
2637 #endif // WIN9X_SUPPORT
2640 /////////////////////////////////////////////////////////////////////////////
2642 // Interface to ATA devices
2643 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2645 // No multi-sector support for now, see above
2646 // warning about IOCTL_ATA_PASS_THROUGH
2647 if (!ata_cmd_is_ok(in
,
2648 true, // data_out_support
2649 false, // !multi_sector_support
2650 true) // ata_48bit_support
2654 // 3ware RAID: SMART DISABLE without port number disables SMART functions
2655 if ( m_is_3ware
&& m_port
< 0
2656 && in
.in_regs
.command
== ATA_SMART_CMD
2657 && in
.in_regs
.features
== ATA_SMART_DISABLE
)
2658 return set_err(ENOSYS
, "SMART DISABLE requires 3ware port number");
2660 // Determine ioctl functions valid for this ATA cmd
2661 const char * valid_options
= 0;
2663 switch (in
.in_regs
.command
) {
2664 case ATA_IDENTIFY_DEVICE
:
2665 case ATA_IDENTIFY_PACKET_DEVICE
:
2666 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2667 // and SCSI_MINIPORT_* if requested by user
2668 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2671 case ATA_CHECK_POWER_MODE
:
2672 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
2673 valid_options
= "pai3";
2677 switch (in
.in_regs
.features
) {
2678 case ATA_SMART_READ_VALUES
:
2679 case ATA_SMART_READ_THRESHOLDS
:
2680 case ATA_SMART_AUTOSAVE
:
2681 case ATA_SMART_ENABLE
:
2682 case ATA_SMART_DISABLE
:
2683 case ATA_SMART_AUTO_OFFLINE
:
2684 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2685 // and SCSI_MINIPORT_* if requested by user
2686 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2689 case ATA_SMART_IMMEDIATE_OFFLINE
:
2690 // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
2691 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ || win9x
?
2692 "saicm3" : "aicm3");
2695 case ATA_SMART_READ_LOG_SECTOR
:
2696 // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
2697 // Try SCSI_MINIPORT also to skip buggy class driver
2698 // SMART functions do not support multi sector I/O.
2700 valid_options
= (m_usr_options
|| win9x
? "saicm3" : "aicm3");
2702 valid_options
= "a";
2705 case ATA_SMART_WRITE_LOG_SECTOR
:
2706 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
2707 // but SCSI_MINIPORT_* only if requested by user and single sector.
2708 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
2711 case ATA_SMART_STATUS
:
2712 // May require lba_mid,lba_high register return
2713 if (in
.out_needed
.is_set())
2714 valid_options
= (m_usr_options
? "saimf" : "saif");
2716 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2720 // Unknown SMART command, handle below
2726 // Other ATA command, handle below
2730 if (!valid_options
) {
2731 // No special ATA command found above, select a generic pass through ioctl.
2732 if (!( in
.direction
== ata_cmd_in::no_data
2733 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
2734 || in
.in_regs
.is_48bit_cmd() )
2735 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
2736 valid_options
= "a";
2737 else if (in
.out_needed
.is_set())
2738 // Need output registers: ATA/IDE_PASS_THROUGH
2739 valid_options
= "ai";
2741 valid_options
= "aic";
2745 // Restrict to IOCTL_STORAGE_*
2746 if (strchr(valid_options
, 'f'))
2747 valid_options
= "f";
2748 else if (strchr(valid_options
, 'p'))
2749 valid_options
= "p";
2751 return set_err(ENOSYS
, "Function requires admin rights");
2755 IDEREGS regs
, prev_regs
;
2757 const ata_in_regs
& lo
= in
.in_regs
;
2758 regs
.bFeaturesReg
= lo
.features
;
2759 regs
.bSectorCountReg
= lo
.sector_count
;
2760 regs
.bSectorNumberReg
= lo
.lba_low
;
2761 regs
.bCylLowReg
= lo
.lba_mid
;
2762 regs
.bCylHighReg
= lo
.lba_high
;
2763 regs
.bDriveHeadReg
= lo
.device
;
2764 regs
.bCommandReg
= lo
.command
;
2767 if (in
.in_regs
.is_48bit_cmd()) {
2768 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2769 prev_regs
.bFeaturesReg
= hi
.features
;
2770 prev_regs
.bSectorCountReg
= hi
.sector_count
;
2771 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
2772 prev_regs
.bCylLowReg
= hi
.lba_mid
;
2773 prev_regs
.bCylHighReg
= hi
.lba_high
;
2774 prev_regs
.bDriveHeadReg
= hi
.device
;
2775 prev_regs
.bCommandReg
= hi
.command
;
2776 prev_regs
.bReserved
= 0;
2779 // Set data direction
2782 switch (in
.direction
) {
2783 case ata_cmd_in::no_data
:
2785 case ata_cmd_in::data_in
:
2786 datasize
= (int)in
.size
;
2787 data
= (char *)in
.buffer
;
2789 case ata_cmd_in::data_out
:
2790 datasize
= -(int)in
.size
;
2791 data
= (char *)in
.buffer
;
2794 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
2799 // Try all valid ioctls in the order specified in m_options
2800 bool powered_up
= false;
2801 bool out_regs_set
= false;
2802 bool id_is_cached
= false;
2803 const char * options
= m_options
.c_str();
2805 for (int i
= 0; ; i
++) {
2806 char opt
= options
[i
];
2809 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
2810 // Power up reported by GetDevicePowerState() and no ioctl available
2811 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
2812 regs
.bSectorCountReg
= 0xff;
2813 out_regs_set
= true;
2817 return set_err(ENOSYS
);
2819 if (!strchr(valid_options
, opt
))
2820 // Invalid for this command
2824 assert( datasize
== 0 || datasize
== 512
2825 || (datasize
== -512 && strchr("am", opt
))
2826 || (datasize
> 512 && opt
== 'a'));
2831 // call SMART_GET_VERSION once for each drive
2832 if (m_smartver_state
> 1) {
2833 rc
= -1; errno
= ENOSYS
;
2836 if (!m_smartver_state
) {
2837 assert(m_port
== -1);
2838 GETVERSIONINPARAMS_EX vers_ex
;
2839 if (smart_get_version(get_fh(), &vers_ex
) < 0) {
2840 if (!failuretest_permissive
) {
2841 m_smartver_state
= 2;
2842 rc
= -1; errno
= ENOSYS
;
2845 failuretest_permissive
--;
2848 // 3ware RAID if vendor id present
2849 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
2852 m_smartver_state
= 1;
2854 rc
= smart_ioctl(get_fh(), m_drive
, ®s
, data
, datasize
, m_port
);
2855 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
2856 id_is_cached
= (m_port
< 0 && !win9x
); // Not cached by 3ware or Win9x/ME driver
2859 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
2860 id_is_cached
= (m_port
< 0 && !win9x
);
2863 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
2864 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
2866 out_regs_set
= true;
2869 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
2870 out_regs_set
= true;
2873 rc
= ata_via_scsi_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
2876 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
2877 rc
= get_identify_from_device_property(get_fh(), (ata_identify_device
*)data
);
2878 id_is_cached
= true;
2880 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
2881 case ATA_SMART_READ_VALUES
:
2882 rc
= storage_predict_failure_ioctl(get_fh(), data
);
2886 case ATA_SMART_ENABLE
:
2889 case ATA_SMART_STATUS
:
2890 rc
= storage_predict_failure_ioctl(get_fh());
2892 // Good SMART status
2893 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
2897 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
2902 errno
= ENOSYS
; rc
= -1;
2905 errno
= ENOSYS
; rc
= -1;
2909 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
2910 out_regs_set
= true;
2913 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
2914 rc
= get_device_power_state(get_fh());
2916 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
2917 // spin up the drive => simulate ATA result STANDBY.
2918 regs
.bSectorCountReg
= 0x00;
2919 out_regs_set
= true;
2922 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
2923 // only if it is selected by the device driver => try a passthrough ioctl to get the
2924 // actual mode, if none available simulate ACTIVE/IDLE.
2926 rc
= -1; errno
= ENOSYS
;
2932 // Working ioctl found
2935 if (errno
!= ENOSYS
)
2936 // Abort on I/O error
2937 return set_err(errno
);
2939 out_regs_set
= false;
2940 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
2943 // Return IDEREGS if set
2945 ata_out_regs
& lo
= out
.out_regs
;
2946 lo
.error
= regs
.bFeaturesReg
;
2947 lo
.sector_count
= regs
.bSectorCountReg
;
2948 lo
.lba_low
= regs
.bSectorNumberReg
;
2949 lo
.lba_mid
= regs
.bCylLowReg
;
2950 lo
.lba_high
= regs
.bCylHighReg
;
2951 lo
.device
= regs
.bDriveHeadReg
;
2952 lo
.status
= regs
.bCommandReg
;
2953 if (in
.in_regs
.is_48bit_cmd()) {
2954 ata_out_regs
& hi
= out
.out_regs
.prev
;
2955 hi
.sector_count
= prev_regs
.bSectorCountReg
;
2956 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
2957 hi
.lba_mid
= prev_regs
.bCylLowReg
;
2958 hi
.lba_high
= prev_regs
.bCylHighReg
;
2962 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
2963 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
2964 // Update ata_identify_is_cached() result according to ioctl used.
2965 m_id_is_cached
= id_is_cached
;
2970 // Return true if OS caches the ATA identify sector
2971 bool win_ata_device::ata_identify_is_cached() const
2973 return m_id_is_cached
;
2977 //////////////////////////////////////////////////////////////////////
2980 bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
)
2982 // Get driver info to check CSMI support
2983 CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf
;
2984 memset(&driver_info_buf
, 0, sizeof(driver_info_buf
));
2985 if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO
, &driver_info_buf
.IoctlHeader
, sizeof(driver_info_buf
)))
2988 if (scsi_debugmode
> 1) {
2989 const CSMI_SAS_DRIVER_INFO
& driver_info
= driver_info_buf
.Information
;
2990 pout("CSMI_SAS_DRIVER_INFO:\n");
2991 pout(" Name: \"%.81s\"\n", driver_info
.szName
);
2992 pout(" Description: \"%.81s\"\n", driver_info
.szDescription
);
2993 pout(" Revision: %d.%d\n", driver_info
.usMajorRevision
, driver_info
.usMinorRevision
);
2997 CSMI_SAS_PHY_INFO_BUFFER phy_info_buf
;
2998 memset(&phy_info_buf
, 0, sizeof(phy_info_buf
));
2999 if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO
, &phy_info_buf
.IoctlHeader
, sizeof(phy_info_buf
)))
3002 phy_info
= phy_info_buf
.Information
;
3003 if (phy_info
.bNumberOfPhys
> sizeof(phy_info
.Phy
)/sizeof(phy_info
.Phy
[0]))
3004 return set_err(EIO
, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info
.bNumberOfPhys
);
3006 if (scsi_debugmode
> 1) {
3007 pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info
.bNumberOfPhys
);
3008 for (int i
= 0; i
< phy_info
.bNumberOfPhys
; i
++) {
3009 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
3010 const CSMI_SAS_IDENTIFY
& id
= pe
.Identify
, & at
= pe
.Attached
;
3011 pout("Phy[%d] Port: 0x%02x\n", i
, pe
.bPortIdentifier
);
3012 pout(" Type: 0x%02x, 0x%02x\n", id
.bDeviceType
, at
.bDeviceType
);
3013 pout(" InitProto: 0x%02x, 0x%02x\n", id
.bInitiatorPortProtocol
, at
.bInitiatorPortProtocol
);
3014 pout(" TargetProto: 0x%02x, 0x%02x\n", id
.bTargetPortProtocol
, at
.bTargetPortProtocol
);
3015 pout(" PhyIdent: 0x%02x, 0x%02x\n", id
.bPhyIdentifier
, at
.bPhyIdentifier
);
3016 const unsigned char * b
= id
.bSASAddress
;
3017 pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ",
3018 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
3020 pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
3021 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
3028 bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO
& phy_info
, unsigned phy_no
)
3030 // Check Phy presence
3031 if (phy_no
>= phy_info
.bNumberOfPhys
)
3032 return set_err(ENOENT
, "Port %u does not exist (#ports: %d)", phy_no
,
3033 phy_info
.bNumberOfPhys
);
3035 const CSMI_SAS_PHY_ENTITY
& phy_ent
= phy_info
.Phy
[phy_no
];
3036 if (phy_ent
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
3037 return set_err(ENOENT
, "No device on port %u", phy_no
);
3039 switch (phy_ent
.Attached
.bTargetPortProtocol
) {
3040 case CSMI_SAS_PROTOCOL_SATA
:
3041 case CSMI_SAS_PROTOCOL_STP
:
3044 return set_err(ENOENT
, "No SATA device on port %u (protocol: %u)",
3045 phy_no
, phy_ent
.Attached
.bTargetPortProtocol
);
3051 bool csmi_device::select_phy(unsigned phy_no
)
3053 CSMI_SAS_PHY_INFO phy_info
;
3054 if (!get_phy_info(phy_info
))
3058 if (!check_phy(phy_info
, phy_no
))
3061 m_phy_ent
= phy_info
.Phy
[phy_no
];
3066 bool csmi_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
3068 if (!ata_cmd_is_ok(in
,
3069 true, // data_out_support
3070 true, // multi_sector_support
3071 true) // ata_48bit_support
3075 // Create buffer with appropriate size
3076 raw_buffer
pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER
) + in
.size
);
3077 CSMI_SAS_STP_PASSTHRU_BUFFER
* pthru_buf
= (CSMI_SAS_STP_PASSTHRU_BUFFER
*)pthru_raw_buf
.data();
3079 // Set addresses from Phy info
3080 CSMI_SAS_STP_PASSTHRU
& pthru
= pthru_buf
->Parameters
;
3081 const CSMI_SAS_PHY_ENTITY
& phy_ent
= get_phy_ent();
3082 pthru
.bPhyIdentifier
= phy_ent
.Identify
.bPhyIdentifier
;
3083 pthru
.bPortIdentifier
= phy_ent
.bPortIdentifier
;
3084 memcpy(pthru
.bDestinationSASAddress
, phy_ent
.Attached
.bSASAddress
,
3085 sizeof(pthru
.bDestinationSASAddress
));
3086 pthru
.bConnectionRate
= CSMI_SAS_LINK_RATE_NEGOTIATED
;
3088 // Set transfer mode
3089 switch (in
.direction
) {
3090 case ata_cmd_in::no_data
:
3091 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_UNSPECIFIED
;
3093 case ata_cmd_in::data_in
:
3094 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_READ
;
3095 pthru
.uDataLength
= in
.size
;
3097 case ata_cmd_in::data_out
:
3098 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_WRITE
;
3099 pthru
.uDataLength
= in
.size
;
3100 memcpy(pthru_buf
->bDataBuffer
, in
.buffer
, in
.size
);
3103 return set_err(EINVAL
, "csmi_ata_device::ata_pass_through: invalid direction=%d",
3107 // Set host-to-device FIS
3109 unsigned char * fis
= pthru
.bCommandFIS
;
3110 const ata_in_regs
& lo
= in
.in_regs
;
3111 const ata_in_regs
& hi
= in
.in_regs
.prev
;
3112 fis
[ 0] = 0x27; // Type: host-to-device FIS
3113 fis
[ 1] = 0x80; // Bit7: Update command register
3114 fis
[ 2] = lo
.command
;
3115 fis
[ 3] = lo
.features
;
3116 fis
[ 4] = lo
.lba_low
;
3117 fis
[ 5] = lo
.lba_mid
;
3118 fis
[ 6] = lo
.lba_high
;
3119 fis
[ 7] = lo
.device
;
3120 fis
[ 8] = hi
.lba_low
;
3121 fis
[ 9] = hi
.lba_mid
;
3122 fis
[10] = hi
.lba_high
;
3123 fis
[11] = hi
.features
;
3124 fis
[12] = lo
.sector_count
;
3125 fis
[13] = hi
.sector_count
;
3129 if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU
, &pthru_buf
->IoctlHeader
, pthru_raw_buf
.size())) {
3133 // Get device-to-host FIS
3135 const unsigned char * fis
= pthru_buf
->Status
.bStatusFIS
;
3136 ata_out_regs
& lo
= out
.out_regs
;
3137 lo
.status
= fis
[ 2];
3139 lo
.lba_low
= fis
[ 4];
3140 lo
.lba_mid
= fis
[ 5];
3141 lo
.lba_high
= fis
[ 6];
3142 lo
.device
= fis
[ 7];
3143 lo
.sector_count
= fis
[12];
3144 if (in
.in_regs
.is_48bit_cmd()) {
3145 ata_out_regs
& hi
= out
.out_regs
.prev
;
3146 hi
.lba_low
= fis
[ 8];
3147 hi
.lba_mid
= fis
[ 9];
3148 hi
.lba_high
= fis
[10];
3149 hi
.sector_count
= fis
[13];
3154 if (in
.direction
== ata_cmd_in::data_in
)
3155 // TODO: Check ptru_buf->Status.uDataBytes
3156 memcpy(in
.buffer
, pthru_buf
->bDataBuffer
, in
.size
);
3162 //////////////////////////////////////////////////////////////////////
3165 win_csmi_device::win_csmi_device(smart_interface
* intf
, const char * dev_name
,
3166 const char * req_type
)
3167 : smart_device(intf
, dev_name
, "ata", req_type
),
3168 m_fh(INVALID_HANDLE_VALUE
), m_phy_no(0)
3172 win_csmi_device::~win_csmi_device() throw()
3174 if (m_fh
!= INVALID_HANDLE_VALUE
)
3178 bool win_csmi_device::is_open() const
3180 return (m_fh
!= INVALID_HANDLE_VALUE
);
3183 bool win_csmi_device::close()
3185 if (m_fh
== INVALID_HANDLE_VALUE
)
3187 BOOL rc
= CloseHandle(m_fh
);
3188 m_fh
= INVALID_HANDLE_VALUE
;
3193 bool win_csmi_device::open_scsi()
3196 unsigned contr_no
= ~0, phy_no
= ~0; int nc
= -1;
3197 const char * name
= skipdev(get_dev_name());
3198 if (!( sscanf(name
, "csmi%u,%u%n", &contr_no
, &phy_no
, &nc
) >= 0
3199 && nc
== (int)strlen(name
) && contr_no
<= 9 && phy_no
< 32) )
3200 return set_err(EINVAL
);
3202 // Open controller handle
3204 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%u:", contr_no
);
3206 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
3207 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3208 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
3210 if (h
== INVALID_HANDLE_VALUE
) {
3211 long err
= GetLastError();
3212 if (err
== ERROR_FILE_NOT_FOUND
)
3213 set_err(ENOENT
, "%s: not found", devpath
);
3214 else if (err
== ERROR_ACCESS_DENIED
)
3215 set_err(EACCES
, "%s: access denied", devpath
);
3217 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3221 if (scsi_debugmode
> 1)
3222 pout(" %s: successfully opened\n", devpath
);
3230 bool win_csmi_device::open()
3235 // Get Phy info for this drive
3236 if (!select_phy(m_phy_no
)) {
3245 bool win_csmi_device::csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
3246 unsigned csmi_bufsiz
)
3248 // Determine signature
3251 case CC_CSMI_SAS_GET_DRIVER_INFO
:
3252 sig
= CSMI_ALL_SIGNATURE
; break;
3253 case CC_CSMI_SAS_GET_PHY_INFO
:
3254 case CC_CSMI_SAS_STP_PASSTHRU
:
3255 sig
= CSMI_SAS_SIGNATURE
; break;
3257 return set_err(ENOSYS
, "Unknown CSMI code=%u", code
);
3261 csmi_buffer
->HeaderLength
= sizeof(IOCTL_HEADER
);
3262 strncpy((char *)csmi_buffer
->Signature
, sig
, sizeof(csmi_buffer
->Signature
));
3263 csmi_buffer
->Timeout
= CSMI_SAS_TIMEOUT
;
3264 csmi_buffer
->ControlCode
= code
;
3265 csmi_buffer
->ReturnCode
= 0;
3266 csmi_buffer
->Length
= csmi_bufsiz
- sizeof(IOCTL_HEADER
);
3270 if (!DeviceIoControl(m_fh
, IOCTL_SCSI_MINIPORT
,
3271 csmi_buffer
, csmi_bufsiz
, csmi_buffer
, csmi_bufsiz
, &num_out
, (OVERLAPPED
*)0)) {
3272 long err
= GetLastError();
3274 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code
, err
);
3275 if ( err
== ERROR_INVALID_FUNCTION
3276 || err
== ERROR_NOT_SUPPORTED
3277 || err
== ERROR_DEV_NOT_EXIST
)
3278 return set_err(ENOSYS
, "CSMI is not supported (Error=%ld)", err
);
3280 return set_err(EIO
, "CSMI(%u) failed with Error=%ld", code
, err
);
3284 if (csmi_buffer
->ReturnCode
) {
3285 if (scsi_debugmode
) {
3286 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",
3287 code
, csmi_buffer
->ReturnCode
);
3289 return set_err(EIO
, "CSMI(%u) failed with ReturnCode=%lu", code
, csmi_buffer
->ReturnCode
);
3292 if (scsi_debugmode
> 1)
3293 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code
, num_out
);
3299 /////////////////////////////////////////////////////////////////////////////
3300 // ASPI Interface (for SCSI devices on 9x/ME)
3301 /////////////////////////////////////////////////////////////////////////////
3307 #define ASPI_SENSE_SIZE 18
3309 // ASPI SCSI Request block header
3312 unsigned char cmd
; // 00: Command code
3313 unsigned char status
; // 01: ASPI status
3314 unsigned char adapter
; // 02: Host adapter number
3315 unsigned char flags
; // 03: Request flags
3316 unsigned char reserved
[4]; // 04: 0
3319 // SRB for host adapter inquiry
3322 ASPI_SRB_HEAD h
; // 00: Header
3323 unsigned char adapters
; // 08: Number of adapters
3324 unsigned char target_id
; // 09: Target ID ?
3325 char manager_id
[16]; // 10: SCSI manager ID
3326 char adapter_id
[16]; // 26: Host adapter ID
3327 unsigned char parameters
[16]; // 42: Host adapter unique parmameters
3330 // SRB for get device type
3333 ASPI_SRB_HEAD h
; // 00: Header
3334 unsigned char target_id
; // 08: Target ID
3335 unsigned char lun
; // 09: LUN
3336 unsigned char devtype
; // 10: Device type
3337 unsigned char reserved
; // 11: Reserved
3343 ASPI_SRB_HEAD h
; // 00: Header
3344 unsigned char target_id
; // 08: Target ID
3345 unsigned char lun
; // 09: LUN
3346 unsigned char reserved
[2]; // 10: Reserved
3347 unsigned long data_size
; // 12: Data alloc. lenght
3348 void * data_addr
; // 16: Data buffer pointer
3349 unsigned char sense_size
; // 20: Sense alloc. length
3350 unsigned char cdb_size
; // 21: CDB length
3351 unsigned char host_status
; // 22: Host status
3352 unsigned char target_status
; // 23: Target status
3353 void * event_handle
; // 24: Event handle
3354 unsigned char workspace
[20]; // 28: ASPI workspace
3355 unsigned char cdb
[16+ASPI_SENSE_SIZE
];
3358 // Macro to retrieve start of sense information
3359 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
3364 ASPI_SRB_HEAD h
; // Common header
3365 ASPI_SRB_INQUIRY q
; // Inquiry
3366 ASPI_SRB_DEVTYPE t
; // Device type
3367 ASPI_SRB_IO i
; // I/O
3373 #define ASPI_CMD_ADAPTER_INQUIRE 0x00
3374 #define ASPI_CMD_GET_DEVICE_TYPE 0x01
3375 #define ASPI_CMD_EXECUTE_IO 0x02
3376 #define ASPI_CMD_ABORT_IO 0x03
3379 #define ASPI_REQFLAG_DIR_TO_HOST 0x08
3380 #define ASPI_REQFLAG_DIR_TO_TARGET 0x10
3381 #define ASPI_REQFLAG_DIR_NO_XFER 0x18
3382 #define ASPI_REQFLAG_EVENT_NOTIFY 0x40
3385 #define ASPI_STATUS_IN_PROGRESS 0x00
3386 #define ASPI_STATUS_NO_ERROR 0x01
3387 #define ASPI_STATUS_ABORTED 0x02
3388 #define ASPI_STATUS_ABORT_ERR 0x03
3389 #define ASPI_STATUS_ERROR 0x04
3390 #define ASPI_STATUS_INVALID_COMMAND 0x80
3391 #define ASPI_STATUS_INVALID_ADAPTER 0x81
3392 #define ASPI_STATUS_INVALID_TARGET 0x82
3393 #define ASPI_STATUS_NO_ADAPTERS 0xE8
3395 // Adapter (host) status
3396 #define ASPI_HSTATUS_NO_ERROR 0x00
3397 #define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
3398 #define ASPI_HSTATUS_DATA_OVERRUN 0x12
3399 #define ASPI_HSTATUS_BUS_FREE 0x13
3400 #define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
3401 #define ASPI_HSTATUS_BAD_SGLIST 0x1A
3404 #define ASPI_TSTATUS_NO_ERROR 0x00
3405 #define ASPI_TSTATUS_CHECK_CONDITION 0x02
3406 #define ASPI_TSTATUS_BUSY 0x08
3407 #define ASPI_TSTATUS_RESERV_CONFLICT 0x18
3410 static HINSTANCE h_aspi_dll
; // DLL handle
3411 static UINT (* aspi_entry
)(ASPI_SRB
* srb
); // ASPI entrypoint
3412 static unsigned num_aspi_adapters
;
3415 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
3416 static DWORD aspi_dll_pid
; // PID of DLL owner to detect fork()
3417 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
3419 #define aspi_entry_valid() (!!aspi_entry)
3423 static int aspi_call(ASPI_SRB
* srb
)
3428 while (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
3429 if (++i
> 100/*10sek*/) {
3430 pout("ASPI Adapter %u: Timed out\n", srb
->h
.adapter
);
3432 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3436 if (scsi_debugmode
> 1)
3437 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb
->h
.adapter
, i
);
3444 // Get ASPI entrypoint from wnaspi32.dll
3446 static FARPROC
aspi_get_address(const char * name
, int verbose
)
3449 assert(h_aspi_dll
&& h_aspi_dll
!= INVALID_HANDLE_VALUE
);
3451 if (!(addr
= GetProcAddress(h_aspi_dll
, name
))) {
3453 pout("Missing %s() in WNASPI32.DLL\n", name
);
3455 FreeLibrary(h_aspi_dll
);
3456 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3464 static int aspi_open_dll(int verbose
)
3466 UINT (*aspi_info
)(void);
3469 assert(!aspi_entry_valid());
3471 // Check structure layout
3472 assert(sizeof(ASPI_SRB_HEAD
) == 8);
3473 assert(sizeof(ASPI_SRB_INQUIRY
) == 58);
3474 assert(sizeof(ASPI_SRB_DEVTYPE
) == 12);
3475 assert(sizeof(ASPI_SRB_IO
) == 64+ASPI_SENSE_SIZE
);
3476 assert(offsetof(ASPI_SRB
,h
.cmd
) == 0);
3477 assert(offsetof(ASPI_SRB
,h
.flags
) == 3);
3478 assert(offsetof(ASPI_SRB_IO
,lun
) == 9);
3479 assert(offsetof(ASPI_SRB_IO
,data_addr
) == 16);
3480 assert(offsetof(ASPI_SRB_IO
,workspace
) == 28);
3481 assert(offsetof(ASPI_SRB_IO
,cdb
) == 48);
3483 if (h_aspi_dll
== INVALID_HANDLE_VALUE
) {
3490 if (!(h_aspi_dll
= LoadLibraryA("WNASPI32.DLL"))) {
3492 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
3493 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3497 if (scsi_debugmode
> 1) {
3498 // Print full path of WNASPI32.DLL
3499 char path
[MAX_PATH
];
3500 if (!GetModuleFileName(h_aspi_dll
, path
, sizeof(path
)))
3501 strcpy(path
, "*unknown*");
3502 pout("Using ASPI interface \"%s\"\n", path
);
3505 // Get ASPI entrypoints
3506 if (!(aspi_info
= (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose
)))
3508 if (!(aspi_entry
= (UINT (*)(ASPI_SRB
*))aspi_get_address("SendASPI32Command", verbose
)))
3511 // Init ASPI manager and get number of adapters
3512 info
= (aspi_info
)();
3513 if (scsi_debugmode
> 1)
3514 pout("GetASPI32SupportInfo() returns 0x%04x\n", info
);
3515 rc
= (info
>> 8) & 0xff;
3516 if (rc
== ASPI_STATUS_NO_ADAPTERS
) {
3517 num_aspi_adapters
= 0;
3519 else if (rc
== ASPI_STATUS_NO_ERROR
) {
3520 num_aspi_adapters
= info
& 0xff;
3524 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info
);
3526 FreeLibrary(h_aspi_dll
);
3527 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3533 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3536 // save PID to detect fork() in aspi_entry_valid()
3537 aspi_dll_pid
= GetCurrentProcessId();
3539 assert(aspi_entry_valid());
3544 static int aspi_io_call(ASPI_SRB
* srb
, unsigned timeout
)
3548 if (!(event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
))) {
3549 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO
;
3551 srb
->i
.event_handle
= event
;
3552 srb
->h
.flags
|= ASPI_REQFLAG_EVENT_NOTIFY
;
3553 // Start ASPI request
3555 if (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
3557 DWORD rc
= WaitForSingleObject(event
, timeout
*1000L);
3558 if (rc
!= WAIT_OBJECT_0
) {
3559 if (rc
== WAIT_TIMEOUT
) {
3560 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
3561 srb
->h
.adapter
, srb
->i
.target_id
, timeout
);
3564 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
3565 (unsigned long)(ULONG_PTR
)event
, rc
, rc
, GetLastError());
3567 // TODO: ASPI_ABORT_IO command
3569 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3578 win_aspi_device::win_aspi_device(smart_interface
* intf
,
3579 const char * dev_name
, const char * req_type
)
3580 : smart_device(intf
, dev_name
, "scsi", req_type
),
3581 m_adapter(-1), m_id(0)
3585 bool win_aspi_device::is_open() const
3587 return (m_adapter
>= 0);
3590 bool win_aspi_device::open()
3592 // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
3593 unsigned adapter
= ~0, id
= ~0; int n1
= -1;
3594 const char * name
= skipdev(get_dev_name());
3595 if (!(sscanf(name
,"scsi%1u%1x%n", &adapter
, &id
, &n1
) == 2 && n1
== (int)strlen(name
)
3596 && adapter
<= 9 && id
< 16))
3597 return set_err(EINVAL
);
3599 if (!aspi_entry_valid()) {
3600 if (aspi_open_dll(1/*verbose*/))
3601 return set_err(ENOENT
);
3605 if (adapter
>= num_aspi_adapters
) {
3606 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
3607 adapter
, num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3608 if (!is_permissive())
3609 return set_err(ENOENT
);
3614 memset(&srb
, 0, sizeof(srb
));
3615 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3616 srb
.h
.adapter
= adapter
; srb
.i
.target_id
= id
;
3617 if (aspi_call(&srb
))
3618 return set_err(EIO
);
3619 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3620 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter
, id
, srb
.h
.status
);
3621 if (!is_permissive())
3622 return set_err(srb
.h
.status
== ASPI_STATUS_INVALID_TARGET
? ENOENT
: EIO
);
3624 else if (scsi_debugmode
)
3625 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter
, id
, srb
.t
.devtype
);
3627 m_adapter
= (int)adapter
; m_id
= (unsigned char)id
;
3632 bool win_aspi_device::close()
3634 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
3639 // Scan for ASPI drives
3641 bool win9x_smart_interface::scsi_scan(smart_device_list
& devlist
)
3643 if (!aspi_entry_valid()) {
3644 if (aspi_open_dll(scsi_debugmode
/*default is quiet*/))
3648 for (unsigned ad
= 0; ad
< num_aspi_adapters
; ad
++) {
3653 pout(" ASPI Adapter %u: Ignored\n", ad
);
3658 memset(&srb
, 0, sizeof(srb
));
3659 srb
.h
.cmd
= ASPI_CMD_ADAPTER_INQUIRE
;
3661 if (aspi_call(&srb
))
3664 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3666 pout(" ASPI Adapter %u: Status=0x%02x\n", ad
, srb
.h
.status
);
3670 if (scsi_debugmode
) {
3671 for (int i
= 1; i
< 16 && srb
.q
.adapter_id
[i
]; i
++)
3672 if (!(' ' <= srb
.q
.adapter_id
[i
] && srb
.q
.adapter_id
[i
] <= '~'))
3673 srb
.q
.adapter_id
[i
] = '?';
3674 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad
, srb
.q
.adapter_id
);
3677 bool ignore
= !strnicmp(srb
.q
.adapter_id
, "3ware", 5);
3679 for (unsigned id
= 0; id
<= 7; id
++) {
3681 memset(&srb
, 0, sizeof(srb
));
3682 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3683 srb
.h
.adapter
= ad
; srb
.i
.target_id
= id
;
3684 if (aspi_call(&srb
))
3686 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3687 if (scsi_debugmode
> 1)
3688 pout(" ID %u: No such device (Status=0x%02x)\n", id
, srb
.h
.status
);
3692 if (!ignore
&& srb
.t
.devtype
== 0x00/*HDD*/) {
3694 pout(" ID %u: Device Type=0x%02x\n", id
, srb
.t
.devtype
);
3696 sprintf(name
, "/dev/scsi%u%u", ad
, id
);
3697 devlist
.push_back( new win_aspi_device(this, name
, "scsi") );
3699 else if (scsi_debugmode
)
3700 pout(" ID %u: Device Type=0x%02x (ignored)\n", id
, srb
.t
.devtype
);
3707 // Interface to ASPI SCSI devices
3708 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
3710 int report
= scsi_debugmode
; // TODO
3712 if (m_adapter
< 0) {
3717 if (!aspi_entry_valid()) {
3722 if (!(iop
->cmnd_len
== 6 || iop
->cmnd_len
== 10 || iop
->cmnd_len
== 12 || iop
->cmnd_len
== 16)) {
3723 set_err(EINVAL
, "bad CDB length");
3730 const unsigned char * ucp
= iop
->cmnd
;
3733 const int sz
= (int)sizeof(buff
);
3735 np
= scsi_get_opcode_name(ucp
[0]);
3736 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3737 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3738 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3740 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3741 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3743 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3744 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3745 (trunc
? " [only first 256 bytes shown]" : ""));
3746 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3749 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3754 memset(&srb
, 0, sizeof(srb
));
3755 srb
.h
.cmd
= ASPI_CMD_EXECUTE_IO
;
3756 srb
.h
.adapter
= m_adapter
;
3757 srb
.i
.target_id
= m_id
;
3759 srb
.i
.sense_size
= ASPI_SENSE_SIZE
;
3760 srb
.i
.cdb_size
= iop
->cmnd_len
;
3761 memcpy(srb
.i
.cdb
, iop
->cmnd
, iop
->cmnd_len
);
3763 switch (iop
->dxfer_dir
) {
3765 srb
.h
.flags
= ASPI_REQFLAG_DIR_NO_XFER
;
3767 case DXFER_FROM_DEVICE
:
3768 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_HOST
;
3769 srb
.i
.data_size
= iop
->dxfer_len
;
3770 srb
.i
.data_addr
= iop
->dxferp
;
3772 case DXFER_TO_DEVICE
:
3773 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_TARGET
;
3774 srb
.i
.data_size
= iop
->dxfer_len
;
3775 srb
.i
.data_addr
= iop
->dxferp
;
3778 set_err(EINVAL
, "bad dxfer_dir");
3782 iop
->resp_sense_len
= 0;
3783 iop
->scsi_status
= 0;
3786 if (aspi_io_call(&srb
, (iop
->timeout
? iop
->timeout
: 60))) {
3788 set_err(EIO
, "ASPI Timeout"); return false;
3791 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3792 if ( srb
.h
.status
== ASPI_STATUS_ERROR
3793 && srb
.i
.host_status
== ASPI_HSTATUS_NO_ERROR
3794 && srb
.i
.target_status
== ASPI_TSTATUS_CHECK_CONDITION
) {
3796 const unsigned char * sense
= ASPI_SRB_SENSE(&srb
.i
, iop
->cmnd_len
);
3797 int len
= (ASPI_SENSE_SIZE
< iop
->max_sense_len
? ASPI_SENSE_SIZE
: iop
->max_sense_len
);
3798 iop
->scsi_status
= SCSI_STATUS_CHECK_CONDITION
;
3799 if (len
> 0 && iop
->sensep
) {
3800 memcpy(iop
->sensep
, sense
, len
);
3801 iop
->resp_sense_len
= len
;
3803 pout(" >>> Sense buffer, len=%d:\n", (int)len
);
3804 dStrHex(iop
->sensep
, len
, 1);
3808 pout(" sense_key=%x asc=%x ascq=%x\n",
3809 sense
[2] & 0xf, sense
[12], sense
[13]);
3815 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb
.h
.status
, srb
.i
.host_status
, srb
.i
.target_status
);
3824 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
&& report
> 1) {
3825 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3826 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
3827 (trunc
? " [only first 256 bytes shown]" : ""));
3828 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3834 #endif // WIN9X_SUPPORT
3836 /////////////////////////////////////////////////////////////////////////////
3837 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
3838 // Only supported in NT and later
3839 /////////////////////////////////////////////////////////////////////////////
3841 win_scsi_device::win_scsi_device(smart_interface
* intf
,
3842 const char * dev_name
, const char * req_type
)
3843 : smart_device(intf
, dev_name
, "scsi", req_type
)
3847 bool win_scsi_device::open()
3849 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
3850 // sd[a-z],N => Physical drive 0-26, RAID port N
3851 char drive
[1+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
3852 if ( sscanf(name
, "sd%1[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
3853 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
3854 return open(drive
[0] - 'a', -1, -1, sub_addr
);
3856 // pd<m>,N => Physical drive <m>, RAID port N
3857 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
3858 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
3859 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
3860 return open(pd_num
, -1, -1, sub_addr
);
3862 // [a-zA-Z]: => Physical drive behind logical drive 0-25
3863 int logdrive
= drive_letter(name
);
3864 if (logdrive
>= 0) {
3865 return open(-1, logdrive
, -1, -1);
3867 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
3868 int tape_num
= -1; n1
= -1;
3869 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3870 return open(-1, -1, tape_num
, -1);
3872 tape_num
= -1; n1
= -1;
3873 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3874 return open(-1, -1, tape_num
, -1);
3876 // tape<m> => tape drive <m>
3877 tape_num
= -1; n1
= -1;
3878 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3879 return open(-1, -1, tape_num
, -1);
3882 return set_err(EINVAL
);
3885 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
3888 b
[sizeof(b
) - 1] = '\0';
3890 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
3891 else if (ld_num
>= 0)
3892 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
3893 else if (tape_num
>= 0)
3894 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
3901 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
3902 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
3903 OPEN_EXISTING
, 0, 0);
3904 if (h
== INVALID_HANDLE_VALUE
) {
3905 set_err(ENODEV
, "%s: Open failed, Error=%ld", b
, GetLastError());
3914 SCSI_PASS_THROUGH_DIRECT spt
;
3916 UCHAR ucSenseBuf
[64];
3917 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
3920 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
3921 // Used if DataTransferLength not supported by *_DIRECT.
3922 static long scsi_pass_through_indirect(HANDLE h
,
3923 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
3925 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
3926 SCSI_PASS_THROUGH spt
;
3928 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
3929 UCHAR ucDataBuf
[512];
3932 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
3933 memset(&sb
, 0, sizeof(sb
));
3935 // DATA_OUT not implemented yet
3936 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
3937 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
3938 return ERROR_INVALID_PARAMETER
;
3940 sb
.spt
.Length
= sizeof(sb
.spt
);
3941 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
3942 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
3943 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3944 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
3945 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
3946 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
3947 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
3948 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
3951 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
3952 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3953 return GetLastError();
3955 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
3956 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
3957 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
3959 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
3960 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
3961 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
3966 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
3967 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
3969 int report
= scsi_debugmode
; // TODO
3973 const unsigned char * ucp
= iop
->cmnd
;
3976 const int sz
= (int)sizeof(buff
);
3978 np
= scsi_get_opcode_name(ucp
[0]);
3979 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3980 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3981 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3983 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3984 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3986 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3987 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3988 (trunc
? " [only first 256 bytes shown]" : ""));
3989 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3992 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3996 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
3997 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
3998 set_err(EINVAL
, "cmnd_len too large");
4002 memset(&sb
, 0, sizeof(sb
));
4003 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
4004 sb
.spt
.CdbLength
= iop
->cmnd_len
;
4005 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
4006 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
4007 sb
.spt
.SenseInfoOffset
=
4008 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
4009 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
4012 switch (iop
->dxfer_dir
) {
4014 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
4016 case DXFER_FROM_DEVICE
:
4017 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
4018 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4019 sb
.spt
.DataBuffer
= iop
->dxferp
;
4020 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
4021 // transfers (needed for SMART STATUS check of JMicron USB bridges)
4022 if (sb
.spt
.DataTransferLength
== 1)
4025 case DXFER_TO_DEVICE
:
4026 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
4027 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4028 sb
.spt
.DataBuffer
= iop
->dxferp
;
4031 set_err(EINVAL
, "bad dxfer_dir");
4038 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
4039 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
4040 err
= GetLastError();
4043 err
= scsi_pass_through_indirect(get_fh(), &sb
);
4046 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
4047 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
4048 (direct
? "_DIRECT" : ""), err
);
4050 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
4051 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
4052 int slen
= sb
.ucSenseBuf
[7] + 8;
4054 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
4055 slen
= sizeof(sb
.ucSenseBuf
);
4056 if (slen
> (int)iop
->max_sense_len
)
4057 slen
= iop
->max_sense_len
;
4058 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
4059 iop
->resp_sense_len
= slen
;
4062 pout(" >>> Sense buffer, len=%d:\n", slen
);
4063 dStrHex(iop
->sensep
, slen
, 1);
4065 if ((iop
->sensep
[0] & 0x7f) > 0x71)
4066 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
4067 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
4068 iop
->sensep
[2], iop
->sensep
[3]);
4070 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
4071 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
4072 iop
->sensep
[12], iop
->sensep
[13]);
4075 iop
->resp_sense_len
= 0;
4077 if ((iop
->dxfer_len
> 0) && (sb
.spt
.DataTransferLength
> 0))
4078 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
4082 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
4083 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4084 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
4085 (trunc
? " [only first 256 bytes shown]" : ""));
4086 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4092 //////////////////////////////////////////////////////////////////////////////////////////////////
4097 /////////////////////////////////////////////////////////////////////////////
4099 // Initialize platform interface and register with smi()
4100 void smart_interface::init()
4103 // Remove "." from DLL search path if supported
4104 // to prevent DLL preloading attacks
4105 BOOL (WINAPI
* SetDllDirectoryA_p
)(LPCSTR
) = (BOOL (WINAPI
*)(LPCSTR
))
4106 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4107 if (SetDllDirectoryA_p
)
4108 SetDllDirectoryA_p("");
4111 // Select interface for Windows flavor
4112 if (GetVersion() & 0x80000000) {
4114 static os_win32::win9x_smart_interface the_win9x_interface
;
4115 smart_interface::set(&the_win9x_interface
);
4117 throw std::runtime_error("Win9x/ME not supported");
4121 static os_win32::winnt_smart_interface the_winnt_interface
;
4122 smart_interface::set(&the_winnt_interface
);
4129 // Get exe directory
4130 // (prototype in utiliy.h)
4131 std::string
get_exe_dir()
4133 char path
[MAX_PATH
];
4134 // Get path of this exe
4135 if (!GetModuleFileNameA(GetModuleHandleA(0), path
, sizeof(path
)))
4136 throw std::runtime_error("GetModuleFileName() failed");
4137 // Replace backslash by slash
4139 for (int i
= 0; path
[i
]; i
++)
4140 if (path
[i
] == '\\') {
4141 path
[i
] = '/'; sl
= i
;