4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * You should have received a copy of the GNU General Public License
15 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
21 #define _WIN32_WINNT WINVER
27 #include "smartctl.h" // TODO: Do not use smartctl only variables here
29 #include "dev_interface.h"
30 #include "dev_ata_cmd_set.h"
32 #include "os_win32/wmiquery.h"
40 #define assert(x) /* */
43 #include <stddef.h> // offsetof()
44 #include <io.h> // access()
46 // WIN32_LEAN_AND_MEAN may be required to prevent inclusion of <winioctl.h>
47 #define WIN32_LEAN_AND_MEAN
51 // i686-w64-mingw32, x86_64-w64-mingw32
52 // (Missing: FILE_DEVICE_SCSI)
57 #elif HAVE_DDK_NTDDDISK_H
58 // i686-pc-cygwin, i686-pc-mingw32, i586-mingw32msvc
59 // (Missing: IOCTL_IDE_PASS_THROUGH, IOCTL_ATA_PASS_THROUGH, FILE_DEVICE_SCSI)
60 #include <ddk/ntdddisk.h>
61 #include <ddk/ntddscsi.h>
62 #include <ddk/ntddstor.h>
64 // MSVC10, older MinGW
65 // (Missing: IOCTL_SCSI_MINIPORT_*)
74 #include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
77 // Macro to check constants at compile time using a dummy typedef
78 #define ASSERT_CONST(c, n) \
79 typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
80 #define ASSERT_SIZEOF(t, n) \
81 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
84 #define SELECT_WIN_32_64(x32, x64) (x32)
86 #define SELECT_WIN_32_64(x32, x64) (x64)
89 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 3558 2012-06-05 16:42:05Z chrfranke $";
91 // Disable Win9x/ME specific code if no longer supported by compiler.
94 #elif !defined(WIN9X_SUPPORT)
95 #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
96 // Win9x/ME support was dropped in Cygwin 1.7
97 #elif defined(_MSC_VER) && (_MSC_VER >= 1500)
98 // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)
100 #define WIN9X_SUPPORT 1
104 /////////////////////////////////////////////////////////////////////////////
105 // Windows I/O-controls, some declarations are missing in the include files
109 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
111 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
112 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
113 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
114 ASSERT_SIZEOF(GETVERSIONINPARAMS
, 24);
115 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
116 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
119 // IDE PASS THROUGH (2000, XP, undocumented)
121 #ifndef IOCTL_IDE_PASS_THROUGH
123 #define IOCTL_IDE_PASS_THROUGH \
124 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
126 #endif // IOCTL_IDE_PASS_THROUGH
132 ULONG DataBufferSize
;
138 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
139 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
142 // ATA PASS THROUGH (Win2003, XP SP2)
144 #ifndef IOCTL_ATA_PASS_THROUGH
146 #define IOCTL_ATA_PASS_THROUGH \
147 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
149 typedef struct _ATA_PASS_THROUGH_EX
{
155 UCHAR ReservedAsUchar
;
156 ULONG DataTransferLength
;
158 ULONG ReservedAsUlong
;
159 ULONG_PTR DataBufferOffset
;
160 UCHAR PreviousTaskFile
[8];
161 UCHAR CurrentTaskFile
[8];
162 } ATA_PASS_THROUGH_EX
;
164 #define ATA_FLAGS_DRDY_REQUIRED 0x01
165 #define ATA_FLAGS_DATA_IN 0x02
166 #define ATA_FLAGS_DATA_OUT 0x04
167 #define ATA_FLAGS_48BIT_COMMAND 0x08
168 #define ATA_FLAGS_USE_DMA 0x10
169 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
171 #endif // IOCTL_ATA_PASS_THROUGH
173 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
174 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, SELECT_WIN_32_64(40, 48));
177 // IOCTL_SCSI_PASS_THROUGH[_DIRECT]
179 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
180 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT
, 0x04d014);
181 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, SELECT_WIN_32_64(44, 56));
182 ASSERT_SIZEOF(SCSI_PASS_THROUGH_DIRECT
, SELECT_WIN_32_64(44, 56));
185 // SMART IOCTL via SCSI MINIPORT ioctl
187 #ifndef FILE_DEVICE_SCSI
188 #define FILE_DEVICE_SCSI 0x001b
191 #ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
193 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
194 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
195 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
196 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
197 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
198 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
199 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
200 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
201 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
202 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
203 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
204 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
205 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
207 #endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
209 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
210 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
213 // IOCTL_STORAGE_QUERY_PROPERTY
215 #ifndef IOCTL_STORAGE_QUERY_PROPERTY
217 #define IOCTL_STORAGE_QUERY_PROPERTY \
218 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
220 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
224 UCHAR DeviceTypeModifier
;
225 BOOLEAN RemovableMedia
;
226 BOOLEAN CommandQueueing
;
227 ULONG VendorIdOffset
;
228 ULONG ProductIdOffset
;
229 ULONG ProductRevisionOffset
;
230 ULONG SerialNumberOffset
;
231 STORAGE_BUS_TYPE BusType
;
232 ULONG RawPropertiesLength
;
233 UCHAR RawDeviceProperties
[1];
234 } STORAGE_DEVICE_DESCRIPTOR
;
236 typedef enum _STORAGE_QUERY_TYPE
{
237 PropertyStandardQuery
= 0,
240 PropertyQueryMaxDefined
241 } STORAGE_QUERY_TYPE
;
243 typedef enum _STORAGE_PROPERTY_ID
{
244 StorageDeviceProperty
= 0,
245 StorageAdapterProperty
,
246 StorageDeviceIdProperty
,
247 StorageDeviceUniqueIdProperty
,
248 StorageDeviceWriteCacheProperty
,
249 StorageMiniportProperty
,
250 StorageAccessAlignmentProperty
251 } STORAGE_PROPERTY_ID
;
253 typedef struct _STORAGE_PROPERTY_QUERY
{
254 STORAGE_PROPERTY_ID PropertyId
;
255 STORAGE_QUERY_TYPE QueryType
;
256 UCHAR AdditionalParameters
[1];
257 } STORAGE_PROPERTY_QUERY
;
259 #endif // IOCTL_STORAGE_QUERY_PROPERTY
261 ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY
, 0x002d1400);
262 ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR
, 36+1+3);
263 ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY
, 8+1+3);
266 // IOCTL_STORAGE_PREDICT_FAILURE
268 ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE
, 0x002d1100);
269 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
272 // 3ware specific versions of SMART ioctl structs
274 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
278 typedef struct _GETVERSIONINPARAMS_EX
{
284 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
285 WORD wIdentifier
; // Vendor specific identifier
286 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
288 } GETVERSIONINPARAMS_EX
;
290 typedef struct _SENDCMDINPARAMS_EX
{
294 BYTE bPortNumber
; // 3ware specific: port number
295 WORD wIdentifier
; // Vendor specific identifier
298 } SENDCMDINPARAMS_EX
;
302 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONINPARAMS
));
303 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
308 ASSERT_SIZEOF(IOCTL_HEADER
, sizeof(SRB_IO_CONTROL
));
309 ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER
, 204);
310 ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER
, 2080);
311 ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER
, 168);
315 /////////////////////////////////////////////////////////////////////////////
317 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
320 #pragma warning(disable:4250)
323 // Running on Win9x/ME ?
325 // Set true in win9x_smart_interface ctor.
326 static bool win9x
= false;
328 // Never true (const allows compiler to remove dead code).
329 const bool win9x
= false;
333 class win_smart_device
334 : virtual public /*implements*/ smart_device
338 : smart_device(never_called
),
339 m_fh(INVALID_HANDLE_VALUE
)
342 virtual ~win_smart_device() throw();
344 virtual bool is_open() const;
346 virtual bool close();
349 /// Set handle for open() in derived classes.
350 void set_fh(HANDLE fh
)
353 /// Return handle for derived classes.
354 HANDLE
get_fh() const
358 HANDLE m_fh
; ///< File handle
362 /////////////////////////////////////////////////////////////////////////////
365 : public /*implements*/ ata_device
,
366 public /*extends*/ win_smart_device
369 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
371 virtual ~win_ata_device() throw();
375 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
377 virtual bool ata_identify_is_cached() const;
380 bool open(int phydrive
, int logdrive
, const char * options
, int port
);
382 std::string m_options
;
383 bool m_usr_options
; // options set by user?
384 bool m_admin
; // open with admin access?
385 bool m_id_is_cached
; // ata_identify_is_cached() return value.
386 bool m_is_3ware
; // AMCC/3ware controller detected?
388 int m_smartver_state
;
392 /////////////////////////////////////////////////////////////////////////////
394 class win_scsi_device
395 : public /*implements*/ scsi_device
,
396 virtual public /*extends*/ win_smart_device
399 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
403 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
406 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
410 /////////////////////////////////////////////////////////////////////////////
414 class win_aspi_device
415 : public /*implements*/ scsi_device
418 win_aspi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
420 virtual bool is_open() const;
424 virtual bool close();
426 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
433 #endif // WIN9X_SUPPORT
436 //////////////////////////////////////////////////////////////////////
439 : virtual public /*extends*/ smart_device
443 bool get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
);
445 /// Check physical drive existence
446 bool check_phy(const CSMI_SAS_PHY_INFO
& phy_info
, unsigned phy_no
);
450 : smart_device(never_called
)
451 { memset(&m_phy_ent
, 0, sizeof(m_phy_ent
)); }
453 /// Select physical drive
454 bool select_phy(unsigned phy_no
);
456 /// Get info for selected physical drive
457 const CSMI_SAS_PHY_ENTITY
& get_phy_ent() const
458 { return m_phy_ent
; }
460 /// Call platform-specific CSMI ioctl
461 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
462 unsigned csmi_bufsiz
) = 0;
465 CSMI_SAS_PHY_ENTITY m_phy_ent
; ///< CSMI info for this phy
469 class csmi_ata_device
470 : virtual public /*extends*/ csmi_device
,
471 virtual public /*implements*/ ata_device
474 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
478 : smart_device(never_called
) { }
482 //////////////////////////////////////////////////////////////////////
484 class win_csmi_device
485 : public /*implements*/ csmi_ata_device
488 win_csmi_device(smart_interface
* intf
, const char * dev_name
,
489 const char * req_type
);
491 virtual ~win_csmi_device() throw();
495 virtual bool close();
497 virtual bool is_open() const;
502 virtual bool csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
503 unsigned csmi_bufsiz
);
506 HANDLE m_fh
; ///< Controller device handle
507 unsigned m_phy_no
; ///< Physical drive number
511 //////////////////////////////////////////////////////////////////////
513 class win_tw_cli_device
514 : public /*implements*/ ata_device_with_command_set
517 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
519 virtual bool is_open() const;
523 virtual bool close();
526 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
529 bool m_ident_valid
, m_smart_valid
;
530 ata_identify_device m_ident_buf
;
531 ata_smart_values m_smart_buf
;
535 /////////////////////////////////////////////////////////////////////////////
536 /// Areca RAID support
538 /* ARECA IO CONTROL CODE*/
539 #define ARCMSR_IOCTL_READ_RQBUFFER 0x90002004
540 #define ARCMSR_IOCTL_WRITE_WQBUFFER 0x90002008
541 #define ARCMSR_IOCTL_CLEAR_RQBUFFER 0x9000200C
542 #define ARCMSR_IOCTL_CLEAR_WQBUFFER 0x90002010
543 #define ARCMSR_IOCTL_RETURN_CODE_3F 0x90002018
544 #define ARECA_SIG_STR "ARCMSR"
547 // The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver
548 typedef struct _SRB_IO_CONTROL
550 unsigned int HeaderLength
;
551 unsigned char Signature
[8];
552 unsigned int Timeout
;
553 unsigned int ControlCode
;
554 unsigned int ReturnCode
;
558 typedef struct _SRB_BUFFER
560 sSRB_IO_CONTROL srbioctl
;
561 unsigned char ioctldatabuffer
[1032]; // the buffer to put the command data to/from firmware
564 class win_areca_device
565 : public /*implements*/ ata_device
,
566 public /*extends*/ win_smart_device
569 win_areca_device(smart_interface
* intf
, const char * dev_name
, HANDLE fh
, int disknum
, int encnum
= 1);
571 static int arcmsr_command_handler(HANDLE fh
, unsigned long arcmsr_cmd
, unsigned char *data
, int data_len
);
576 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
578 bool arcmsr_ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
581 int m_disknum
; ///< Disk number.
582 int m_encnum
; ///< Enclosure number.
586 //////////////////////////////////////////////////////////////////////
587 // Platform specific interfaces
589 // Common to all windows flavors
590 class win_smart_interface
591 : public /*implements part of*/ smart_interface
594 virtual std::string
get_os_version_str();
596 virtual std::string
get_app_examples(const char * appname
);
599 virtual int64_t get_timer_usec();
602 //virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
603 // const char * pattern = 0);
606 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
608 //virtual scsi_device * get_scsi_device(const char * name, const char * type);
610 virtual smart_device
* autodetect_smart_device(const char * name
);
615 // Win9x/ME reduced functionality
616 class win9x_smart_interface
617 : public /*extends*/ win_smart_interface
620 win9x_smart_interface()
623 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
624 const char * pattern
= 0);
627 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
630 bool ata_scan(smart_device_list
& devlist
);
632 bool scsi_scan(smart_device_list
& devlist
);
635 #endif // WIN9X_SUPPORT
638 class winnt_smart_interface
639 : public /*extends*/ win_smart_interface
642 virtual bool disable_system_auto_standby(bool disable
);
644 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
645 const char * pattern
= 0);
648 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
650 virtual smart_device
* autodetect_smart_device(const char * name
);
652 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
654 virtual std::string
get_valid_custom_dev_types_str();
658 //////////////////////////////////////////////////////////////////////
661 // Running on 64-bit Windows as 32-bit app ?
662 static bool is_wow64()
664 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
665 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))
666 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
667 if (!IsWow64Process_p
)
670 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
676 // Return info string about build host and OS version
677 std::string
win_smart_interface::get_os_version_str()
679 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
680 = SMARTMONTOOLS_BUILD_HOST
;
683 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
684 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
685 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
687 OSVERSIONINFOEXA vi
; memset(&vi
, 0, sizeof(vi
));
688 vi
.dwOSVersionInfoSize
= sizeof(vi
);
689 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
)) {
690 memset(&vi
, 0, sizeof(vi
));
691 vi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
692 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
))
696 if (vi
.dwPlatformId
> 0xff || vi
.dwMajorVersion
> 0xff || vi
.dwMinorVersion
> 0xff)
700 switch (vi
.dwPlatformId
<< 16 | vi
.dwMajorVersion
<< 8 | vi
.dwMinorVersion
) {
701 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400| 0:
702 w
= (vi
.szCSDVersion
[1] == 'B' ||
703 vi
.szCSDVersion
[1] == 'C' ? "95-osr2" : "95"); break;
704 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|10:
705 w
= (vi
.szCSDVersion
[1] == 'A' ? "98se" : "98"); break;
706 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|90: w
= "me"; break;
707 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
708 case VER_PLATFORM_WIN32_NT
<<16|0x0400| 0: w
= "nt4"; break;
709 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 0: w
= "2000"; break;
710 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 1:
711 w
= (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
713 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 2:
714 w
= (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
716 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 0:
717 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "vista"
719 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 1:
720 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "win7"
722 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 2:
723 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "win8"
725 default: w
= 0; break;
728 const char * w64
= "";
735 snprintf(vptr
, vlen
, "-%s%lu.%lu%s",
736 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "9x"),
737 vi
.dwMajorVersion
, vi
.dwMinorVersion
, w64
);
738 else if (vi
.wServicePackMinor
)
739 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
740 else if (vi
.wServicePackMajor
)
741 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
743 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
748 // MSVCRT only provides ftime() which uses GetSystemTime()
749 // This provides only ~15ms resolution by default.
750 // Use QueryPerformanceCounter instead (~300ns).
751 // (Cygwin provides CLOCK_MONOTONIC which has the same effect)
752 int64_t win_smart_interface::get_timer_usec()
754 static int64_t freq
= 0;
758 freq
= (QueryPerformanceFrequency(&t
) ? t
.QuadPart
: -1);
760 return smart_interface::get_timer_usec();
762 if (!QueryPerformanceCounter(&t
))
764 if (!(0 <= t
.QuadPart
&& t
.QuadPart
<= (int64_t)(~(uint64_t)0 >> 1)/1000000))
767 return (t
.QuadPart
* 1000000LL) / freq
;
772 // Return value for device detection functions
773 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_USB
};
775 static win_dev_type
get_phy_drive_type(int drive
);
776 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
);
777 static win_dev_type
get_log_drive_type(int drive
);
778 static bool get_usb_id(int drive
, unsigned short & vendor_id
,
779 unsigned short & product_id
);
781 static const char * ata_get_def_options(void);
784 static int is_permissive()
786 if (!failuretest_permissive
) {
787 pout("To continue, add one or more '-T permissive' options.\n");
790 failuretest_permissive
--;
794 // return number for drive letter, -1 on error
795 // "[A-Za-z]:([/\\][.]?)?" => 0-25
796 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
797 static int drive_letter(const char * s
)
799 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
801 && (!s
[2] || ( strchr("/\\\"", s
[2])
802 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
803 (s
[0] & 0x1f) - 1 : -1);
806 // Skip trailing "/dev/", do not allow "/dev/X:"
807 static const char * skipdev(const char * s
)
809 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
812 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
814 const char * testname
= skipdev(name
);
815 if (!strncmp(testname
, "csmi", 4))
816 return new win_csmi_device(this, name
, type
);
817 if (!strncmp(testname
, "tw_cli", 6))
818 return new win_tw_cli_device(this, name
, type
);
819 return new win_ata_device(this, name
, type
);
824 scsi_device
* win9x_smart_interface::get_scsi_device(const char * name
, const char * type
)
826 return new win_aspi_device(this, name
, type
);
831 scsi_device
* winnt_smart_interface::get_scsi_device(const char * name
, const char * type
)
833 const char * testname
= skipdev(name
);
834 if (!strncmp(testname
, "scsi", 4))
836 return new win_aspi_device(this, name
, type
);
838 return (set_err(EINVAL
, "ASPI interface not supported"), (scsi_device
*)0);
840 return new win_scsi_device(this, name
, type
);
843 static win_dev_type
get_dev_type(const char * name
, int & phydrive
)
846 name
= skipdev(name
);
847 if (!strncmp(name
, "st", 2))
849 if (!strncmp(name
, "nst", 3))
851 if (!strncmp(name
, "tape", 4))
854 int logdrive
= drive_letter(name
);
856 win_dev_type type
= get_log_drive_type(logdrive
);
857 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
860 char drive
[1+1] = "";
861 if (sscanf(name
, "sd%1[a-z]", drive
) == 1) {
862 phydrive
= drive
[0] - 'a';
863 return get_phy_drive_type(phydrive
);
867 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
868 return get_phy_drive_type(phydrive
);
872 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
874 const char * testname
= skipdev(name
);
875 if (!strncmp(testname
, "hd", 2))
876 return new win_ata_device(this, name
, "");
878 if (!strncmp(testname
, "scsi", 4))
879 return new win_aspi_device(this, name
, "");
881 if (!strncmp(testname
, "tw_cli", 6))
882 return new win_tw_cli_device(this, name
, "");
887 smart_device
* winnt_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
890 int disknum
= -1, n1
= -1, n2
= -1;
892 HANDLE fh
= INVALID_HANDLE_VALUE
;
895 if (sscanf(type
, "areca,%n%d/%d%n", &n1
, &disknum
, &encnum
, &n2
) >= 1 || n1
== 6) {
896 if (!(1 <= disknum
&& disknum
<= 128)) {
897 set_err(EINVAL
, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum
);
900 if (!(1 <= encnum
&& encnum
<= 8)) {
901 set_err(EINVAL
, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum
);
905 name
= skipdev(name
);
906 #define ARECA_MAX_CTLR_NUM 16
909 if (sscanf(name
, "arcmsr%d%n", &ctlrindex
, &n1
) >= 1 && n1
== (int)strlen(name
)) {
911 1. scan from "\\\\.\\scsi[0]:" up to "\\\\.\\scsi[ARECA_MAX_CTLR_NUM]:" and
912 2. map arcmsrX into "\\\\.\\scsiX"
914 for (int idx
= 0; idx
< ARECA_MAX_CTLR_NUM
; idx
++) {
915 memset(devpath
, 0, sizeof(devpath
));
916 sprintf(devpath
, "\\\\.\\scsi%d:", idx
);
917 if ( (fh
= CreateFile( devpath
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
918 NULL
, OPEN_EXISTING
, 0, NULL
)) != INVALID_HANDLE_VALUE
) {
919 if (win_areca_device::arcmsr_command_handler(fh
, ARCMSR_IOCTL_RETURN_CODE_3F
, NULL
, 0) == 0) {
920 if (ctlrindex
-- == 0) {
921 return new win_areca_device(this, devpath
, fh
, disknum
, encnum
);
927 set_err(ENOENT
, "No Areca controller found");
930 set_err(EINVAL
, "Option -d areca,N/E requires device name /dev/arcmsrX");
936 std::string
winnt_smart_interface::get_valid_custom_dev_types_str()
938 return "areca,N[/E]";
942 smart_device
* winnt_smart_interface::autodetect_smart_device(const char * name
)
944 smart_device
* dev
= win_smart_interface::autodetect_smart_device(name
);
948 if (!strncmp(skipdev(name
), "csmi", 4))
949 return new win_csmi_device(this, name
, "");
952 win_dev_type type
= get_dev_type(name
, phydrive
);
955 return new win_ata_device(this, name
, "");
956 if (type
== DEV_SCSI
)
957 return new win_scsi_device(this, name
, "");
959 if (type
== DEV_USB
) {
961 unsigned short vendor_id
= 0, product_id
= 0;
962 if (!(phydrive
>= 0 && get_usb_id(phydrive
, vendor_id
, product_id
))) {
963 set_err(EINVAL
, "Unable to read USB device ID");
966 // Get type name for this ID
967 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
970 // Return SAT/USB device for this type
971 return get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
980 // Scan for devices on Win9x/ME
982 bool win9x_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
983 const char * type
, const char * pattern
/* = 0*/)
986 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
990 if (!type
|| !strcmp(type
, "ata")) {
991 if (!ata_scan(devlist
))
995 if (!type
|| !strcmp(type
, "scsi")) {
996 if (!scsi_scan(devlist
))
1002 #endif // WIN9X_SUPPORT
1007 bool winnt_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
1008 const char * type
, const char * pattern
/* = 0*/)
1011 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
1016 bool ata
, scsi
, usb
, csmi
;
1018 ata
= scsi
= usb
= csmi
= true;
1021 ata
= scsi
= usb
= csmi
= false;
1022 if (!strcmp(type
, "ata"))
1024 else if (!strcmp(type
, "scsi"))
1026 else if (!strcmp(type
, "usb"))
1028 else if (!strcmp(type
, "csmi"))
1031 set_err(EINVAL
, "Invalid type '%s', valid arguments are: ata, scsi, usb, csmi", type
);
1036 // Scan up to 10 drives and 2 3ware controllers
1037 const int max_raid
= 2;
1038 bool raid_seen
[max_raid
] = {false, false};
1041 for (int i
= 0; i
<= 9; i
++) {
1042 sprintf(name
, "/dev/sd%c", 'a'+i
);
1043 GETVERSIONINPARAMS_EX vers_ex
;
1045 switch (get_phy_drive_type(i
, (ata
? &vers_ex
: 0))) {
1047 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returned ATA/SATA
1051 // Interpret RAID drive map if present
1052 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
1053 // Skip if too many controllers or logical drive from this controller already seen
1054 if (!(vers_ex
.wControllerId
< max_raid
&& !raid_seen
[vers_ex
.wControllerId
]))
1056 raid_seen
[vers_ex
.wControllerId
] = true;
1057 // Add physical drives
1058 int len
= strlen(name
);
1059 for (int pi
= 0; pi
< 32; pi
++) {
1060 if (vers_ex
.dwDeviceMapEx
& (1L << pi
)) {
1061 sprintf(name
+len
, ",%u", pi
);
1062 devlist
.push_back( new win_ata_device(this, name
, "ata") );
1067 devlist
.push_back( new win_ata_device(this, name
, "ata") );
1072 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
1075 devlist
.push_back( new win_scsi_device(this, name
, "scsi") );
1079 // STORAGE_QUERY_PROPERTY returned USB
1083 // TODO: Use common function for this and autodetect_smart_device()
1084 // Get USB bridge ID
1085 unsigned short vendor_id
= 0, product_id
= 0;
1086 if (!get_usb_id(i
, vendor_id
, product_id
))
1088 // Get type name for this ID
1089 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
1092 // Return SAT/USB device for this type
1093 ata_device
* dev
= get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
1096 devlist
.push_back(dev
);
1107 // Scan CSMI devices
1108 for (int i
= 0; i
<= 9; i
++) {
1109 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,0", i
);
1110 win_csmi_device
test_dev(this, name
, "");
1111 if (!test_dev
.open_scsi())
1113 CSMI_SAS_PHY_INFO phy_info
;
1114 if (!test_dev
.get_phy_info(phy_info
))
1117 for (int pi
= 0; pi
< phy_info
.bNumberOfPhys
; pi
++) {
1118 if (!test_dev
.check_phy(phy_info
, pi
))
1120 snprintf(name
, sizeof(name
)-1, "/dev/csmi%d,%d", i
, pi
);
1121 devlist
.push_back( new win_csmi_device(this, name
, "ata") );
1129 // get examples for smartctl
1130 std::string
win_smart_interface::get_app_examples(const char * appname
)
1132 if (strcmp(appname
, "smartctl"))
1134 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
1135 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
1136 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
1137 " (Enables SMART on first disk)\n\n"
1138 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
1139 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
1140 " (Prints Self-Test & Attribute errors)\n"
1142 " smartctl -a /dev/scsi21\n"
1143 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
1145 " smartctl -a /dev/sda\n"
1146 " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
1147 " smartctl -a /dev/pd3\n"
1148 " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
1149 " smartctl -a /dev/tape1\n"
1150 " (Prints all information for SCSI tape on Tape 1)\n"
1151 " smartctl -A /dev/hdb,3\n"
1152 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
1153 " smartctl -A /dev/tw_cli/c0/p1\n"
1154 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
1155 " smartctl --all --device=areca,3/1 /dev/arcmsr0\n"
1156 " (Prints all SMART info for 3rd ATA disk of the 1st enclosure\n"
1157 " on 1st Areca RAID controller)\n"
1159 " ATA SMART access methods and ordering may be specified by modifiers\n"
1160 " following the device name: /dev/hdX:[saicm], where\n"
1161 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
1162 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
1163 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
1165 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
1170 bool winnt_smart_interface::disable_system_auto_standby(bool disable
)
1173 SYSTEM_POWER_STATUS ps
;
1174 if (!GetSystemPowerStatus(&ps
))
1175 return set_err(ENOSYS
, "Unknown power status");
1176 if (ps
.ACLineStatus
!= 1) {
1177 SetThreadExecutionState(ES_CONTINUOUS
);
1178 if (ps
.ACLineStatus
== 0)
1179 set_err(EIO
, "AC offline");
1181 set_err(EIO
, "Unknown AC line status");
1186 if (!SetThreadExecutionState(ES_CONTINUOUS
| (disable
? ES_SYSTEM_REQUIRED
: 0)))
1187 return set_err(ENOSYS
);
1192 /////////////////////////////////////////////////////////////////////////////
1194 /////////////////////////////////////////////////////////////////////////////
1196 #define SMART_CYL_LOW 0x4F
1197 #define SMART_CYL_HI 0xC2
1199 static void print_ide_regs(const IDEREGS
* r
, int out
)
1201 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
1202 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
1203 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
1206 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
1208 pout(" Input : "); print_ide_regs(ri
, 0);
1210 pout(" Output: "); print_ide_regs(ro
, 1);
1214 /////////////////////////////////////////////////////////////////////////////
1216 // call SMART_GET_VERSION, return device map or -1 on error
1218 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
1220 GETVERSIONINPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
1221 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
1224 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
1225 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
1227 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
1231 assert(num_out
== sizeof(GETVERSIONINPARAMS
));
1233 if (ata_debugmode
> 1) {
1234 pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
1235 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
1236 num_out
, vers
.bVersion
, vers
.bRevision
,
1237 vers
.fCapabilities
, vers
.bIDEDeviceMap
);
1238 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
1239 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
1240 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, vers_ex
.dwDeviceMapEx
);
1244 *ata_version_ex
= vers_ex
;
1246 // TODO: Check vers.fCapabilities here?
1247 return vers
.bIDEDeviceMap
;
1251 // call SMART_* ioctl
1253 static int smart_ioctl(HANDLE hdevice
, int drive
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
1255 SENDCMDINPARAMS inpar
;
1256 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
1258 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
1259 const SENDCMDOUTPARAMS
* outpar
;
1260 DWORD code
, num_out
;
1261 unsigned int size_out
;
1264 memset(&inpar
, 0, sizeof(inpar
));
1265 inpar
.irDriveRegs
= *regs
;
1266 // drive is set to 0-3 on Win9x only
1267 inpar
.irDriveRegs
.bDriveHeadReg
= 0xA0 | ((drive
& 1) << 4);
1268 inpar
.bDriveNumber
= drive
;
1272 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
1273 inpar_ex
.bPortNumber
= port
;
1276 if (datasize
== 512) {
1277 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
1278 inpar
.cBufferSize
= size_out
= 512;
1280 else if (datasize
== 0) {
1281 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
1282 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
1283 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
1284 // Note: cBufferSize must be 0 on Win9x
1293 memset(&outbuf
, 0, sizeof(outbuf
));
1295 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
1296 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
1297 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
1298 long err
= GetLastError();
1299 if (ata_debugmode
&& (err
!= ERROR_INVALID_PARAMETER
|| ata_debugmode
> 1)) {
1300 pout(" %s failed, Error=%ld\n", name
, err
);
1301 print_ide_regs_io(regs
, NULL
);
1303 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
1304 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
1305 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1308 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
1310 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
1312 if (outpar
->DriverStatus
.bDriverError
) {
1313 if (ata_debugmode
) {
1314 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1315 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
1316 print_ide_regs_io(regs
, NULL
);
1318 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1322 if (ata_debugmode
> 1) {
1323 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1324 num_out
, outpar
->cBufferSize
);
1325 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
1326 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
1330 memcpy(data
, outpar
->bBuffer
, 512);
1331 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
1332 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
1333 memcpy(regs
, outpar
->bBuffer
, sizeof(IDEREGS
));
1334 else { // Workaround for driver not returning regs
1336 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
1337 *regs
= inpar
.irDriveRegs
;
1345 /////////////////////////////////////////////////////////////////////////////
1346 // IDE PASS THROUGH (2000, XP, undocumented)
1348 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
1349 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
1351 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1353 if (datasize
> 512) {
1357 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
1358 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
1360 const unsigned char magic
= 0xcf;
1367 buf
->IdeReg
= *regs
;
1368 buf
->DataBufferSize
= datasize
;
1370 buf
->DataBuffer
[0] = magic
;
1372 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
1373 buf
, size
, buf
, size
, &num_out
, NULL
)) {
1374 long err
= GetLastError();
1375 if (ata_debugmode
) {
1376 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
1377 print_ide_regs_io(regs
, NULL
);
1379 VirtualFree(buf
, 0, MEM_RELEASE
);
1380 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1385 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
1386 if (ata_debugmode
) {
1387 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
1388 print_ide_regs_io(regs
, &buf
->IdeReg
);
1390 VirtualFree(buf
, 0, MEM_RELEASE
);
1395 // Check and copy data
1397 if ( num_out
!= size
1398 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
1399 if (ata_debugmode
) {
1400 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
1401 num_out
, buf
->DataBufferSize
);
1402 print_ide_regs_io(regs
, &buf
->IdeReg
);
1404 VirtualFree(buf
, 0, MEM_RELEASE
);
1408 memcpy(data
, buf
->DataBuffer
, datasize
);
1411 if (ata_debugmode
> 1) {
1412 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
1413 num_out
, buf
->DataBufferSize
);
1414 print_ide_regs_io(regs
, &buf
->IdeReg
);
1416 *regs
= buf
->IdeReg
;
1418 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
1419 VirtualFree(buf
, 0, MEM_RELEASE
);
1424 /////////////////////////////////////////////////////////////////////////////
1425 // ATA PASS THROUGH (Win2003, XP SP2)
1428 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
1429 // transfer per command. Therefore, multi-sector transfers are only supported
1430 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
1431 // or READ/WRITE LOG EXT work only with single sector transfers.
1432 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
1434 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
1436 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
1438 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
1441 ATA_PASS_THROUGH_EX apt
;
1443 UCHAR ucDataBuf
[max_sectors
* 512];
1444 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
1446 const unsigned char magic
= 0xcf;
1448 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
1449 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
1450 //ab.apt.PathId = 0;
1451 //ab.apt.TargetId = 0;
1453 ab
.apt
.TimeOutValue
= 10;
1454 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
1455 ab
.apt
.DataBufferOffset
= size
;
1458 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1462 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
1463 ab
.apt
.DataTransferLength
= datasize
;
1465 ab
.ucDataBuf
[0] = magic
;
1467 else if (datasize
< 0) {
1468 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1472 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
1473 ab
.apt
.DataTransferLength
= -datasize
;
1475 memcpy(ab
.ucDataBuf
, data
, -datasize
);
1478 assert(ab
.apt
.AtaFlags
== 0);
1479 assert(ab
.apt
.DataTransferLength
== 0);
1482 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
1483 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
1484 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
1488 *ptfregs
= *prev_regs
;
1489 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
1493 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
1494 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
1495 long err
= GetLastError();
1496 if (ata_debugmode
) {
1497 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
1498 print_ide_regs_io(regs
, NULL
);
1500 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1505 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
1506 if (ata_debugmode
) {
1507 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
1508 print_ide_regs_io(regs
, ctfregs
);
1514 // Check and copy data
1516 if ( num_out
!= size
1517 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
1518 if (ata_debugmode
) {
1519 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out
);
1520 print_ide_regs_io(regs
, ctfregs
);
1525 memcpy(data
, ab
.ucDataBuf
, datasize
);
1528 if (ata_debugmode
> 1) {
1529 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1530 print_ide_regs_io(regs
, ctfregs
);
1534 *prev_regs
= *ptfregs
;
1540 /////////////////////////////////////////////////////////////////////////////
1541 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
1543 // undocumented SCSI opcode to for ATA passthrough
1544 #define SCSIOP_ATA_PASSTHROUGH 0xCC
1546 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1549 SCSI_PASS_THROUGH spt
;
1551 UCHAR ucSenseBuf
[32];
1552 UCHAR ucDataBuf
[512];
1553 } SCSI_PASS_THROUGH_WITH_BUFFERS
;
1555 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
1559 const unsigned char magic
= 0xcf;
1561 memset(&sb
, 0, sizeof(sb
));
1562 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH
);
1563 //sb.spt.PathId = 0;
1564 sb
.spt
.TargetId
= 1;
1566 sb
.spt
.CdbLength
= 10; sb
.spt
.SenseInfoLength
= 24;
1567 sb
.spt
.TimeOutValue
= 10;
1568 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
1569 size
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
1570 sb
.spt
.DataBufferOffset
= size
;
1573 if (datasize
> sizeof(sb
.ucDataBuf
)) {
1577 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
1578 sb
.spt
.DataTransferLength
= datasize
;
1580 sb
.ucDataBuf
[0] = magic
;
1583 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
1584 //sb.spt.DataTransferLength = 0;
1587 // Use pseudo SCSI command followed by registers
1588 sb
.spt
.Cdb
[0] = SCSIOP_ATA_PASSTHROUGH
;
1589 cdbregs
= (IDEREGS
*)(sb
.spt
.Cdb
+2);
1592 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_PASS_THROUGH
,
1593 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1594 long err
= GetLastError();
1596 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err
);
1597 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1601 // Cannot check ATA status, because command does not return IDEREGS
1603 // Check and copy data
1605 if ( num_out
!= size
1606 || (sb
.ucDataBuf
[0] == magic
&& !nonempty(sb
.ucDataBuf
+1, datasize
-1))) {
1607 if (ata_debugmode
) {
1608 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out
);
1609 print_ide_regs_io(regs
, NULL
);
1614 memcpy(data
, sb
.ucDataBuf
, datasize
);
1617 if (ata_debugmode
> 1) {
1618 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1619 print_ide_regs_io(regs
, NULL
);
1625 /////////////////////////////////////////////////////////////////////////////
1626 // SMART IOCTL via SCSI MINIPORT ioctl
1628 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
1629 // miniport driver (via SCSI port driver scsiport.sys).
1630 // It can be used to skip the missing or broken handling of some SMART
1631 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
1633 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
1636 DWORD code
= 0; const char * name
= 0;
1637 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
1638 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
1640 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
1641 case ATA_SMART_READ_VALUES
:
1642 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
1643 case ATA_SMART_READ_THRESHOLDS
:
1644 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
1645 case ATA_SMART_ENABLE
:
1646 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
1647 case ATA_SMART_DISABLE
:
1648 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
1649 case ATA_SMART_STATUS
:
1650 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
1651 case ATA_SMART_AUTOSAVE
:
1652 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
1653 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
1654 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
1655 case ATA_SMART_IMMEDIATE_OFFLINE
:
1656 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
1657 case ATA_SMART_AUTO_OFFLINE
:
1658 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
1659 case ATA_SMART_READ_LOG_SECTOR
:
1660 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
1661 case ATA_SMART_WRITE_LOG_SECTOR
:
1662 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
1671 SRB_IO_CONTROL srbc
;
1674 SENDCMDOUTPARAMS out
;
1678 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
1679 memset(&sb
, 0, sizeof(sb
));
1683 if (datasize
> (int)sizeof(sb
.space
)+1) {
1689 else if (datasize
< 0) {
1690 if (-datasize
> (int)sizeof(sb
.space
)+1) {
1695 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
1697 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1698 size
= sizeof(IDEREGS
);
1701 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1702 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
1703 sb
.srbc
.Timeout
= 60; // seconds
1704 sb
.srbc
.ControlCode
= code
;
1705 //sb.srbc.ReturnCode = 0;
1706 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
1707 sb
.params
.in
.irDriveRegs
= *regs
;
1708 sb
.params
.in
.cBufferSize
= size
;
1710 // Call miniport ioctl
1711 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
1713 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1714 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1715 long err
= GetLastError();
1716 if (ata_debugmode
) {
1717 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
1718 print_ide_regs_io(regs
, NULL
);
1720 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1725 if (sb
.srbc
.ReturnCode
) {
1726 if (ata_debugmode
) {
1727 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name
, sb
.srbc
.ReturnCode
);
1728 print_ide_regs_io(regs
, NULL
);
1734 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
1735 if (ata_debugmode
) {
1736 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1737 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
1738 print_ide_regs_io(regs
, NULL
);
1740 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1744 if (ata_debugmode
> 1) {
1745 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1746 num_out
, sb
.params
.out
.cBufferSize
);
1747 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
1748 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
1752 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
1753 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1754 memcpy(regs
, sb
.params
.out
.bBuffer
, sizeof(IDEREGS
));
1760 /////////////////////////////////////////////////////////////////////////////
1762 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1764 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1767 SRB_IO_CONTROL srbc
;
1771 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1773 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1777 memset(&sb
, 0, sizeof(sb
));
1778 strcpy((char *)sb
.srbc
.Signature
, "<3ware>");
1779 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1780 sb
.srbc
.Timeout
= 60; // seconds
1781 sb
.srbc
.ControlCode
= 0xA0000000;
1782 sb
.srbc
.ReturnCode
= 0;
1783 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1785 sb
.regs
.bReserved
= port
;
1788 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1789 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1790 long err
= GetLastError();
1791 if (ata_debugmode
) {
1792 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1793 print_ide_regs_io(regs
, NULL
);
1795 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1799 if (sb
.srbc
.ReturnCode
) {
1800 if (ata_debugmode
) {
1801 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb
.srbc
.ReturnCode
);
1802 print_ide_regs_io(regs
, NULL
);
1810 memcpy(data
, sb
.buffer
, datasize
);
1812 if (ata_debugmode
> 1) {
1813 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out
);
1814 print_ide_regs_io(regs
, &sb
.regs
);
1822 /////////////////////////////////////////////////////////////////////////////
1824 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1825 // 3DM/CLI "Rescan Controller" function does not to always update it.
1827 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1829 SRB_IO_CONTROL srbc
;
1830 memset(&srbc
, 0, sizeof(srbc
));
1831 strcpy((char *)srbc
.Signature
, "<3ware>");
1832 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1833 srbc
.Timeout
= 60; // seconds
1834 srbc
.ControlCode
= 0xCC010014;
1835 srbc
.ReturnCode
= 0;
1839 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1840 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1841 long err
= GetLastError();
1843 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1844 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1847 if (srbc
.ReturnCode
) {
1849 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc
.ReturnCode
);
1853 if (ata_debugmode
> 1)
1854 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1860 /////////////////////////////////////////////////////////////////////////////
1862 // Routines for pseudo device /dev/tw_cli/*
1863 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1866 // Get clipboard data
1868 static int get_clipboard(char * data
, int datasize
)
1870 if (!OpenClipboard(NULL
))
1872 HANDLE h
= GetClipboardData(CF_TEXT
);
1877 const void * p
= GlobalLock(h
);
1878 int n
= GlobalSize(h
);
1888 // Run a command, write stdout to dataout
1889 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
1891 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
1893 // Create stdout pipe
1894 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
1895 HANDLE pipe_out_w
, h
;
1896 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
1898 HANDLE self
= GetCurrentProcess();
1900 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
1901 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
1902 CloseHandle(pipe_out_w
);
1906 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
1907 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
1908 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1913 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
1914 si
.hStdInput
= INVALID_HANDLE_VALUE
;
1915 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
1916 si
.dwFlags
= STARTF_USESTDHANDLES
;
1917 PROCESS_INFORMATION pi
;
1919 NULL
, const_cast<char *>(cmd
),
1920 NULL
, NULL
, TRUE
/*inherit*/,
1921 CREATE_NO_WINDOW
/*do not create a new console window*/,
1922 NULL
, NULL
, &si
, &pi
)) {
1923 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1926 CloseHandle(pi
.hThread
);
1927 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
1929 // Copy stdout to output buffer
1931 while (i
< outsize
) {
1933 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
1937 CloseHandle(pipe_out_r
);
1939 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1940 CloseHandle(pi
.hProcess
);
1945 static const char * findstr(const char * str
, const char * sub
)
1947 const char * s
= strstr(str
, sub
);
1948 return (s
? s
+strlen(sub
) : "");
1952 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
1954 int srclen
= strcspn(src
, "\r\n");
1956 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
1957 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
1959 if (i
< destsize
-1 && i
< srclen
)
1964 // TODO: This is OS independent
1966 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1967 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
1968 m_ident_valid(false), m_smart_valid(false)
1970 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
1971 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
1975 bool win_tw_cli_device::is_open() const
1977 return (m_ident_valid
|| m_smart_valid
);
1981 bool win_tw_cli_device::open()
1983 m_ident_valid
= m_smart_valid
= false;
1984 const char * name
= skipdev(get_dev_name());
1985 // Read tw_cli or 3DM browser output into buffer
1987 int size
= -1, n1
= -1, n2
= -1;
1988 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
1989 size
= get_clipboard(buffer
, sizeof(buffer
));
1991 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
1992 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
1994 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
1995 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
1997 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
1998 if (ata_debugmode
> 1)
1999 pout("%s: Run: \"%s\"\n", name
, cmd
);
2000 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
2003 return set_err(EINVAL
);
2006 if (ata_debugmode
> 1)
2007 pout("%s: Read %d bytes\n", name
, size
);
2009 return set_err(ENOENT
);
2010 if (size
>= (int)sizeof(buffer
))
2011 return set_err(EIO
);
2014 if (ata_debugmode
> 1)
2015 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
2017 // Fake identify sector
2018 ASSERT_SIZEOF(ata_identify_device
, 512);
2019 ata_identify_device
* id
= &m_ident_buf
;
2020 memset(id
, 0, sizeof(*id
));
2021 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
2022 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
2023 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
2024 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
2025 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
2027 id
->words047_079
[49-47] = 0x0200; // size valid
2028 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
2029 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
2031 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2032 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2034 // Parse smart data hex dump
2035 const char * s
= findstr(buffer
, "Drive Smart Data:");
2037 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
2039 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
2041 const char * s1
= findstr(s
, "<td class"); // html version
2044 s
+= strcspn(s
, "\r\n");
2047 s
= buffer
; // try raw hex dump without header
2049 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
2052 unsigned x
= ~0; int n
= -1;
2053 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
2055 sd
[i
] = (unsigned char)x
;
2056 if (!(++i
< 512 && n
> 0))
2059 if (*s
== '<') // "<br>"
2060 s
+= strcspn(s
, "\r\n");
2063 if (!id
->model
[1]) {
2064 // No useful data found
2065 char * err
= strstr(buffer
, "Error:");
2067 err
= strstr(buffer
, "error :");
2068 if (err
&& (err
= strchr(err
, ':'))) {
2069 // Show tw_cli error message
2071 err
[strcspn(err
, "\r\n")] = 0;
2072 return set_err(EIO
, "%s", err
);
2074 return set_err(EIO
);
2079 m_ident_valid
= true;
2080 m_smart_valid
= !!sd
;
2085 bool win_tw_cli_device::close()
2087 m_ident_valid
= m_smart_valid
= false;
2092 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
2098 memcpy(data
, &m_ident_buf
, 512);
2103 memcpy(data
, &m_smart_buf
, 512);
2107 case STATUS_CHECK
: // Fake "good" SMART status
2112 // Arrive here for all unsupported commands
2118 /////////////////////////////////////////////////////////////////////////////
2119 // IOCTL_STORAGE_QUERY_PROPERTY
2121 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
2122 STORAGE_DEVICE_DESCRIPTOR desc
;
2126 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
2127 // (This works without admin rights)
2129 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
2131 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
2132 memset(data
, 0, sizeof(*data
));
2135 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
2136 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
2137 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
2138 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
2143 if (ata_debugmode
> 1 || scsi_debugmode
> 1) {
2144 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
2146 " Product: \"%s\"\n"
2147 " Revision: \"%s\"\n"
2149 " BusType: 0x%02x\n",
2150 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: "(null)"),
2151 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: "(null)"),
2152 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: "(null)"),
2153 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
2160 /////////////////////////////////////////////////////////////////////////////
2161 // IOCTL_STORAGE_PREDICT_FAILURE
2163 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
2164 // or -1 on error, opionally return VendorSpecific data.
2165 // (This works without admin rights)
2167 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
2169 STORAGE_PREDICT_FAILURE pred
;
2170 memset(&pred
, 0, sizeof(pred
));
2173 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
2174 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
2175 if (ata_debugmode
> 1)
2176 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
2181 if (ata_debugmode
> 1) {
2182 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
2183 " PredictFailure: 0x%08lx\n"
2184 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
2185 pred
.PredictFailure
,
2186 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
2187 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
2191 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
2192 return (!pred
.PredictFailure
? 0 : 1);
2196 /////////////////////////////////////////////////////////////////////////////
2198 // Return true if Intel ICHxR RAID volume
2199 static bool is_intel_raid_volume(const STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
2201 if (!(data
->desc
.VendorIdOffset
&& data
->desc
.ProductIdOffset
))
2203 const char * vendor
= data
->raw
+ data
->desc
.VendorIdOffset
;
2204 if (!(!strnicmp(vendor
, "Intel", 5) && strspn(vendor
+5, " ") == strlen(vendor
+5)))
2206 if (strnicmp(data
->raw
+ data
->desc
.ProductIdOffset
, "Raid ", 5))
2211 // get DEV_* for open handle
2212 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
2214 // Get BusType from device descriptor
2215 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
2216 if (storage_query_property_ioctl(hdevice
, &data
))
2219 // Newer BusType* values are missing in older includes
2220 switch ((int)data
.desc
.BusType
) {
2222 case 0x0b: // BusTypeSata
2224 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
2229 // Intel ICHxR RAID volume: reports SMART_GET_VERSION but does not support SMART_*
2230 if (is_intel_raid_volume(&data
))
2232 // LSI/3ware RAID volume: supports SMART_*
2233 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
2237 case 0x09: // BusTypeiScsi
2238 case 0x0a: // BusTypeSas
2250 // get DEV_* for device path
2251 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
2254 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
2255 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2256 if (h
== INVALID_HANDLE_VALUE
) {
2258 h
= CreateFileA(path
, 0,
2259 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
2260 if (h
== INVALID_HANDLE_VALUE
)
2263 if (ata_debugmode
> 1 || scsi_debugmode
> 1)
2264 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
2265 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
2270 // get DEV_* for physical drive number
2271 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
2274 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
2275 return get_controller_type(path
, ata_version_ex
);
2278 static win_dev_type
get_phy_drive_type(int drive
)
2280 return get_phy_drive_type(drive
, 0);
2283 // get DEV_* for logical drive number
2284 static win_dev_type
get_log_drive_type(int drive
)
2287 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
2288 return get_controller_type(path
);
2291 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
2292 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
2294 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
2295 if (storage_query_property_ioctl(hdevice
, &data
))
2298 memset(id
, 0, sizeof(*id
));
2300 // Some drivers split ATA model string into VendorId and ProductId,
2301 // others return it as ProductId only.
2302 char model
[sizeof(id
->model
) + 1] = "";
2305 if (data
.desc
.VendorIdOffset
) {
2306 for ( ;i
< sizeof(model
)-1 && data
.raw
[data
.desc
.VendorIdOffset
+i
]; i
++)
2307 model
[i
] = data
.raw
[data
.desc
.VendorIdOffset
+i
];
2310 if (data
.desc
.ProductIdOffset
) {
2311 while (i
> 1 && model
[i
-2] == ' ') // Keep last blank from VendorId
2313 // Ignore VendorId "ATA"
2314 if (i
<= 4 && !strncmp(model
, "ATA", 3) && (i
== 3 || model
[3] == ' '))
2316 for (unsigned j
= 0; i
< sizeof(model
)-1 && data
.raw
[data
.desc
.ProductIdOffset
+j
]; i
++, j
++)
2317 model
[i
] = data
.raw
[data
.desc
.ProductIdOffset
+j
];
2320 while (i
> 0 && model
[i
-1] == ' ')
2323 copy_swapped(id
->model
, model
, sizeof(id
->model
));
2325 if (data
.desc
.ProductRevisionOffset
)
2326 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
2328 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
2329 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
2334 /////////////////////////////////////////////////////////////////////////////
2335 // USB ID detection using WMI
2337 // Get USB ID for a physical drive number
2338 static bool get_usb_id(int drive
, unsigned short & vendor_id
, unsigned short & product_id
)
2340 bool debug
= (scsi_debugmode
> 1);
2343 if (!ws
.connect()) {
2345 pout("WMI connect failed\n");
2351 if (!ws
.query1(wo
, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive
))
2354 std::string name
= wo
.get_str("Model");
2356 pout("PhysicalDrive%d, \"%s\":\n", drive
, name
.c_str());
2358 // Get USB_CONTROLLER -> DEVICE associations
2360 if (!ws
.query(we
, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
2363 unsigned short usb_venid
= 0, prev_usb_venid
= 0;
2364 unsigned short usb_proid
= 0, prev_usb_proid
= 0;
2365 std::string prev_usb_ant
;
2366 std::string prev_ant
, ant
, dep
;
2368 const regular_expression
regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED
);
2370 while (we
.next(wo
)) {
2372 // Find next 'USB_CONTROLLER, DEVICE' pair
2373 ant
= wo
.get_str("Antecedent");
2374 dep
= wo
.get_str("Dependent");
2376 if (debug
&& ant
!= prev_ant
)
2377 pout(" %s:\n", ant
.c_str());
2380 regmatch_t match
[2];
2381 if (!(regex
.execute(dep
.c_str(), 2, match
) && match
[1].rm_so
>= 0)) {
2383 pout(" | (\"%s\")\n", dep
.c_str());
2387 std::string
devid(dep
.c_str()+match
[1].rm_so
, match
[1].rm_eo
-match
[1].rm_so
);
2389 if (str_starts_with(devid
, "USB\\\\VID_")) {
2390 // USB bridge entry, save CONTROLLER, ID
2392 if (!(sscanf(devid
.c_str(), "USB\\\\VID_%4hx&PID_%4hx%n",
2393 &prev_usb_venid
, &prev_usb_proid
, &nc
) == 2 && nc
== 9+4+5+4)) {
2394 prev_usb_venid
= prev_usb_proid
= 0;
2398 pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid
.c_str(), prev_usb_venid
, prev_usb_proid
);
2401 else if (str_starts_with(devid
, "USBSTOR\\\\")) {
2402 // USBSTOR device found
2404 pout(" +--> \"%s\"\n", devid
.c_str());
2408 if (!ws
.query1(wo2
, "SELECT Name FROM Win32_PnPEntity WHERE DeviceID=\"%s\"", devid
.c_str()))
2410 std::string name2
= wo2
.get_str("Name");
2412 // Continue if not name of physical disk drive
2413 if (name2
!= name
) {
2415 pout(" +---> (\"%s\")\n", name2
.c_str());
2419 // Fail if previos USB bridge is associated to other controller or ID is unknown
2420 if (!(ant
== prev_usb_ant
&& prev_usb_venid
)) {
2422 pout(" +---> \"%s\" (Error: No USB bridge found)\n", name2
.c_str());
2426 // Handle multiple devices with same name
2428 // Fail if multiple devices with same name have different USB bridge types
2429 if (!(usb_venid
== prev_usb_venid
&& usb_proid
== prev_usb_proid
)) {
2431 pout(" +---> \"%s\" (Error: More than one USB ID found)\n", name2
.c_str());
2437 usb_venid
= prev_usb_venid
;
2438 usb_proid
= prev_usb_proid
;
2440 pout(" +===> \"%s\" [0x%04x:0x%04x]\n", name2
.c_str(), usb_venid
, usb_proid
);
2442 // Continue to check for duplicate names ...
2446 pout(" | \"%s\"\n", devid
.c_str());
2453 vendor_id
= usb_venid
;
2454 product_id
= usb_proid
;
2460 /////////////////////////////////////////////////////////////////////////////
2462 // Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
2463 // returns: 1=active, 0=standby, -1=error
2464 // (This would also work for SCSI drives)
2466 static int get_device_power_state(HANDLE hdevice
)
2468 static bool unsupported
= false;
2475 static DWORD kernel_dll_pid
= 0;
2477 static BOOL (WINAPI
* GetDevicePowerState_p
)(HANDLE
, BOOL
*) = 0;
2479 if (!GetDevicePowerState_p
2481 || kernel_dll_pid
!= GetCurrentProcessId() // detect fork()
2484 if (!(GetDevicePowerState_p
= (BOOL (WINAPI
*)(HANDLE
, BOOL
*))
2485 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) {
2487 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
2493 kernel_dll_pid
= GetCurrentProcessId();
2498 if (!GetDevicePowerState_p(hdevice
, &state
)) {
2499 long err
= GetLastError();
2501 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
2502 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
2503 // TODO: This may not work as expected on transient errors,
2504 // because smartd interprets -1 as SLEEP mode regardless of errno.
2508 if (ata_debugmode
> 1)
2509 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
2514 /////////////////////////////////////////////////////////////////////////////
2517 // Print SMARTVSD error message, return errno
2519 static int smartvsd_error()
2521 char path
[MAX_PATH
];
2523 if (!(5 <= (len
= GetSystemDirectoryA(path
, MAX_PATH
)) && len
< MAX_PATH
/2))
2525 // SMARTVSD.VXD present?
2526 strcpy(path
+len
, "\\IOSUBSYS\\SMARTVSD.VXD");
2527 if (!access(path
, 0)) {
2528 // Yes, standard IDE driver used?
2530 if ( (h
= CreateFileA("\\\\.\\ESDI_506",
2531 GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2532 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
2533 && GetLastError() == ERROR_FILE_NOT_FOUND
) {
2534 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
2538 if (h
!= INVALID_HANDLE_VALUE
) // should not happen
2540 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
2545 strcpy(path
+len
, "\\SMARTVSD.VXD");
2546 if (!access(path
, 0)) {
2547 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
2548 // (http://support.microsoft.com/kb/265854/en-us).
2550 pout("SMART driver is not properly installed,\n"
2551 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
2552 " and reboot Windows.\n", path
, path
);
2555 // Some Windows versions do not provide SMARTVSD.VXD
2556 // (http://support.microsoft.com/kb/199886/en-us).
2558 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path
);
2564 #endif // WIN9X_SUPPORT
2566 // Get default ATA device options
2568 static const char * ata_get_def_options()
2570 DWORD ver
= GetVersion();
2571 if ((ver
& 0x80000000) || (ver
& 0xff) < 4) // Win9x/ME
2572 return "s"; // SMART_* only
2573 else if ((ver
& 0xff) == 4) // WinNT4
2574 return "sc"; // SMART_*, SCSI_PASS_THROUGH
2575 else // WinXP, 2003, Vista
2576 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
2577 // STORAGE_*, SCSI_MINIPORT_*
2581 // Common routines for devices with HANDLEs
2583 win_smart_device::~win_smart_device() throw()
2585 if (m_fh
!= INVALID_HANDLE_VALUE
)
2586 ::CloseHandle(m_fh
);
2589 bool win_smart_device::is_open() const
2591 return (m_fh
!= INVALID_HANDLE_VALUE
);
2594 bool win_smart_device::close()
2596 if (m_fh
== INVALID_HANDLE_VALUE
)
2598 BOOL rc
= ::CloseHandle(m_fh
);
2599 m_fh
= INVALID_HANDLE_VALUE
;
2605 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2606 : smart_device(intf
, dev_name
, "ata", req_type
),
2607 m_usr_options(false),
2609 m_id_is_cached(false),
2617 win_ata_device::~win_ata_device() throw()
2624 bool win_ata_device::open()
2626 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2627 // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
2628 char drive
[1+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
2629 if ( sscanf(name
, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive
, &n1
, options
, &n2
) >= 1
2630 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2631 return open(drive
[0] - 'a', -1, options
, -1);
2633 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
2634 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
2636 if ( sscanf(name
, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
2637 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2638 return open(drive
[0] - 'a', -1, options
, port
);
2640 // pd<m>,N => Physical drive <m>, RAID port N
2641 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
2642 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
2643 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
2644 return open(phydrive
, -1, "", (int)port
);
2646 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2647 int logdrive
= drive_letter(name
);
2648 if (logdrive
>= 0) {
2649 return open(-1, logdrive
, "", -1);
2652 return set_err(EINVAL
);
2656 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
)
2658 // path depends on Windows Version
2660 if (win9x
&& 0 <= phydrive
&& phydrive
<= 7)
2661 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
2662 strcpy(devpath
, (phydrive
<= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
2663 else if (!win9x
&& 0 <= phydrive
&& phydrive
<= 255)
2664 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", phydrive
);
2665 else if (!win9x
&& 0 <= logdrive
&& logdrive
<= 'Z'-'A')
2666 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
2668 return set_err(ENOENT
);
2671 HANDLE h
= INVALID_HANDLE_VALUE
;
2672 if (win9x
|| !(*options
&& !options
[strspn(options
, "fp")])) {
2673 // Open with admin rights
2675 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2676 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2677 NULL
, OPEN_EXISTING
, 0, 0);
2679 if (!win9x
&& h
== INVALID_HANDLE_VALUE
) {
2680 // Open without admin rights
2682 h
= CreateFileA(devpath
, 0,
2683 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2684 NULL
, OPEN_EXISTING
, 0, 0);
2686 if (h
== INVALID_HANDLE_VALUE
) {
2687 long err
= GetLastError();
2689 if (win9x
&& phydrive
<= 3 && err
== ERROR_FILE_NOT_FOUND
)
2692 if (err
== ERROR_FILE_NOT_FOUND
)
2693 set_err(ENOENT
, "%s: not found", devpath
);
2694 else if (err
== ERROR_ACCESS_DENIED
)
2695 set_err(EACCES
, "%s: access denied", devpath
);
2697 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2702 // Warn once if admin rights are missing
2704 static bool noadmin_warning
= false;
2705 if (!noadmin_warning
) {
2706 pout("Warning: Limited functionality due to missing admin rights\n");
2707 noadmin_warning
= true;
2711 if (ata_debugmode
> 1)
2712 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
2714 m_usr_options
= false;
2716 // Save user options
2717 m_options
= options
; m_usr_options
= true;
2720 // RAID: SMART_* and SCSI_MINIPORT
2723 // Set default options according to Windows version
2724 static const char * def_options
= ata_get_def_options();
2725 m_options
= def_options
;
2728 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
2729 m_drive
= 0; m_port
= port
;
2730 if (!win9x
&& port
< 0)
2733 // Win9X/ME: Get drive map
2734 // RAID: Get port map
2735 GETVERSIONINPARAMS_EX vers_ex
;
2736 int devmap
= smart_get_version(h
, &vers_ex
);
2738 // 3ware RAID if vendor id present
2739 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
2741 unsigned long portmap
= 0;
2742 if (port
>= 0 && devmap
>= 0) {
2743 // 3ware RAID: check vendor id
2745 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
2746 "This is no 3ware 9000 controller or driver has no SMART support.\n",
2747 vers_ex
.wIdentifier
);
2751 portmap
= vers_ex
.dwDeviceMapEx
;
2754 pout("%s: ATA driver has no SMART support\n", devpath
);
2755 if (!is_permissive()) {
2757 return set_err(ENOSYS
);
2761 m_smartver_state
= 1;
2764 // 3ware RAID: update devicemap first
2766 if (!update_3ware_devicemap_ioctl(h
)) {
2767 if ( smart_get_version(h
, &vers_ex
) >= 0
2768 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
2769 portmap
= vers_ex
.dwDeviceMapEx
;
2771 // Check port existence
2772 if (!(portmap
& (1L << port
))) {
2773 if (!is_permissive()) {
2775 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
2781 // Win9x/ME: Check device presence & type
2782 if (((devmap
>> (phydrive
& 0x3)) & 0x11) != 0x01) {
2783 unsigned char atapi
= (devmap
>> (phydrive
& 0x3)) & 0x10;
2784 // Win9x drive existence check may not work as expected
2785 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
2786 // (The related KB Article Q196120 is no longer available)
2787 if (!is_permissive()) {
2789 return set_err((atapi
? ENOSYS
: ENOENT
), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
2790 devpath
, phydrive
, (atapi
?"is an ATAPI device":"does not exist"), devmap
);
2793 // Drive number must be passed to ioctl
2794 m_drive
= (phydrive
& 0x3);
2801 // Scan for ATA drives on Win9x/ME
2803 bool win9x_smart_interface::ata_scan(smart_device_list
& devlist
)
2806 const char devpath
[] = "\\\\.\\SMARTVSD";
2807 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2808 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0);
2809 if (h
== INVALID_HANDLE_VALUE
) {
2810 if (ata_debugmode
> 1)
2811 pout(" %s: Open failed, Error=%ld\n", devpath
, GetLastError());
2812 return true; // SMARTVSD.VXD missing or no ATA devices
2816 int devmap
= smart_get_version(h
);
2819 return true; // Should not happen
2821 // Check ATA device presence, remove ATAPI devices
2822 devmap
= (devmap
& 0xf) & ~((devmap
>> 4) & 0xf);
2824 for (int i
= 0; i
< 4; i
++) {
2825 if (!(devmap
& (1 << i
)))
2827 sprintf(name
, "/dev/hd%c", 'a'+i
);
2828 devlist
.push_back( new win_ata_device(this, name
, "ata") );
2833 #endif // WIN9X_SUPPORT
2836 /////////////////////////////////////////////////////////////////////////////
2838 // Interface to ATA devices
2839 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2841 // No multi-sector support for now, see above
2842 // warning about IOCTL_ATA_PASS_THROUGH
2843 if (!ata_cmd_is_ok(in
,
2844 true, // data_out_support
2845 false, // !multi_sector_support
2846 true) // ata_48bit_support
2850 // 3ware RAID: SMART DISABLE without port number disables SMART functions
2851 if ( m_is_3ware
&& m_port
< 0
2852 && in
.in_regs
.command
== ATA_SMART_CMD
2853 && in
.in_regs
.features
== ATA_SMART_DISABLE
)
2854 return set_err(ENOSYS
, "SMART DISABLE requires 3ware port number");
2856 // Determine ioctl functions valid for this ATA cmd
2857 const char * valid_options
= 0;
2859 switch (in
.in_regs
.command
) {
2860 case ATA_IDENTIFY_DEVICE
:
2861 case ATA_IDENTIFY_PACKET_DEVICE
:
2862 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2863 // and SCSI_MINIPORT_* if requested by user
2864 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2867 case ATA_CHECK_POWER_MODE
:
2868 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
2869 valid_options
= "pai3";
2873 switch (in
.in_regs
.features
) {
2874 case ATA_SMART_READ_VALUES
:
2875 case ATA_SMART_READ_THRESHOLDS
:
2876 case ATA_SMART_AUTOSAVE
:
2877 case ATA_SMART_ENABLE
:
2878 case ATA_SMART_DISABLE
:
2879 case ATA_SMART_AUTO_OFFLINE
:
2880 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2881 // and SCSI_MINIPORT_* if requested by user
2882 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2885 case ATA_SMART_IMMEDIATE_OFFLINE
:
2886 // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
2887 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ || win9x
?
2888 "saicm3" : "aicm3");
2891 case ATA_SMART_READ_LOG_SECTOR
:
2892 // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
2893 // Try SCSI_MINIPORT also to skip buggy class driver
2894 // SMART functions do not support multi sector I/O.
2896 valid_options
= (m_usr_options
|| win9x
? "saicm3" : "aicm3");
2898 valid_options
= "a";
2901 case ATA_SMART_WRITE_LOG_SECTOR
:
2902 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
2903 // but SCSI_MINIPORT_* only if requested by user and single sector.
2904 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
2907 case ATA_SMART_STATUS
:
2908 // May require lba_mid,lba_high register return
2909 if (in
.out_needed
.is_set())
2910 valid_options
= (m_usr_options
? "saimf" : "saif");
2912 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2916 // Unknown SMART command, handle below
2922 // Other ATA command, handle below
2926 if (!valid_options
) {
2927 // No special ATA command found above, select a generic pass through ioctl.
2928 if (!( in
.direction
== ata_cmd_in::no_data
2929 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
2930 || in
.in_regs
.is_48bit_cmd() )
2931 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
2932 valid_options
= "a";
2933 else if (in
.out_needed
.is_set())
2934 // Need output registers: ATA/IDE_PASS_THROUGH
2935 valid_options
= "ai";
2937 valid_options
= "aic";
2941 // Restrict to IOCTL_STORAGE_*
2942 if (strchr(valid_options
, 'f'))
2943 valid_options
= "f";
2944 else if (strchr(valid_options
, 'p'))
2945 valid_options
= "p";
2947 return set_err(ENOSYS
, "Function requires admin rights");
2951 IDEREGS regs
, prev_regs
;
2953 const ata_in_regs
& lo
= in
.in_regs
;
2954 regs
.bFeaturesReg
= lo
.features
;
2955 regs
.bSectorCountReg
= lo
.sector_count
;
2956 regs
.bSectorNumberReg
= lo
.lba_low
;
2957 regs
.bCylLowReg
= lo
.lba_mid
;
2958 regs
.bCylHighReg
= lo
.lba_high
;
2959 regs
.bDriveHeadReg
= lo
.device
;
2960 regs
.bCommandReg
= lo
.command
;
2963 if (in
.in_regs
.is_48bit_cmd()) {
2964 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2965 prev_regs
.bFeaturesReg
= hi
.features
;
2966 prev_regs
.bSectorCountReg
= hi
.sector_count
;
2967 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
2968 prev_regs
.bCylLowReg
= hi
.lba_mid
;
2969 prev_regs
.bCylHighReg
= hi
.lba_high
;
2970 prev_regs
.bDriveHeadReg
= hi
.device
;
2971 prev_regs
.bCommandReg
= hi
.command
;
2972 prev_regs
.bReserved
= 0;
2975 // Set data direction
2978 switch (in
.direction
) {
2979 case ata_cmd_in::no_data
:
2981 case ata_cmd_in::data_in
:
2982 datasize
= (int)in
.size
;
2983 data
= (char *)in
.buffer
;
2985 case ata_cmd_in::data_out
:
2986 datasize
= -(int)in
.size
;
2987 data
= (char *)in
.buffer
;
2990 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
2995 // Try all valid ioctls in the order specified in m_options
2996 bool powered_up
= false;
2997 bool out_regs_set
= false;
2998 bool id_is_cached
= false;
2999 const char * options
= m_options
.c_str();
3001 for (int i
= 0; ; i
++) {
3002 char opt
= options
[i
];
3005 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
3006 // Power up reported by GetDevicePowerState() and no ioctl available
3007 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
3008 regs
.bSectorCountReg
= 0xff;
3009 out_regs_set
= true;
3013 return set_err(ENOSYS
);
3015 if (!strchr(valid_options
, opt
))
3016 // Invalid for this command
3020 assert( datasize
== 0 || datasize
== 512
3021 || (datasize
== -512 && strchr("am", opt
))
3022 || (datasize
> 512 && opt
== 'a'));
3027 // call SMART_GET_VERSION once for each drive
3028 if (m_smartver_state
> 1) {
3029 rc
= -1; errno
= ENOSYS
;
3032 if (!m_smartver_state
) {
3033 assert(m_port
== -1);
3034 GETVERSIONINPARAMS_EX vers_ex
;
3035 if (smart_get_version(get_fh(), &vers_ex
) < 0) {
3036 if (!failuretest_permissive
) {
3037 m_smartver_state
= 2;
3038 rc
= -1; errno
= ENOSYS
;
3041 failuretest_permissive
--;
3044 // 3ware RAID if vendor id present
3045 m_is_3ware
= (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
);
3048 m_smartver_state
= 1;
3050 rc
= smart_ioctl(get_fh(), m_drive
, ®s
, data
, datasize
, m_port
);
3051 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
3052 id_is_cached
= (m_port
< 0 && !win9x
); // Not cached by 3ware or Win9x/ME driver
3055 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
3056 id_is_cached
= (m_port
< 0 && !win9x
);
3059 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
3060 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
3062 out_regs_set
= true;
3065 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
3066 out_regs_set
= true;
3069 rc
= ata_via_scsi_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
3072 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
3073 rc
= get_identify_from_device_property(get_fh(), (ata_identify_device
*)data
);
3074 id_is_cached
= true;
3076 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
3077 case ATA_SMART_READ_VALUES
:
3078 rc
= storage_predict_failure_ioctl(get_fh(), data
);
3082 case ATA_SMART_ENABLE
:
3085 case ATA_SMART_STATUS
:
3086 rc
= storage_predict_failure_ioctl(get_fh());
3088 // Good SMART status
3089 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
3093 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
3098 errno
= ENOSYS
; rc
= -1;
3101 errno
= ENOSYS
; rc
= -1;
3105 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
3106 out_regs_set
= true;
3109 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
3110 rc
= get_device_power_state(get_fh());
3112 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
3113 // spin up the drive => simulate ATA result STANDBY.
3114 regs
.bSectorCountReg
= 0x00;
3115 out_regs_set
= true;
3118 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
3119 // only if it is selected by the device driver => try a passthrough ioctl to get the
3120 // actual mode, if none available simulate ACTIVE/IDLE.
3122 rc
= -1; errno
= ENOSYS
;
3128 // Working ioctl found
3131 if (errno
!= ENOSYS
)
3132 // Abort on I/O error
3133 return set_err(errno
);
3135 out_regs_set
= false;
3136 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
3139 // Return IDEREGS if set
3141 ata_out_regs
& lo
= out
.out_regs
;
3142 lo
.error
= regs
.bFeaturesReg
;
3143 lo
.sector_count
= regs
.bSectorCountReg
;
3144 lo
.lba_low
= regs
.bSectorNumberReg
;
3145 lo
.lba_mid
= regs
.bCylLowReg
;
3146 lo
.lba_high
= regs
.bCylHighReg
;
3147 lo
.device
= regs
.bDriveHeadReg
;
3148 lo
.status
= regs
.bCommandReg
;
3149 if (in
.in_regs
.is_48bit_cmd()) {
3150 ata_out_regs
& hi
= out
.out_regs
.prev
;
3151 hi
.sector_count
= prev_regs
.bSectorCountReg
;
3152 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
3153 hi
.lba_mid
= prev_regs
.bCylLowReg
;
3154 hi
.lba_high
= prev_regs
.bCylHighReg
;
3158 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
3159 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
3160 // Update ata_identify_is_cached() result according to ioctl used.
3161 m_id_is_cached
= id_is_cached
;
3166 // Return true if OS caches the ATA identify sector
3167 bool win_ata_device::ata_identify_is_cached() const
3169 return m_id_is_cached
;
3173 //////////////////////////////////////////////////////////////////////
3176 bool csmi_device::get_phy_info(CSMI_SAS_PHY_INFO
& phy_info
)
3178 // Get driver info to check CSMI support
3179 CSMI_SAS_DRIVER_INFO_BUFFER driver_info_buf
;
3180 memset(&driver_info_buf
, 0, sizeof(driver_info_buf
));
3181 if (!csmi_ioctl(CC_CSMI_SAS_GET_DRIVER_INFO
, &driver_info_buf
.IoctlHeader
, sizeof(driver_info_buf
)))
3184 if (scsi_debugmode
> 1) {
3185 const CSMI_SAS_DRIVER_INFO
& driver_info
= driver_info_buf
.Information
;
3186 pout("CSMI_SAS_DRIVER_INFO:\n");
3187 pout(" Name: \"%.81s\"\n", driver_info
.szName
);
3188 pout(" Description: \"%.81s\"\n", driver_info
.szDescription
);
3189 pout(" Revision: %d.%d\n", driver_info
.usMajorRevision
, driver_info
.usMinorRevision
);
3193 CSMI_SAS_PHY_INFO_BUFFER phy_info_buf
;
3194 memset(&phy_info_buf
, 0, sizeof(phy_info_buf
));
3195 if (!csmi_ioctl(CC_CSMI_SAS_GET_PHY_INFO
, &phy_info_buf
.IoctlHeader
, sizeof(phy_info_buf
)))
3198 phy_info
= phy_info_buf
.Information
;
3199 if (phy_info
.bNumberOfPhys
> sizeof(phy_info
.Phy
)/sizeof(phy_info
.Phy
[0]))
3200 return set_err(EIO
, "CSMI_SAS_PHY_INFO: Bogus NumberOfPhys=%d", phy_info
.bNumberOfPhys
);
3202 if (scsi_debugmode
> 1) {
3203 pout("CSMI_SAS_PHY_INFO: NumberOfPhys=%d\n", phy_info
.bNumberOfPhys
);
3204 for (int i
= 0; i
< phy_info
.bNumberOfPhys
; i
++) {
3205 const CSMI_SAS_PHY_ENTITY
& pe
= phy_info
.Phy
[i
];
3206 const CSMI_SAS_IDENTIFY
& id
= pe
.Identify
, & at
= pe
.Attached
;
3207 pout("Phy[%d] Port: 0x%02x\n", i
, pe
.bPortIdentifier
);
3208 pout(" Type: 0x%02x, 0x%02x\n", id
.bDeviceType
, at
.bDeviceType
);
3209 pout(" InitProto: 0x%02x, 0x%02x\n", id
.bInitiatorPortProtocol
, at
.bInitiatorPortProtocol
);
3210 pout(" TargetProto: 0x%02x, 0x%02x\n", id
.bTargetPortProtocol
, at
.bTargetPortProtocol
);
3211 pout(" PhyIdent: 0x%02x, 0x%02x\n", id
.bPhyIdentifier
, at
.bPhyIdentifier
);
3212 const unsigned char * b
= id
.bSASAddress
;
3213 pout(" SASAddress: %02x %02x %02x %02x %02x %02x %02x %02x, ",
3214 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
3216 pout( "%02x %02x %02x %02x %02x %02x %02x %02x\n",
3217 b
[0], b
[1], b
[2], b
[3], b
[4], b
[5], b
[6], b
[7]);
3224 bool csmi_device::check_phy(const CSMI_SAS_PHY_INFO
& phy_info
, unsigned phy_no
)
3226 // Check Phy presence
3227 if (phy_no
>= phy_info
.bNumberOfPhys
)
3228 return set_err(ENOENT
, "Port %u does not exist (#ports: %d)", phy_no
,
3229 phy_info
.bNumberOfPhys
);
3231 const CSMI_SAS_PHY_ENTITY
& phy_ent
= phy_info
.Phy
[phy_no
];
3232 if (phy_ent
.Attached
.bDeviceType
== CSMI_SAS_NO_DEVICE_ATTACHED
)
3233 return set_err(ENOENT
, "No device on port %u", phy_no
);
3235 switch (phy_ent
.Attached
.bTargetPortProtocol
) {
3236 case CSMI_SAS_PROTOCOL_SATA
:
3237 case CSMI_SAS_PROTOCOL_STP
:
3240 return set_err(ENOENT
, "No SATA device on port %u (protocol: %u)",
3241 phy_no
, phy_ent
.Attached
.bTargetPortProtocol
);
3247 bool csmi_device::select_phy(unsigned phy_no
)
3249 CSMI_SAS_PHY_INFO phy_info
;
3250 if (!get_phy_info(phy_info
))
3254 if (!check_phy(phy_info
, phy_no
))
3257 m_phy_ent
= phy_info
.Phy
[phy_no
];
3262 bool csmi_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
3264 if (!ata_cmd_is_ok(in
,
3265 true, // data_out_support
3266 true, // multi_sector_support
3267 true) // ata_48bit_support
3271 // Create buffer with appropriate size
3272 raw_buffer
pthru_raw_buf(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER
) + in
.size
);
3273 CSMI_SAS_STP_PASSTHRU_BUFFER
* pthru_buf
= (CSMI_SAS_STP_PASSTHRU_BUFFER
*)pthru_raw_buf
.data();
3275 // Set addresses from Phy info
3276 CSMI_SAS_STP_PASSTHRU
& pthru
= pthru_buf
->Parameters
;
3277 const CSMI_SAS_PHY_ENTITY
& phy_ent
= get_phy_ent();
3278 pthru
.bPhyIdentifier
= phy_ent
.Identify
.bPhyIdentifier
;
3279 pthru
.bPortIdentifier
= phy_ent
.bPortIdentifier
;
3280 memcpy(pthru
.bDestinationSASAddress
, phy_ent
.Attached
.bSASAddress
,
3281 sizeof(pthru
.bDestinationSASAddress
));
3282 pthru
.bConnectionRate
= CSMI_SAS_LINK_RATE_NEGOTIATED
;
3284 // Set transfer mode
3285 switch (in
.direction
) {
3286 case ata_cmd_in::no_data
:
3287 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_UNSPECIFIED
;
3289 case ata_cmd_in::data_in
:
3290 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_READ
;
3291 pthru
.uDataLength
= in
.size
;
3293 case ata_cmd_in::data_out
:
3294 pthru
.uFlags
= CSMI_SAS_STP_PIO
| CSMI_SAS_STP_WRITE
;
3295 pthru
.uDataLength
= in
.size
;
3296 memcpy(pthru_buf
->bDataBuffer
, in
.buffer
, in
.size
);
3299 return set_err(EINVAL
, "csmi_ata_device::ata_pass_through: invalid direction=%d",
3303 // Set host-to-device FIS
3305 unsigned char * fis
= pthru
.bCommandFIS
;
3306 const ata_in_regs
& lo
= in
.in_regs
;
3307 const ata_in_regs
& hi
= in
.in_regs
.prev
;
3308 fis
[ 0] = 0x27; // Type: host-to-device FIS
3309 fis
[ 1] = 0x80; // Bit7: Update command register
3310 fis
[ 2] = lo
.command
;
3311 fis
[ 3] = lo
.features
;
3312 fis
[ 4] = lo
.lba_low
;
3313 fis
[ 5] = lo
.lba_mid
;
3314 fis
[ 6] = lo
.lba_high
;
3315 fis
[ 7] = lo
.device
;
3316 fis
[ 8] = hi
.lba_low
;
3317 fis
[ 9] = hi
.lba_mid
;
3318 fis
[10] = hi
.lba_high
;
3319 fis
[11] = hi
.features
;
3320 fis
[12] = lo
.sector_count
;
3321 fis
[13] = hi
.sector_count
;
3325 if (!csmi_ioctl(CC_CSMI_SAS_STP_PASSTHRU
, &pthru_buf
->IoctlHeader
, pthru_raw_buf
.size())) {
3329 // Get device-to-host FIS
3331 const unsigned char * fis
= pthru_buf
->Status
.bStatusFIS
;
3332 ata_out_regs
& lo
= out
.out_regs
;
3333 lo
.status
= fis
[ 2];
3335 lo
.lba_low
= fis
[ 4];
3336 lo
.lba_mid
= fis
[ 5];
3337 lo
.lba_high
= fis
[ 6];
3338 lo
.device
= fis
[ 7];
3339 lo
.sector_count
= fis
[12];
3340 if (in
.in_regs
.is_48bit_cmd()) {
3341 ata_out_regs
& hi
= out
.out_regs
.prev
;
3342 hi
.lba_low
= fis
[ 8];
3343 hi
.lba_mid
= fis
[ 9];
3344 hi
.lba_high
= fis
[10];
3345 hi
.sector_count
= fis
[13];
3350 if (in
.direction
== ata_cmd_in::data_in
)
3351 // TODO: Check ptru_buf->Status.uDataBytes
3352 memcpy(in
.buffer
, pthru_buf
->bDataBuffer
, in
.size
);
3358 //////////////////////////////////////////////////////////////////////
3361 win_csmi_device::win_csmi_device(smart_interface
* intf
, const char * dev_name
,
3362 const char * req_type
)
3363 : smart_device(intf
, dev_name
, "ata", req_type
),
3364 m_fh(INVALID_HANDLE_VALUE
), m_phy_no(0)
3368 win_csmi_device::~win_csmi_device() throw()
3370 if (m_fh
!= INVALID_HANDLE_VALUE
)
3374 bool win_csmi_device::is_open() const
3376 return (m_fh
!= INVALID_HANDLE_VALUE
);
3379 bool win_csmi_device::close()
3381 if (m_fh
== INVALID_HANDLE_VALUE
)
3383 BOOL rc
= CloseHandle(m_fh
);
3384 m_fh
= INVALID_HANDLE_VALUE
;
3389 bool win_csmi_device::open_scsi()
3392 unsigned contr_no
= ~0, phy_no
= ~0; int nc
= -1;
3393 const char * name
= skipdev(get_dev_name());
3394 if (!( sscanf(name
, "csmi%u,%u%n", &contr_no
, &phy_no
, &nc
) >= 0
3395 && nc
== (int)strlen(name
) && contr_no
<= 9 && phy_no
< 32) )
3396 return set_err(EINVAL
);
3398 // Open controller handle
3400 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\Scsi%u:", contr_no
);
3402 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
3403 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3404 (SECURITY_ATTRIBUTES
*)0, OPEN_EXISTING
, 0, 0);
3406 if (h
== INVALID_HANDLE_VALUE
) {
3407 long err
= GetLastError();
3408 if (err
== ERROR_FILE_NOT_FOUND
)
3409 set_err(ENOENT
, "%s: not found", devpath
);
3410 else if (err
== ERROR_ACCESS_DENIED
)
3411 set_err(EACCES
, "%s: access denied", devpath
);
3413 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
3417 if (scsi_debugmode
> 1)
3418 pout(" %s: successfully opened\n", devpath
);
3426 bool win_csmi_device::open()
3431 // Get Phy info for this drive
3432 if (!select_phy(m_phy_no
)) {
3441 bool win_csmi_device::csmi_ioctl(unsigned code
, IOCTL_HEADER
* csmi_buffer
,
3442 unsigned csmi_bufsiz
)
3444 // Determine signature
3447 case CC_CSMI_SAS_GET_DRIVER_INFO
:
3448 sig
= CSMI_ALL_SIGNATURE
; break;
3449 case CC_CSMI_SAS_GET_PHY_INFO
:
3450 case CC_CSMI_SAS_STP_PASSTHRU
:
3451 sig
= CSMI_SAS_SIGNATURE
; break;
3453 return set_err(ENOSYS
, "Unknown CSMI code=%u", code
);
3457 csmi_buffer
->HeaderLength
= sizeof(IOCTL_HEADER
);
3458 strncpy((char *)csmi_buffer
->Signature
, sig
, sizeof(csmi_buffer
->Signature
));
3459 csmi_buffer
->Timeout
= CSMI_SAS_TIMEOUT
;
3460 csmi_buffer
->ControlCode
= code
;
3461 csmi_buffer
->ReturnCode
= 0;
3462 csmi_buffer
->Length
= csmi_bufsiz
- sizeof(IOCTL_HEADER
);
3466 if (!DeviceIoControl(m_fh
, IOCTL_SCSI_MINIPORT
,
3467 csmi_buffer
, csmi_bufsiz
, csmi_buffer
, csmi_bufsiz
, &num_out
, (OVERLAPPED
*)0)) {
3468 long err
= GetLastError();
3470 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, Error=%ld\n", code
, err
);
3471 if ( err
== ERROR_INVALID_FUNCTION
3472 || err
== ERROR_NOT_SUPPORTED
3473 || err
== ERROR_DEV_NOT_EXIST
)
3474 return set_err(ENOSYS
, "CSMI is not supported (Error=%ld)", err
);
3476 return set_err(EIO
, "CSMI(%u) failed with Error=%ld", code
, err
);
3480 if (csmi_buffer
->ReturnCode
) {
3481 if (scsi_debugmode
) {
3482 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) failed, ReturnCode=%lu\n",
3483 code
, csmi_buffer
->ReturnCode
);
3485 return set_err(EIO
, "CSMI(%u) failed with ReturnCode=%lu", code
, csmi_buffer
->ReturnCode
);
3488 if (scsi_debugmode
> 1)
3489 pout(" IOCTL_SCSI_MINIPORT(CC_CSMI_%u) succeeded, bytes returned: %lu\n", code
, num_out
);
3495 /////////////////////////////////////////////////////////////////////////////
3496 // ASPI Interface (for SCSI devices on 9x/ME)
3497 /////////////////////////////////////////////////////////////////////////////
3503 #define ASPI_SENSE_SIZE 18
3505 // ASPI SCSI Request block header
3508 unsigned char cmd
; // 00: Command code
3509 unsigned char status
; // 01: ASPI status
3510 unsigned char adapter
; // 02: Host adapter number
3511 unsigned char flags
; // 03: Request flags
3512 unsigned char reserved
[4]; // 04: 0
3515 // SRB for host adapter inquiry
3518 ASPI_SRB_HEAD h
; // 00: Header
3519 unsigned char adapters
; // 08: Number of adapters
3520 unsigned char target_id
; // 09: Target ID ?
3521 char manager_id
[16]; // 10: SCSI manager ID
3522 char adapter_id
[16]; // 26: Host adapter ID
3523 unsigned char parameters
[16]; // 42: Host adapter unique parmameters
3526 // SRB for get device type
3529 ASPI_SRB_HEAD h
; // 00: Header
3530 unsigned char target_id
; // 08: Target ID
3531 unsigned char lun
; // 09: LUN
3532 unsigned char devtype
; // 10: Device type
3533 unsigned char reserved
; // 11: Reserved
3539 ASPI_SRB_HEAD h
; // 00: Header
3540 unsigned char target_id
; // 08: Target ID
3541 unsigned char lun
; // 09: LUN
3542 unsigned char reserved
[2]; // 10: Reserved
3543 unsigned long data_size
; // 12: Data alloc. lenght
3544 void * data_addr
; // 16: Data buffer pointer
3545 unsigned char sense_size
; // 20: Sense alloc. length
3546 unsigned char cdb_size
; // 21: CDB length
3547 unsigned char host_status
; // 22: Host status
3548 unsigned char target_status
; // 23: Target status
3549 void * event_handle
; // 24: Event handle
3550 unsigned char workspace
[20]; // 28: ASPI workspace
3551 unsigned char cdb
[16+ASPI_SENSE_SIZE
];
3554 // Macro to retrieve start of sense information
3555 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
3560 ASPI_SRB_HEAD h
; // Common header
3561 ASPI_SRB_INQUIRY q
; // Inquiry
3562 ASPI_SRB_DEVTYPE t
; // Device type
3563 ASPI_SRB_IO i
; // I/O
3569 #define ASPI_CMD_ADAPTER_INQUIRE 0x00
3570 #define ASPI_CMD_GET_DEVICE_TYPE 0x01
3571 #define ASPI_CMD_EXECUTE_IO 0x02
3572 #define ASPI_CMD_ABORT_IO 0x03
3575 #define ASPI_REQFLAG_DIR_TO_HOST 0x08
3576 #define ASPI_REQFLAG_DIR_TO_TARGET 0x10
3577 #define ASPI_REQFLAG_DIR_NO_XFER 0x18
3578 #define ASPI_REQFLAG_EVENT_NOTIFY 0x40
3581 #define ASPI_STATUS_IN_PROGRESS 0x00
3582 #define ASPI_STATUS_NO_ERROR 0x01
3583 #define ASPI_STATUS_ABORTED 0x02
3584 #define ASPI_STATUS_ABORT_ERR 0x03
3585 #define ASPI_STATUS_ERROR 0x04
3586 #define ASPI_STATUS_INVALID_COMMAND 0x80
3587 #define ASPI_STATUS_INVALID_ADAPTER 0x81
3588 #define ASPI_STATUS_INVALID_TARGET 0x82
3589 #define ASPI_STATUS_NO_ADAPTERS 0xE8
3591 // Adapter (host) status
3592 #define ASPI_HSTATUS_NO_ERROR 0x00
3593 #define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
3594 #define ASPI_HSTATUS_DATA_OVERRUN 0x12
3595 #define ASPI_HSTATUS_BUS_FREE 0x13
3596 #define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
3597 #define ASPI_HSTATUS_BAD_SGLIST 0x1A
3600 #define ASPI_TSTATUS_NO_ERROR 0x00
3601 #define ASPI_TSTATUS_CHECK_CONDITION 0x02
3602 #define ASPI_TSTATUS_BUSY 0x08
3603 #define ASPI_TSTATUS_RESERV_CONFLICT 0x18
3606 static HINSTANCE h_aspi_dll
; // DLL handle
3607 static UINT (* aspi_entry
)(ASPI_SRB
* srb
); // ASPI entrypoint
3608 static unsigned num_aspi_adapters
;
3611 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
3612 static DWORD aspi_dll_pid
; // PID of DLL owner to detect fork()
3613 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
3615 #define aspi_entry_valid() (!!aspi_entry)
3619 static int aspi_call(ASPI_SRB
* srb
)
3624 while (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
3625 if (++i
> 100/*10sek*/) {
3626 pout("ASPI Adapter %u: Timed out\n", srb
->h
.adapter
);
3628 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3632 if (scsi_debugmode
> 1)
3633 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb
->h
.adapter
, i
);
3640 // Get ASPI entrypoint from wnaspi32.dll
3642 static FARPROC
aspi_get_address(const char * name
, int verbose
)
3645 assert(h_aspi_dll
&& h_aspi_dll
!= INVALID_HANDLE_VALUE
);
3647 if (!(addr
= GetProcAddress(h_aspi_dll
, name
))) {
3649 pout("Missing %s() in WNASPI32.DLL\n", name
);
3651 FreeLibrary(h_aspi_dll
);
3652 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3660 static int aspi_open_dll(int verbose
)
3662 UINT (*aspi_info
)(void);
3665 assert(!aspi_entry_valid());
3667 // Check structure layout
3668 assert(sizeof(ASPI_SRB_HEAD
) == 8);
3669 assert(sizeof(ASPI_SRB_INQUIRY
) == 58);
3670 assert(sizeof(ASPI_SRB_DEVTYPE
) == 12);
3671 assert(sizeof(ASPI_SRB_IO
) == 64+ASPI_SENSE_SIZE
);
3672 assert(offsetof(ASPI_SRB
,h
.cmd
) == 0);
3673 assert(offsetof(ASPI_SRB
,h
.flags
) == 3);
3674 assert(offsetof(ASPI_SRB_IO
,lun
) == 9);
3675 assert(offsetof(ASPI_SRB_IO
,data_addr
) == 16);
3676 assert(offsetof(ASPI_SRB_IO
,workspace
) == 28);
3677 assert(offsetof(ASPI_SRB_IO
,cdb
) == 48);
3679 if (h_aspi_dll
== INVALID_HANDLE_VALUE
) {
3686 if (!(h_aspi_dll
= LoadLibraryA("WNASPI32.DLL"))) {
3688 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
3689 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3693 if (scsi_debugmode
> 1) {
3694 // Print full path of WNASPI32.DLL
3695 char path
[MAX_PATH
];
3696 if (!GetModuleFileName(h_aspi_dll
, path
, sizeof(path
)))
3697 strcpy(path
, "*unknown*");
3698 pout("Using ASPI interface \"%s\"\n", path
);
3701 // Get ASPI entrypoints
3702 if (!(aspi_info
= (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose
)))
3704 if (!(aspi_entry
= (UINT (*)(ASPI_SRB
*))aspi_get_address("SendASPI32Command", verbose
)))
3707 // Init ASPI manager and get number of adapters
3708 info
= (aspi_info
)();
3709 if (scsi_debugmode
> 1)
3710 pout("GetASPI32SupportInfo() returns 0x%04x\n", info
);
3711 rc
= (info
>> 8) & 0xff;
3712 if (rc
== ASPI_STATUS_NO_ADAPTERS
) {
3713 num_aspi_adapters
= 0;
3715 else if (rc
== ASPI_STATUS_NO_ERROR
) {
3716 num_aspi_adapters
= info
& 0xff;
3720 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info
);
3722 FreeLibrary(h_aspi_dll
);
3723 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3729 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3732 // save PID to detect fork() in aspi_entry_valid()
3733 aspi_dll_pid
= GetCurrentProcessId();
3735 assert(aspi_entry_valid());
3740 static int aspi_io_call(ASPI_SRB
* srb
, unsigned timeout
)
3744 if (!(event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
))) {
3745 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO
;
3747 srb
->i
.event_handle
= event
;
3748 srb
->h
.flags
|= ASPI_REQFLAG_EVENT_NOTIFY
;
3749 // Start ASPI request
3751 if (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
3753 DWORD rc
= WaitForSingleObject(event
, timeout
*1000L);
3754 if (rc
!= WAIT_OBJECT_0
) {
3755 if (rc
== WAIT_TIMEOUT
) {
3756 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
3757 srb
->h
.adapter
, srb
->i
.target_id
, timeout
);
3760 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
3761 (unsigned long)(ULONG_PTR
)event
, rc
, rc
, GetLastError());
3763 // TODO: ASPI_ABORT_IO command
3765 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3774 win_aspi_device::win_aspi_device(smart_interface
* intf
,
3775 const char * dev_name
, const char * req_type
)
3776 : smart_device(intf
, dev_name
, "scsi", req_type
),
3777 m_adapter(-1), m_id(0)
3781 bool win_aspi_device::is_open() const
3783 return (m_adapter
>= 0);
3786 bool win_aspi_device::open()
3788 // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
3789 unsigned adapter
= ~0, id
= ~0; int n1
= -1;
3790 const char * name
= skipdev(get_dev_name());
3791 if (!(sscanf(name
,"scsi%1u%1x%n", &adapter
, &id
, &n1
) == 2 && n1
== (int)strlen(name
)
3792 && adapter
<= 9 && id
< 16))
3793 return set_err(EINVAL
);
3795 if (!aspi_entry_valid()) {
3796 if (aspi_open_dll(1/*verbose*/))
3797 return set_err(ENOENT
);
3801 if (adapter
>= num_aspi_adapters
) {
3802 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
3803 adapter
, num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3804 if (!is_permissive())
3805 return set_err(ENOENT
);
3810 memset(&srb
, 0, sizeof(srb
));
3811 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3812 srb
.h
.adapter
= adapter
; srb
.i
.target_id
= id
;
3813 if (aspi_call(&srb
))
3814 return set_err(EIO
);
3815 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3816 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter
, id
, srb
.h
.status
);
3817 if (!is_permissive())
3818 return set_err(srb
.h
.status
== ASPI_STATUS_INVALID_TARGET
? ENOENT
: EIO
);
3820 else if (scsi_debugmode
)
3821 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter
, id
, srb
.t
.devtype
);
3823 m_adapter
= (int)adapter
; m_id
= (unsigned char)id
;
3828 bool win_aspi_device::close()
3830 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
3835 // Scan for ASPI drives
3837 bool win9x_smart_interface::scsi_scan(smart_device_list
& devlist
)
3839 if (!aspi_entry_valid()) {
3840 if (aspi_open_dll(scsi_debugmode
/*default is quiet*/))
3844 for (unsigned ad
= 0; ad
< num_aspi_adapters
; ad
++) {
3849 pout(" ASPI Adapter %u: Ignored\n", ad
);
3854 memset(&srb
, 0, sizeof(srb
));
3855 srb
.h
.cmd
= ASPI_CMD_ADAPTER_INQUIRE
;
3857 if (aspi_call(&srb
))
3860 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3862 pout(" ASPI Adapter %u: Status=0x%02x\n", ad
, srb
.h
.status
);
3866 if (scsi_debugmode
) {
3867 for (int i
= 1; i
< 16 && srb
.q
.adapter_id
[i
]; i
++)
3868 if (!(' ' <= srb
.q
.adapter_id
[i
] && srb
.q
.adapter_id
[i
] <= '~'))
3869 srb
.q
.adapter_id
[i
] = '?';
3870 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad
, srb
.q
.adapter_id
);
3873 bool ignore
= !strnicmp(srb
.q
.adapter_id
, "3ware", 5);
3875 for (unsigned id
= 0; id
<= 7; id
++) {
3877 memset(&srb
, 0, sizeof(srb
));
3878 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3879 srb
.h
.adapter
= ad
; srb
.i
.target_id
= id
;
3880 if (aspi_call(&srb
))
3882 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3883 if (scsi_debugmode
> 1)
3884 pout(" ID %u: No such device (Status=0x%02x)\n", id
, srb
.h
.status
);
3888 if (!ignore
&& srb
.t
.devtype
== 0x00/*HDD*/) {
3890 pout(" ID %u: Device Type=0x%02x\n", id
, srb
.t
.devtype
);
3892 sprintf(name
, "/dev/scsi%u%u", ad
, id
);
3893 devlist
.push_back( new win_aspi_device(this, name
, "scsi") );
3895 else if (scsi_debugmode
)
3896 pout(" ID %u: Device Type=0x%02x (ignored)\n", id
, srb
.t
.devtype
);
3903 // Interface to ASPI SCSI devices
3904 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
3906 int report
= scsi_debugmode
; // TODO
3908 if (m_adapter
< 0) {
3913 if (!aspi_entry_valid()) {
3918 if (!(iop
->cmnd_len
== 6 || iop
->cmnd_len
== 10 || iop
->cmnd_len
== 12 || iop
->cmnd_len
== 16)) {
3919 set_err(EINVAL
, "bad CDB length");
3926 const unsigned char * ucp
= iop
->cmnd
;
3929 const int sz
= (int)sizeof(buff
);
3931 np
= scsi_get_opcode_name(ucp
[0]);
3932 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3933 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3934 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3936 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3937 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3939 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3940 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3941 (trunc
? " [only first 256 bytes shown]" : ""));
3942 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3945 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3950 memset(&srb
, 0, sizeof(srb
));
3951 srb
.h
.cmd
= ASPI_CMD_EXECUTE_IO
;
3952 srb
.h
.adapter
= m_adapter
;
3953 srb
.i
.target_id
= m_id
;
3955 srb
.i
.sense_size
= ASPI_SENSE_SIZE
;
3956 srb
.i
.cdb_size
= iop
->cmnd_len
;
3957 memcpy(srb
.i
.cdb
, iop
->cmnd
, iop
->cmnd_len
);
3959 switch (iop
->dxfer_dir
) {
3961 srb
.h
.flags
= ASPI_REQFLAG_DIR_NO_XFER
;
3963 case DXFER_FROM_DEVICE
:
3964 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_HOST
;
3965 srb
.i
.data_size
= iop
->dxfer_len
;
3966 srb
.i
.data_addr
= iop
->dxferp
;
3968 case DXFER_TO_DEVICE
:
3969 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_TARGET
;
3970 srb
.i
.data_size
= iop
->dxfer_len
;
3971 srb
.i
.data_addr
= iop
->dxferp
;
3974 set_err(EINVAL
, "bad dxfer_dir");
3978 iop
->resp_sense_len
= 0;
3979 iop
->scsi_status
= 0;
3982 if (aspi_io_call(&srb
, (iop
->timeout
? iop
->timeout
: 60))) {
3984 set_err(EIO
, "ASPI Timeout"); return false;
3987 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3988 if ( srb
.h
.status
== ASPI_STATUS_ERROR
3989 && srb
.i
.host_status
== ASPI_HSTATUS_NO_ERROR
3990 && srb
.i
.target_status
== ASPI_TSTATUS_CHECK_CONDITION
) {
3992 const unsigned char * sense
= ASPI_SRB_SENSE(&srb
.i
, iop
->cmnd_len
);
3993 int len
= (ASPI_SENSE_SIZE
< iop
->max_sense_len
? ASPI_SENSE_SIZE
: iop
->max_sense_len
);
3994 iop
->scsi_status
= SCSI_STATUS_CHECK_CONDITION
;
3995 if (len
> 0 && iop
->sensep
) {
3996 memcpy(iop
->sensep
, sense
, len
);
3997 iop
->resp_sense_len
= len
;
3999 pout(" >>> Sense buffer, len=%d:\n", (int)len
);
4000 dStrHex(iop
->sensep
, len
, 1);
4004 pout(" sense_key=%x asc=%x ascq=%x\n",
4005 sense
[2] & 0xf, sense
[12], sense
[13]);
4011 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb
.h
.status
, srb
.i
.host_status
, srb
.i
.target_status
);
4020 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
&& report
> 1) {
4021 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4022 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
4023 (trunc
? " [only first 256 bytes shown]" : ""));
4024 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4030 #endif // WIN9X_SUPPORT
4032 /////////////////////////////////////////////////////////////////////////////
4033 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
4034 // Only supported in NT and later
4035 /////////////////////////////////////////////////////////////////////////////
4037 win_scsi_device::win_scsi_device(smart_interface
* intf
,
4038 const char * dev_name
, const char * req_type
)
4039 : smart_device(intf
, dev_name
, "scsi", req_type
)
4043 bool win_scsi_device::open()
4045 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
4046 // sd[a-z],N => Physical drive 0-26, RAID port N
4047 char drive
[1+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
4048 if ( sscanf(name
, "sd%1[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
4049 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
4050 return open(drive
[0] - 'a', -1, -1, sub_addr
);
4052 // pd<m>,N => Physical drive <m>, RAID port N
4053 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
4054 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
4055 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
4056 return open(pd_num
, -1, -1, sub_addr
);
4058 // [a-zA-Z]: => Physical drive behind logical drive 0-25
4059 int logdrive
= drive_letter(name
);
4060 if (logdrive
>= 0) {
4061 return open(-1, logdrive
, -1, -1);
4063 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
4064 int tape_num
= -1; n1
= -1;
4065 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
4066 return open(-1, -1, tape_num
, -1);
4068 tape_num
= -1; n1
= -1;
4069 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
4070 return open(-1, -1, tape_num
, -1);
4072 // tape<m> => tape drive <m>
4073 tape_num
= -1; n1
= -1;
4074 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
4075 return open(-1, -1, tape_num
, -1);
4078 return set_err(EINVAL
);
4081 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
4084 b
[sizeof(b
) - 1] = '\0';
4086 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
4087 else if (ld_num
>= 0)
4088 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
4089 else if (tape_num
>= 0)
4090 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
4097 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
4098 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
4099 OPEN_EXISTING
, 0, 0);
4100 if (h
== INVALID_HANDLE_VALUE
) {
4101 set_err(ENODEV
, "%s: Open failed, Error=%ld", b
, GetLastError());
4110 SCSI_PASS_THROUGH_DIRECT spt
;
4112 UCHAR ucSenseBuf
[64];
4113 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
4116 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
4117 // Used if DataTransferLength not supported by *_DIRECT.
4118 static long scsi_pass_through_indirect(HANDLE h
,
4119 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
4121 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
4122 SCSI_PASS_THROUGH spt
;
4124 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
4125 UCHAR ucDataBuf
[512];
4128 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
4129 memset(&sb
, 0, sizeof(sb
));
4131 // DATA_OUT not implemented yet
4132 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
4133 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
4134 return ERROR_INVALID_PARAMETER
;
4136 sb
.spt
.Length
= sizeof(sb
.spt
);
4137 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
4138 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
4139 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
4140 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
4141 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
4142 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
4143 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
4144 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
4147 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
4148 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
4149 return GetLastError();
4151 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
4152 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
4153 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
4155 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
4156 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
4157 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
4162 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
4163 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
4165 int report
= scsi_debugmode
; // TODO
4169 const unsigned char * ucp
= iop
->cmnd
;
4172 const int sz
= (int)sizeof(buff
);
4174 np
= scsi_get_opcode_name(ucp
[0]);
4175 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
4176 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
4177 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
4179 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
4180 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4182 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
4183 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
4184 (trunc
? " [only first 256 bytes shown]" : ""));
4185 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4188 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
4192 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
4193 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
4194 set_err(EINVAL
, "cmnd_len too large");
4198 memset(&sb
, 0, sizeof(sb
));
4199 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
4200 sb
.spt
.CdbLength
= iop
->cmnd_len
;
4201 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
4202 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
4203 sb
.spt
.SenseInfoOffset
=
4204 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
4205 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
4208 switch (iop
->dxfer_dir
) {
4210 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
4212 case DXFER_FROM_DEVICE
:
4213 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
4214 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4215 sb
.spt
.DataBuffer
= iop
->dxferp
;
4216 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
4217 // transfers (needed for SMART STATUS check of JMicron USB bridges)
4218 if (sb
.spt
.DataTransferLength
== 1)
4221 case DXFER_TO_DEVICE
:
4222 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
4223 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4224 sb
.spt
.DataBuffer
= iop
->dxferp
;
4227 set_err(EINVAL
, "bad dxfer_dir");
4234 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
4235 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
4236 err
= GetLastError();
4239 err
= scsi_pass_through_indirect(get_fh(), &sb
);
4242 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
4243 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
4244 (direct
? "_DIRECT" : ""), err
);
4246 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
4247 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
4248 int slen
= sb
.ucSenseBuf
[7] + 8;
4250 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
4251 slen
= sizeof(sb
.ucSenseBuf
);
4252 if (slen
> (int)iop
->max_sense_len
)
4253 slen
= iop
->max_sense_len
;
4254 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
4255 iop
->resp_sense_len
= slen
;
4258 pout(" >>> Sense buffer, len=%d:\n", slen
);
4259 dStrHex(iop
->sensep
, slen
, 1);
4261 if ((iop
->sensep
[0] & 0x7f) > 0x71)
4262 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
4263 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
4264 iop
->sensep
[2], iop
->sensep
[3]);
4266 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
4267 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
4268 iop
->sensep
[12], iop
->sensep
[13]);
4271 iop
->resp_sense_len
= 0;
4273 if ((iop
->dxfer_len
> 0) && (sb
.spt
.DataTransferLength
> 0))
4274 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
4278 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
4279 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4280 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
4281 (trunc
? " [only first 256 bytes shown]" : ""));
4282 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4287 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
4288 static long scsi_pass_through_direct(HANDLE fd
, struct scsi_cmnd_io
* iop
)
4290 int report
= scsi_debugmode
; // TODO
4294 const unsigned char * ucp
= iop
->cmnd
;
4297 const int sz
= (int)sizeof(buff
);
4299 np
= scsi_get_opcode_name(ucp
[0]);
4300 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
4301 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
4302 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
4304 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
4305 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4307 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
4308 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
4309 (trunc
? " [only first 256 bytes shown]" : ""));
4310 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4313 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
4317 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
4318 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
4322 memset(&sb
, 0, sizeof(sb
));
4323 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
4324 //sb.spt.PathId = 0;
4325 sb
.spt
.TargetId
= 127;
4327 sb
.spt
.CdbLength
= iop
->cmnd_len
;
4328 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
4329 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
4330 sb
.spt
.SenseInfoOffset
=
4331 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
4332 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
4335 switch (iop
->dxfer_dir
) {
4337 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
4339 case DXFER_FROM_DEVICE
:
4340 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
4341 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4342 sb
.spt
.DataBuffer
= iop
->dxferp
;
4343 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
4344 // transfers (needed for SMART STATUS check of JMicron USB bridges)
4345 if (sb
.spt
.DataTransferLength
== 1)
4348 case DXFER_TO_DEVICE
:
4349 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
4350 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
4351 sb
.spt
.DataBuffer
= iop
->dxferp
;
4360 if (!DeviceIoControl(fd
, IOCTL_SCSI_PASS_THROUGH_DIRECT
,
4361 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
4362 err
= GetLastError();
4365 err
= scsi_pass_through_indirect(fd
, &sb
);
4372 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
4373 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
4374 int slen
= sb
.ucSenseBuf
[7] + 8;
4376 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
4377 slen
= sizeof(sb
.ucSenseBuf
);
4378 if (slen
> (int)iop
->max_sense_len
)
4379 slen
= iop
->max_sense_len
;
4380 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
4381 iop
->resp_sense_len
= slen
;
4384 pout(" >>> Sense buffer, len=%d:\n", slen
);
4385 dStrHex(iop
->sensep
, slen
, 1);
4387 if ((iop
->sensep
[0] & 0x7f) > 0x71)
4388 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
4389 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
4390 iop
->sensep
[2], iop
->sensep
[3]);
4392 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
4393 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
4394 iop
->sensep
[12], iop
->sensep
[13]);
4397 iop
->resp_sense_len
= 0;
4399 if ((iop
->dxfer_len
> 0) && (sb
.spt
.DataTransferLength
> 0))
4400 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
4404 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
4405 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
4406 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
4407 (trunc
? " [only first 256 bytes shown]" : ""));
4408 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
4415 #if 0 // For debugging areca code
4417 static void dumpdata(unsigned char *block
, int len
)
4419 int ln
= (len
/ 16) + 1; // total line#
4423 printf(" Address = %p, Length = (0x%x)%d\n", block
, len
, len
);
4424 printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n");
4425 printf("=====================================================================\n");
4427 for ( int l
= 0; l
< ln
&& len
; l
++ )
4429 // printf the line# and the HEX data
4430 // if a line data length < 16 then append the space to the tail of line to reach 16 chars
4431 printf("%02X | ", l
);
4432 for ( pos
= 0; pos
< 16 && len
; pos
++, len
-- )
4434 c
= block
[l
*16+pos
];
4440 for ( int loop
= pos
; loop
< 16; loop
++ )
4447 for ( int loop
= 0; loop
< pos
; loop
++ )
4449 c
= block
[l
*16+loop
];
4450 if ( c
>= 0x20 && c
<= 0x7F )
4461 printf("=====================================================================\n");
4467 // This is an interface routine meant to isolate the OS dependent
4468 // parts of the code, and to provide a debugging interface. Each
4469 // different port and OS needs to provide it's own interface. This
4470 // is the Windows interface to the Areca "arcmsr" driver. It allows ATA
4471 // commands to be passed through the SCSI driver.
4472 // DETAILED DESCRIPTION OF ARGUMENTS
4473 // fd: is the file descriptor provided by open()
4474 // disknum is the disk number (0 to 127) in the RAID array
4475 // command: defines the different operations.
4476 // select: additional input data if needed (which log, which type of
4478 // data: location to write output data, if needed (512 bytes).
4479 // Note: not all commands use all arguments.
4481 // -1 if the command failed
4482 // 0 if the command succeeded,
4483 // STATUS_CHECK routine:
4484 // -1 if the command failed
4485 // 0 if the command succeeded and disk SMART status is "OK"
4486 // 1 if the command succeeded and disk SMART status is "FAILING"
4487 int win_areca_device::arcmsr_command_handler(HANDLE fd
, unsigned long arcmsr_cmd
, unsigned char *data
, int data_len
)
4489 int ioctlreturn
= 0;
4491 struct scsi_cmnd_io io_hdr
;
4492 int dir
= DXFER_TO_DEVICE
;
4497 unsigned char *areca_return_packet
;
4500 unsigned char return_buff
[2048];
4501 unsigned char *ptr
= &return_buff
[0];
4502 memset(return_buff
, 0, sizeof(return_buff
));
4504 memset((unsigned char *)&sBuf
, 0, sizeof(sBuf
));
4505 memset(&io_hdr
, 0, sizeof(io_hdr
));
4506 memset(cdb
, 0, sizeof(cdb
));
4507 memset(sense
, 0, sizeof(sense
));
4510 sBuf
.srbioctl
.HeaderLength
= sizeof(sSRB_IO_CONTROL
);
4511 memcpy(sBuf
.srbioctl
.Signature
, ARECA_SIG_STR
, strlen(ARECA_SIG_STR
));
4512 sBuf
.srbioctl
.Timeout
= 10000;
4513 sBuf
.srbioctl
.ControlCode
= arcmsr_cmd
;
4515 switch ( arcmsr_cmd
)
4517 // command for writing data to driver
4518 case ARCMSR_IOCTL_WRITE_WQBUFFER
:
4519 if ( data
&& data_len
)
4521 sBuf
.srbioctl
.Length
= data_len
;
4522 memcpy((unsigned char *)sBuf
.ioctldatabuffer
, (unsigned char *)data
, data_len
);
4524 // commands for clearing related buffer of driver
4525 case ARCMSR_IOCTL_CLEAR_RQBUFFER
:
4526 case ARCMSR_IOCTL_CLEAR_WQBUFFER
:
4527 cdb
[0] = 0x3B; //SCSI_WRITE_BUF command;
4529 // command for reading data from driver
4530 case ARCMSR_IOCTL_READ_RQBUFFER
:
4531 // command for identifying driver
4532 case ARCMSR_IOCTL_RETURN_CODE_3F
:
4533 cdb
[0] = 0x3C; //SCSI_READ_BUF command;
4534 dir
= DXFER_FROM_DEVICE
;
4537 // unknown arcmsr commands
4544 io_hdr
.dxfer_dir
= dir
;
4545 io_hdr
.dxfer_len
= sizeof(sBuf
);
4546 io_hdr
.dxferp
= (unsigned char *)&sBuf
;
4548 io_hdr
.cmnd_len
= sizeof(cdb
);
4549 io_hdr
.sensep
= sense
;
4550 io_hdr
.max_sense_len
= sizeof(sense
);
4551 io_hdr
.timeout
= SCSI_TIMEOUT_DEFAULT
;
4555 ioctlreturn
= scsi_pass_through_direct(fd
, &io_hdr
);
4556 if ( ioctlreturn
|| io_hdr
.scsi_status
)
4562 if ( arcmsr_cmd
!= ARCMSR_IOCTL_READ_RQBUFFER
)
4564 // if succeeded, just returns the length of outgoing data
4568 if ( sBuf
.srbioctl
.Length
)
4570 //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
4571 memcpy(ptr
, &sBuf
.ioctldatabuffer
[0], sBuf
.srbioctl
.Length
);
4572 ptr
+= sBuf
.srbioctl
.Length
;
4573 total
+= sBuf
.srbioctl
.Length
;
4574 // the returned bytes enough to compute payload length ?
4575 if ( expected
< 0 && total
>= 5 )
4577 areca_return_packet
= (unsigned char *)&return_buff
[0];
4578 if ( areca_return_packet
[0] == 0x5E &&
4579 areca_return_packet
[1] == 0x01 &&
4580 areca_return_packet
[2] == 0x61 )
4582 // valid header, let's compute the returned payload length,
4583 // we expected the total length is
4584 // payload + 3 bytes header + 2 bytes length + 1 byte checksum
4585 expected
= areca_return_packet
[4] * 256 + areca_return_packet
[3] + 6;
4589 if ( total
>= 7 && total
>= expected
)
4591 //printf("total bytes received = %d, expected length = %d\n", total, expected);
4593 // ------ Okay! we received enough --------
4599 // Deal with the different error cases
4600 if ( arcmsr_cmd
== ARCMSR_IOCTL_RETURN_CODE_3F
)
4602 // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...)
4608 pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn
);
4612 if ( io_hdr
.scsi_status
)
4614 pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr
.scsi_status
);
4620 memcpy(data
, return_buff
, total
);
4627 win_areca_device::win_areca_device(smart_interface
* intf
, const char * dev_name
, HANDLE fh
, int disknum
, int encnum
)
4628 : smart_device(intf
, dev_name
, "areca", "areca"),
4633 set_info().info_name
= strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name
, disknum
, encnum
);
4636 bool win_areca_device::open()
4645 hFh
= CreateFile( get_dev_name(),
4646 GENERIC_READ
|GENERIC_WRITE
,
4647 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4652 if(hFh
== INVALID_HANDLE_VALUE
)
4661 // Areca RAID Controller
4662 bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
4664 // ATA input registers
4665 typedef struct _ATA_INPUT_REGISTERS
4667 unsigned char features
;
4668 unsigned char sector_count
;
4669 unsigned char sector_number
;
4670 unsigned char cylinder_low
;
4671 unsigned char cylinder_high
;
4672 unsigned char device_head
;
4673 unsigned char command
;
4674 unsigned char reserved
[8];
4675 unsigned char data
[512]; // [in/out] buffer for outgoing/incoming data
4676 } sATA_INPUT_REGISTERS
;
4678 // ATA output registers
4679 // Note: The output registers is re-sorted for areca internal use only
4680 typedef struct _ATA_OUTPUT_REGISTERS
4682 unsigned char error
;
4683 unsigned char status
;
4684 unsigned char sector_count
;
4685 unsigned char sector_number
;
4686 unsigned char cylinder_low
;
4687 unsigned char cylinder_high
;
4688 } sATA_OUTPUT_REGISTERS
;
4690 // Areca packet format for outgoing:
4691 // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
4692 // B[3~4] : 2 bytes command length + variant data length, little endian
4693 // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c
4694 // B[6~last-1] : variant bytes payload data
4695 // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
4698 // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte
4699 // +--------------------------------------------------------------------------------+
4700 // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 |
4701 // +--------------------------------------------------------------------------------+
4704 //Areca packet format for incoming:
4705 // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
4706 // B[3~4] : 2 bytes payload length, little endian
4707 // B[5~last-1] : variant bytes returned payload data
4708 // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
4711 // header 3 bytes length 2 bytes payload data x bytes cs 1 byte
4712 // +-------------------------------------------------------------------+
4713 // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 |
4714 // +-------------------------------------------------------------------+
4715 unsigned char areca_packet
[640];
4716 int areca_packet_len
= sizeof(areca_packet
);
4717 unsigned char cs
= 0;
4719 sATA_INPUT_REGISTERS
*ata_cmd
;
4723 memset(sInq
, 0, sizeof(sInq
));
4724 scsiStdInquiry(fd
, (unsigned char *)sInq
, (int)sizeof(sInq
));
4725 dumpdata((unsigned char *)sInq
, sizeof(sInq
));
4727 memset(areca_packet
, 0, areca_packet_len
);
4729 // ----- BEGIN TO SETUP HEADERS -------
4730 areca_packet
[0] = 0x5E;
4731 areca_packet
[1] = 0x01;
4732 areca_packet
[2] = 0x61;
4733 areca_packet
[3] = (unsigned char)((areca_packet_len
- 6) & 0xff);
4734 areca_packet
[4] = (unsigned char)(((areca_packet_len
- 6) >> 8) & 0xff);
4735 areca_packet
[5] = 0x1c; // areca defined code for ATA passthrough command
4737 // ----- BEGIN TO SETUP PAYLOAD DATA -----
4738 memcpy(&areca_packet
[7], "SmrT", 4); // areca defined password
4739 ata_cmd
= (sATA_INPUT_REGISTERS
*)&areca_packet
[12];
4743 const ata_in_regs
& r
= in
.in_regs
;
4744 ata_cmd
->features
= r
.features
;
4745 ata_cmd
->sector_count
= r
.sector_count
;
4746 ata_cmd
->sector_number
= r
.lba_low
;
4747 ata_cmd
->cylinder_low
= r
.lba_mid
;
4748 ata_cmd
->cylinder_high
= r
.lba_high
;
4749 ata_cmd
->device_head
= r
.device
;
4750 ata_cmd
->command
= r
.command
;
4752 bool readdata
= false;
4753 if (in
.direction
== ata_cmd_in::data_in
) {
4755 // the command will read data
4756 areca_packet
[6] = 0x13;
4758 else if ( in
.direction
== ata_cmd_in::no_data
)
4760 // the commands will return no data
4761 areca_packet
[6] = 0x15;
4763 else if (in
.direction
== ata_cmd_in::data_out
)
4765 // the commands will write data
4766 memcpy(ata_cmd
->data
, in
.buffer
, in
.size
);
4767 areca_packet
[6] = 0x14;
4770 // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
4771 return set_err(ENOSYS
);
4774 areca_packet
[11] = m_disknum
- 1; // disk#
4775 areca_packet
[19] = m_encnum
- 1; // enc#
4777 // ----- BEGIN TO SETUP CHECKSUM -----
4778 for ( int loop
= 3; loop
< areca_packet_len
- 1; loop
++ )
4780 cs
+= areca_packet
[loop
];
4782 areca_packet
[areca_packet_len
-1] = cs
;
4784 // ----- BEGIN TO SEND TO ARECA DRIVER ------
4786 unsigned char return_buff
[2048];
4787 memset(return_buff
, 0, sizeof(return_buff
));
4789 expected
= arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER
, NULL
, 0);
4791 return set_err(EIO
);
4794 expected
= arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER
, NULL
, 0);
4795 expected
= arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER
, areca_packet
, areca_packet_len
);
4798 expected
= arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER
, return_buff
, sizeof(return_buff
));
4802 return set_err(EIO
);
4805 // ----- VERIFY THE CHECKSUM -----
4807 for ( int loop
= 3; loop
< expected
- 1; loop
++ )
4809 cs
+= return_buff
[loop
];
4812 if ( return_buff
[expected
- 1] != cs
)
4814 return set_err(EIO
);
4817 sATA_OUTPUT_REGISTERS
*ata_out
= (sATA_OUTPUT_REGISTERS
*)&return_buff
[5] ;
4818 if ( ata_out
->status
)
4820 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
4821 && !nonempty((unsigned char *)in
.buffer
, in
.size
))
4823 return set_err(ENODEV
, "No drive on port %d", m_disknum
);
4827 // returns with data
4830 memcpy(in
.buffer
, &return_buff
[7], in
.size
);
4833 // Return register values
4835 ata_out_regs
& r
= out
.out_regs
;
4836 r
.error
= ata_out
->error
;
4837 r
.sector_count
= ata_out
->sector_count
;
4838 r
.lba_low
= ata_out
->sector_number
;
4839 r
.lba_mid
= ata_out
->cylinder_low
;
4840 r
.lba_high
= ata_out
->cylinder_high
;
4841 r
.status
= ata_out
->status
;
4847 bool win_areca_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
4849 #define SYNCOBJNAME "Global\\SynIoctlMutex"
4852 SECURITY_ATTRIBUTES sa
;
4853 PSECURITY_DESCRIPTOR pSD
;
4856 if (!ata_cmd_is_ok(in
,
4857 true, // data_out_support
4858 false, // TODO: multi_sector_support
4859 true) // ata_48bit_support
4863 // Support 48-bit commands with zero high bytes
4864 if (in
.in_regs
.is_real_48bit_cmd())
4865 return set_err(ENOSYS
, "48-bit ATA commands not fully supported by Areca");
4867 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum
) < 1)
4868 return set_err(EINVAL
, "unable to parse device name");
4870 memset(mutexstr
, 0, sizeof(mutexstr
));
4871 sprintf(mutexstr
, "%s%d",SYNCOBJNAME
, ctlrnum
);
4872 pSD
= (PSECURITY_DESCRIPTOR
)LocalAlloc(LPTR
, SECURITY_DESCRIPTOR_MIN_LENGTH
);
4873 if ( !InitializeSecurityDescriptor(pSD
, SECURITY_DESCRIPTOR_REVISION
) )
4875 LocalFree((HLOCAL
)pSD
);
4876 return set_err(EIO
, "InitializeSecurityDescriptor failed");
4879 if ( !SetSecurityDescriptorDacl(pSD
, TRUE
, (PACL
)NULL
, FALSE
) )
4881 LocalFree((HLOCAL
)pSD
);
4882 return set_err(EIO
, "SetSecurityDescriptor failed");
4885 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
4886 sa
.lpSecurityDescriptor
= pSD
;
4887 sa
.bInheritHandle
= TRUE
;
4888 hmutex
= CreateMutex(&sa
, FALSE
, mutexstr
);
4889 if ( hmutex
== NULL
)
4891 LocalFree((HLOCAL
)pSD
);
4892 return set_err(EIO
, "CreateMutex failed");
4895 // atomic access to driver
4896 WaitForSingleObject(hmutex
, INFINITE
);
4897 bool ok
= arcmsr_ata_pass_through(in
,out
);
4898 ReleaseMutex(hmutex
);
4902 CloseHandle(hmutex
);
4907 LocalFree((HLOCAL
)pSD
);
4914 //////////////////////////////////////////////////////////////////////////////////////////////////
4919 /////////////////////////////////////////////////////////////////////////////
4921 // Initialize platform interface and register with smi()
4922 void smart_interface::init()
4925 // Remove "." from DLL search path if supported
4926 // to prevent DLL preloading attacks
4927 BOOL (WINAPI
* SetDllDirectoryA_p
)(LPCSTR
) = (BOOL (WINAPI
*)(LPCSTR
))
4928 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4929 if (SetDllDirectoryA_p
)
4930 SetDllDirectoryA_p("");
4933 // Select interface for Windows flavor
4934 if (GetVersion() & 0x80000000) {
4936 static os_win32::win9x_smart_interface the_win9x_interface
;
4937 smart_interface::set(&the_win9x_interface
);
4939 throw std::runtime_error("Win9x/ME not supported");
4943 static os_win32::winnt_smart_interface the_winnt_interface
;
4944 smart_interface::set(&the_winnt_interface
);
4951 // Get exe directory
4952 // (prototype in utiliy.h)
4953 std::string
get_exe_dir()
4955 char path
[MAX_PATH
];
4956 // Get path of this exe
4957 if (!GetModuleFileNameA(GetModuleHandleA(0), path
, sizeof(path
)))
4958 throw std::runtime_error("GetModuleFileName() failed");
4959 // Replace backslash by slash
4961 for (int i
= 0; path
[i
]; i
++)
4962 if (path
[i
] == '\\') {
4963 path
[i
] = '/'; sl
= i
;