4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-10 Christian Franke <smartmontools-support@lists.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
22 extern smartmonctrl
* con
; // con->permissive,reportataioctl
26 #include "dev_interface.h"
27 #include "dev_ata_cmd_set.h"
35 #define assert(x) /* */
38 #define WIN32_LEAN_AND_MEAN
40 #include <stddef.h> // offsetof()
41 #include <io.h> // access()
44 #include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
47 // Macro to check constants at compile time using a dummy typedef
48 #define ASSERT_CONST(c, n) \
49 typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
50 #define ASSERT_SIZEOF(t, n) \
51 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
53 const char * os_win32_cpp_cvsid
= "$Id: os_win32.cpp 3049 2010-01-27 19:47:09Z chrfranke $";
55 // Disable Win9x/ME specific code if no longer supported by compiler.
57 #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
58 // Win9x/ME support was dropped in Cygwin 1.7
59 #elif defined(_MSC_VER) && (_MSC_VER >= 1500)
60 // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)
62 #define WIN9X_SUPPORT 1
66 /////////////////////////////////////////////////////////////////////////////
68 namespace os_win32
{ // no need to publish anything, name provided for Doxygen
71 #pragma warning(disable:4250)
74 // Running on Win9x/ME ?
76 // Set true in win9x_smart_interface ctor.
77 static bool win9x
= false;
79 // Never true (const allows compiler to remove dead code).
80 const bool win9x
= false;
84 class win_smart_device
85 : virtual public /*implements*/ smart_device
89 : smart_device(never_called
),
90 m_fh(INVALID_HANDLE_VALUE
)
93 virtual ~win_smart_device() throw();
95 virtual bool is_open() const;
100 /// Set handle for open() in derived classes.
101 void set_fh(HANDLE fh
)
104 /// Return handle for derived classes.
105 HANDLE
get_fh() const
109 HANDLE m_fh
; ///< File handle
113 /////////////////////////////////////////////////////////////////////////////
116 : public /*implements*/ ata_device
,
117 public /*extends*/ win_smart_device
120 win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
122 virtual ~win_ata_device() throw();
126 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
128 virtual bool ata_identify_is_cached() const;
131 bool open(int phydrive
, int logdrive
, const char * options
, int port
);
133 std::string m_options
;
134 bool m_usr_options
; // options set by user?
135 bool m_admin
; // open with admin access?
136 bool m_id_is_cached
; // ata_identify_is_cached() return value.
138 int m_smartver_state
;
142 /////////////////////////////////////////////////////////////////////////////
144 class win_scsi_device
145 : public /*implements*/ scsi_device
,
146 virtual public /*extends*/ win_smart_device
149 win_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
153 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
156 bool open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
160 /////////////////////////////////////////////////////////////////////////////
164 class win_aspi_device
165 : public /*implements*/ scsi_device
168 win_aspi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
170 virtual bool is_open() const;
174 virtual bool close();
176 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
183 #endif // WIN9X_SUPPORT
185 //////////////////////////////////////////////////////////////////////
187 class win_tw_cli_device
188 : public /*implements*/ ata_device_with_command_set
191 win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
193 virtual bool is_open() const;
197 virtual bool close();
200 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
203 bool m_ident_valid
, m_smart_valid
;
204 ata_identify_device m_ident_buf
;
205 ata_smart_values m_smart_buf
;
209 //////////////////////////////////////////////////////////////////////
210 // Platform specific interfaces
212 // Common to all windows flavors
213 class win_smart_interface
214 : public /*implements part of*/ smart_interface
217 virtual std::string
get_os_version_str();
219 virtual std::string
get_app_examples(const char * appname
);
221 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
222 const char * pattern
= 0);
225 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
227 //virtual scsi_device * get_scsi_device(const char * name, const char * type);
229 virtual smart_device
* autodetect_smart_device(const char * name
);
231 virtual bool ata_scan(smart_device_list
& devlist
) = 0;
233 virtual bool scsi_scan(smart_device_list
& devlist
) = 0;
238 // Win9x/ME reduced functionality
239 class win9x_smart_interface
240 : public /*extends*/ win_smart_interface
243 win9x_smart_interface()
247 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
249 virtual bool ata_scan(smart_device_list
& devlist
);
251 virtual bool scsi_scan(smart_device_list
& devlist
);
254 #endif // WIN9X_SUPPORT
257 class winnt_smart_interface
258 : public /*extends*/ win_smart_interface
261 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
263 virtual smart_device
* autodetect_smart_device(const char * name
);
265 virtual bool ata_scan(smart_device_list
& devlist
);
267 virtual bool scsi_scan(smart_device_list
& devlist
);
271 //////////////////////////////////////////////////////////////////////
273 // Running on 64-bit Windows as 32-bit app ?
274 static bool is_wow64()
276 HMODULE hk
= GetModuleHandleA("kernel32");
279 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
280 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))GetProcAddress(hk
, "IsWow64Process");
281 if (!IsWow64Process_p
)
284 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
289 // Return info string about build host and OS version
290 std::string
win_smart_interface::get_os_version_str()
292 char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-1+sizeof("-2003r2(64)-sp2.1")+13]
293 = SMARTMONTOOLS_BUILD_HOST
;
296 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-1;
297 const int vlen
= sizeof(vstr
)-sizeof(SMARTMONTOOLS_BUILD_HOST
);
298 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
300 OSVERSIONINFOEXA vi
; memset(&vi
, 0, sizeof(vi
));
301 vi
.dwOSVersionInfoSize
= sizeof(vi
);
302 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
)) {
303 memset(&vi
, 0, sizeof(vi
));
304 vi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
305 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
))
309 if (vi
.dwPlatformId
> 0xff || vi
.dwMajorVersion
> 0xff || vi
.dwMinorVersion
> 0xff)
313 switch (vi
.dwPlatformId
<< 16 | vi
.dwMajorVersion
<< 8 | vi
.dwMinorVersion
) {
314 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400| 0:
315 w
= (vi
.szCSDVersion
[1] == 'B' ||
316 vi
.szCSDVersion
[1] == 'C' ? "95-osr2" : "95"); break;
317 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|10:
318 w
= (vi
.szCSDVersion
[1] == 'A' ? "98se" : "98"); break;
319 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|90: w
= "me"; break;
320 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
321 case VER_PLATFORM_WIN32_NT
<<16|0x0400| 0: w
= "nt4"; break;
322 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 0: w
= "2000"; break;
323 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 1:
324 w
= (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
326 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 2:
327 w
= (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
329 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 0:
330 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "vista"
332 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 1:
333 w
= (vi
.wProductType
== VER_NT_WORKSTATION
? "win7"
335 default: w
= 0; break;
338 const char * w64
= (is_wow64() ? "(64)" : "");
340 snprintf(vptr
, vlen
, "-%s%lu.%lu%s",
341 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "9x"),
342 vi
.dwMajorVersion
, vi
.dwMinorVersion
, w64
);
343 else if (vi
.wServicePackMinor
)
344 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
345 else if (vi
.wServicePackMajor
)
346 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
348 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
352 // Return value for device detection functions
353 enum win_dev_type
{ DEV_UNKNOWN
= 0, DEV_ATA
, DEV_SCSI
, DEV_USB
};
355 static win_dev_type
get_phy_drive_type(int drive
);
356 static win_dev_type
get_log_drive_type(int drive
);
357 static bool get_usb_id(int drive
, unsigned short & vendor_id
,
358 unsigned short & product_id
);
360 static const char * ata_get_def_options(void);
363 static int is_permissive()
365 if (!con
->permissive
) {
366 pout("To continue, add one or more '-T permissive' options.\n");
373 // return number for drive letter, -1 on error
374 // "[A-Za-z]:([/\\][.]?)?" => 0-25
375 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
376 static int drive_letter(const char * s
)
378 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
380 && (!s
[2] || ( strchr("/\\\"", s
[2])
381 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
382 (s
[0] & 0x1f) - 1 : -1);
385 // Skip trailing "/dev/", do not allow "/dev/X:"
386 static const char * skipdev(const char * s
)
388 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
391 ata_device
* win_smart_interface::get_ata_device(const char * name
, const char * type
)
393 const char * testname
= skipdev(name
);
394 if (!strncmp(testname
, "tw_cli", 6))
395 return new win_tw_cli_device(this, name
, type
);
396 return new win_ata_device(this, name
, type
);
401 scsi_device
* win9x_smart_interface::get_scsi_device(const char * name
, const char * type
)
403 return new win_aspi_device(this, name
, type
);
408 scsi_device
* winnt_smart_interface::get_scsi_device(const char * name
, const char * type
)
410 const char * testname
= skipdev(name
);
411 if (!strncmp(testname
, "scsi", 4))
413 return new win_aspi_device(this, name
, type
);
415 return (set_err(EINVAL
, "ASPI interface not supported"), (scsi_device
*)0);
417 return new win_scsi_device(this, name
, type
);
420 static win_dev_type
get_dev_type(const char * name
, int & phydrive
)
423 name
= skipdev(name
);
424 if (!strncmp(name
, "st", 2))
426 if (!strncmp(name
, "nst", 3))
428 if (!strncmp(name
, "tape", 4))
431 int logdrive
= drive_letter(name
);
433 win_dev_type type
= get_log_drive_type(logdrive
);
434 return (type
!= DEV_UNKNOWN
? type
: DEV_SCSI
);
437 char drive
[1+1] = "";
438 if (sscanf(name
, "sd%1[a-z]", drive
) == 1) {
439 phydrive
= drive
[0] - 'a';
440 return get_phy_drive_type(phydrive
);
444 if (sscanf(name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
445 return get_phy_drive_type(phydrive
);
449 smart_device
* win_smart_interface::autodetect_smart_device(const char * name
)
451 const char * testname
= skipdev(name
);
452 if (!strncmp(testname
, "hd", 2))
453 return new win_ata_device(this, name
, "");
455 if (!strncmp(testname
, "scsi", 4))
456 return new win_aspi_device(this, name
, "");
458 if (!strncmp(testname
, "tw_cli", 6))
459 return new win_tw_cli_device(this, name
, "");
463 smart_device
* winnt_smart_interface::autodetect_smart_device(const char * name
)
465 smart_device
* dev
= win_smart_interface::autodetect_smart_device(name
);
470 win_dev_type type
= get_dev_type(name
, phydrive
);
473 return new win_ata_device(this, name
, "");
474 if (type
== DEV_SCSI
)
475 return new win_scsi_device(this, name
, "");
477 if (type
== DEV_USB
) {
479 unsigned short vendor_id
= 0, product_id
= 0;
480 if (!(phydrive
>= 0 && get_usb_id(phydrive
, vendor_id
, product_id
))) {
481 set_err(EINVAL
, "Unable to read USB device ID");
484 // Get type name for this ID
485 const char * usbtype
= get_usb_dev_type_by_id(vendor_id
, product_id
);
488 // Return SAT/USB device for this type
489 return get_sat_device(usbtype
, new win_scsi_device(this, name
, ""));
496 // makes a list of ATA or SCSI devices for the DEVICESCAN directive
497 bool win_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
498 const char * type
, const char * pattern
/* = 0*/)
501 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
505 if (!type
|| !strcmp(type
, "ata")) {
506 if (!ata_scan(devlist
))
510 if (!type
|| !strcmp(type
, "scsi")) {
511 if (!scsi_scan(devlist
))
518 // get examples for smartctl
519 std::string
win_smart_interface::get_app_examples(const char * appname
)
521 if (strcmp(appname
, "smartctl"))
523 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
524 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
525 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
526 " (Enables SMART on first disk)\n\n"
527 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
528 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
529 " (Prints Self-Test & Attribute errors)\n"
530 " smartctl -a /dev/scsi21\n"
531 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
532 " smartctl -a /dev/sda\n"
533 " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
534 " smartctl -a /dev/pd3\n"
535 " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
536 " smartctl -a /dev/tape1\n"
537 " (Prints all information for SCSI tape on Tape 1)\n"
538 " smartctl -A /dev/hdb,3\n"
539 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
540 " smartctl -A /dev/tw_cli/c0/p1\n"
541 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
543 " ATA SMART access methods and ordering may be specified by modifiers\n"
544 " following the device name: /dev/hdX:[saicm], where\n"
545 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
546 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
547 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
549 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
554 /////////////////////////////////////////////////////////////////////////////
556 /////////////////////////////////////////////////////////////////////////////
558 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
560 #define FILE_READ_ACCESS 0x0001
561 #define FILE_WRITE_ACCESS 0x0002
562 #define METHOD_BUFFERED 0
563 #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
565 #define FILE_DEVICE_DISK 7
566 #define IOCTL_DISK_BASE FILE_DEVICE_DISK
568 #define SMART_GET_VERSION \
569 CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
571 #define SMART_SEND_DRIVE_COMMAND \
572 CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
574 #define SMART_RCV_DRIVE_DATA \
575 CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
577 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
578 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
579 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
581 #define SMART_CYL_LOW 0x4F
582 #define SMART_CYL_HI 0xC2
587 typedef struct _GETVERSIONOUTPARAMS
{
594 } GETVERSIONOUTPARAMS
, *PGETVERSIONOUTPARAMS
, *LPGETVERSIONOUTPARAMS
;
596 ASSERT_SIZEOF(GETVERSIONOUTPARAMS
, 24);
599 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
601 typedef struct _GETVERSIONINPARAMS_EX
{
607 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
608 WORD wIdentifier
; // Vendor specific identifier
609 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
611 } GETVERSIONINPARAMS_EX
, *PGETVERSIONINPARAMS_EX
, *LPGETVERSIONINPARAMS_EX
;
613 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONOUTPARAMS
));
616 typedef struct _IDEREGS
{
618 UCHAR bSectorCountReg
;
619 UCHAR bSectorNumberReg
;
625 } IDEREGS
, *PIDEREGS
, *LPIDEREGS
;
627 typedef struct _SENDCMDINPARAMS
{
634 } SENDCMDINPARAMS
, *PSENDCMDINPARAMS
, *LPSENDCMDINPARAMS
;
636 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
638 typedef struct _SENDCMDINPARAMS_EX
{
642 BYTE bPortNumber
; // 3ware specific: port number
643 WORD wIdentifier
; // Vendor specific identifier
646 } SENDCMDINPARAMS_EX
, *PSENDCMDINPARAMS_EX
, *LPSENDCMDINPARAMS_EX
;
648 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
651 /* DRIVERSTATUS.bDriverError constants (just for info, not used)
652 #define SMART_NO_ERROR 0
653 #define SMART_IDE_ERROR 1
654 #define SMART_INVALID_FLAG 2
655 #define SMART_INVALID_COMMAND 3
656 #define SMART_INVALID_BUFFER 4
657 #define SMART_INVALID_DRIVE 5
658 #define SMART_INVALID_IOCTL 6
659 #define SMART_ERROR_NO_MEM 7
660 #define SMART_INVALID_REGISTER 8
661 #define SMART_NOT_SUPPORTED 9
662 #define SMART_NO_IDE_DEVICE 10
665 typedef struct _DRIVERSTATUS
{
670 } DRIVERSTATUS
, *PDRIVERSTATUS
, *LPDRIVERSTATUS
;
672 typedef struct _SENDCMDOUTPARAMS
{
674 DRIVERSTATUS DriverStatus
;
676 } SENDCMDOUTPARAMS
, *PSENDCMDOUTPARAMS
, *LPSENDCMDOUTPARAMS
;
678 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
683 /////////////////////////////////////////////////////////////////////////////
685 static void print_ide_regs(const IDEREGS
* r
, int out
)
687 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
688 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
689 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
692 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
694 pout(" Input : "); print_ide_regs(ri
, 0);
696 pout(" Output: "); print_ide_regs(ro
, 1);
700 /////////////////////////////////////////////////////////////////////////////
702 // call SMART_GET_VERSION, return device map or -1 on error
704 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
706 GETVERSIONOUTPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
707 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
710 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
711 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
712 if (con
->reportataioctl
)
713 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
717 assert(num_out
== sizeof(GETVERSIONOUTPARAMS
));
719 if (con
->reportataioctl
> 1) {
720 pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
721 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
722 num_out
, vers
.bVersion
, vers
.bRevision
,
723 vers
.fCapabilities
, vers
.bIDEDeviceMap
);
724 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
725 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
726 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, vers_ex
.dwDeviceMapEx
);
730 *ata_version_ex
= vers_ex
;
732 // TODO: Check vers.fCapabilities here?
733 return vers
.bIDEDeviceMap
;
737 // call SMART_* ioctl
739 static int smart_ioctl(HANDLE hdevice
, int drive
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
741 SENDCMDINPARAMS inpar
;
742 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
744 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
745 const SENDCMDOUTPARAMS
* outpar
;
747 unsigned int size_out
;
750 memset(&inpar
, 0, sizeof(inpar
));
751 inpar
.irDriveRegs
= *regs
;
752 // drive is set to 0-3 on Win9x only
753 inpar
.irDriveRegs
.bDriveHeadReg
= 0xA0 | ((drive
& 1) << 4);
754 inpar
.bDriveNumber
= drive
;
758 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
759 inpar_ex
.bPortNumber
= port
;
762 if (datasize
== 512) {
763 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
764 inpar
.cBufferSize
= size_out
= 512;
766 else if (datasize
== 0) {
767 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
768 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
769 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
770 // Note: cBufferSize must be 0 on Win9x
779 memset(&outbuf
, 0, sizeof(outbuf
));
781 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
782 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
783 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
784 long err
= GetLastError();
785 if (con
->reportataioctl
&& (err
!= ERROR_INVALID_PARAMETER
|| con
->reportataioctl
> 1)) {
786 pout(" %s failed, Error=%ld\n", name
, err
);
787 print_ide_regs_io(regs
, NULL
);
789 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
790 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
791 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
794 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
796 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
798 if (outpar
->DriverStatus
.bDriverError
) {
799 if (con
->reportataioctl
) {
800 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
801 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
802 print_ide_regs_io(regs
, NULL
);
804 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
808 if (con
->reportataioctl
> 1) {
809 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
810 num_out
, outpar
->cBufferSize
);
811 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
812 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
816 memcpy(data
, outpar
->bBuffer
, 512);
817 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
818 if (nonempty(outpar
->bBuffer
, sizeof(IDEREGS
)))
819 *regs
= *(const IDEREGS
*)(outpar
->bBuffer
);
820 else { // Workaround for driver not returning regs
821 if (con
->reportataioctl
)
822 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
823 *regs
= inpar
.irDriveRegs
;
831 /////////////////////////////////////////////////////////////////////////////
833 // IDE PASS THROUGH (2000, XP, undocumented)
835 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
836 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
838 #define FILE_DEVICE_CONTROLLER 4
839 #define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
841 #define IOCTL_IDE_PASS_THROUGH \
842 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
844 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
850 ULONG DataBufferSize
;
854 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
859 /////////////////////////////////////////////////////////////////////////////
861 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
863 if (datasize
> 512) {
867 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
868 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
870 const unsigned char magic
= 0xcf;
878 buf
->DataBufferSize
= datasize
;
880 buf
->DataBuffer
[0] = magic
;
882 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
883 buf
, size
, buf
, size
, &num_out
, NULL
)) {
884 long err
= GetLastError();
885 if (con
->reportataioctl
) {
886 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
887 print_ide_regs_io(regs
, NULL
);
889 VirtualFree(buf
, 0, MEM_RELEASE
);
890 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
895 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
896 if (con
->reportataioctl
) {
897 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
898 print_ide_regs_io(regs
, &buf
->IdeReg
);
900 VirtualFree(buf
, 0, MEM_RELEASE
);
905 // Check and copy data
908 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
909 if (con
->reportataioctl
) {
910 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
911 num_out
, buf
->DataBufferSize
);
912 print_ide_regs_io(regs
, &buf
->IdeReg
);
914 VirtualFree(buf
, 0, MEM_RELEASE
);
918 memcpy(data
, buf
->DataBuffer
, datasize
);
921 if (con
->reportataioctl
> 1) {
922 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
923 num_out
, buf
->DataBufferSize
);
924 print_ide_regs_io(regs
, &buf
->IdeReg
);
928 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
929 VirtualFree(buf
, 0, MEM_RELEASE
);
934 /////////////////////////////////////////////////////////////////////////////
936 // ATA PASS THROUGH (Win2003, XP SP2)
938 #define IOCTL_ATA_PASS_THROUGH \
939 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
941 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
943 typedef struct _ATA_PASS_THROUGH_EX
{
949 UCHAR ReservedAsUchar
;
950 ULONG DataTransferLength
;
952 ULONG ReservedAsUlong
;
953 ULONG
/*_PTR*/ DataBufferOffset
;
954 UCHAR PreviousTaskFile
[8];
955 UCHAR CurrentTaskFile
[8];
956 } ATA_PASS_THROUGH_EX
, *PATA_PASS_THROUGH_EX
;
958 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, 40);
960 #define ATA_FLAGS_DRDY_REQUIRED 0x01
961 #define ATA_FLAGS_DATA_IN 0x02
962 #define ATA_FLAGS_DATA_OUT 0x04
963 #define ATA_FLAGS_48BIT_COMMAND 0x08
964 #define ATA_FLAGS_USE_DMA 0x10
965 #define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
968 /////////////////////////////////////////////////////////////////////////////
971 // IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
972 // transfer per command. Therefore, multi-sector transfers are only supported
973 // for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
974 // or READ/WRITE LOG EXT work only with single sector transfers.
975 // The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
977 // http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
979 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, IDEREGS
* prev_regs
, char * data
, int datasize
)
981 const int max_sectors
= 32; // TODO: Allocate dynamic buffer
984 ATA_PASS_THROUGH_EX apt
;
986 UCHAR ucDataBuf
[max_sectors
* 512];
987 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
989 const unsigned char magic
= 0xcf;
991 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
992 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
994 //ab.apt.TargetId = 0;
996 ab
.apt
.TimeOutValue
= 10;
997 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
998 ab
.apt
.DataBufferOffset
= size
;
1001 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1005 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
1006 ab
.apt
.DataTransferLength
= datasize
;
1008 ab
.ucDataBuf
[0] = magic
;
1010 else if (datasize
< 0) {
1011 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
1015 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
1016 ab
.apt
.DataTransferLength
= -datasize
;
1018 memcpy(ab
.ucDataBuf
, data
, -datasize
);
1021 assert(ab
.apt
.AtaFlags
== 0);
1022 assert(ab
.apt
.DataTransferLength
== 0);
1025 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
1026 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
1027 IDEREGS
* ptfregs
= (IDEREGS
*)ab
.apt
.PreviousTaskFile
;
1031 *ptfregs
= *prev_regs
;
1032 ab
.apt
.AtaFlags
|= ATA_FLAGS_48BIT_COMMAND
;
1036 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
1037 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
1038 long err
= GetLastError();
1039 if (con
->reportataioctl
) {
1040 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
1041 print_ide_regs_io(regs
, NULL
);
1043 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1048 if (ctfregs
->bCommandReg
/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
1049 if (con
->reportataioctl
) {
1050 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
1051 print_ide_regs_io(regs
, ctfregs
);
1057 // Check and copy data
1059 if ( num_out
!= size
1060 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
1061 if (con
->reportataioctl
) {
1062 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out
);
1063 print_ide_regs_io(regs
, ctfregs
);
1068 memcpy(data
, ab
.ucDataBuf
, datasize
);
1071 if (con
->reportataioctl
> 1) {
1072 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1073 print_ide_regs_io(regs
, ctfregs
);
1077 *prev_regs
= *ptfregs
;
1083 /////////////////////////////////////////////////////////////////////////////
1085 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
1087 #define IOCTL_SCSI_PASS_THROUGH \
1088 CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
1090 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
1092 #define SCSI_IOCTL_DATA_OUT 0
1093 #define SCSI_IOCTL_DATA_IN 1
1094 #define SCSI_IOCTL_DATA_UNSPECIFIED 2
1095 // undocumented SCSI opcode to for ATA passthrough
1096 #define SCSIOP_ATA_PASSTHROUGH 0xCC
1098 typedef struct _SCSI_PASS_THROUGH
{
1105 UCHAR SenseInfoLength
;
1107 ULONG DataTransferLength
;
1109 ULONG
/*_PTR*/ DataBufferOffset
;
1110 ULONG SenseInfoOffset
;
1112 } SCSI_PASS_THROUGH
, *PSCSI_PASS_THROUGH
;
1114 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, 44);
1117 /////////////////////////////////////////////////////////////////////////////
1119 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1122 SCSI_PASS_THROUGH spt
;
1124 UCHAR ucSenseBuf
[32];
1125 UCHAR ucDataBuf
[512];
1126 } SCSI_PASS_THROUGH_WITH_BUFFERS
;
1128 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
1132 const unsigned char magic
= 0xcf;
1134 memset(&sb
, 0, sizeof(sb
));
1135 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH
);
1136 //sb.spt.PathId = 0;
1137 sb
.spt
.TargetId
= 1;
1139 sb
.spt
.CdbLength
= 10; sb
.spt
.SenseInfoLength
= 24;
1140 sb
.spt
.TimeOutValue
= 10;
1141 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
1142 size
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
1143 sb
.spt
.DataBufferOffset
= size
;
1146 if (datasize
> sizeof(sb
.ucDataBuf
)) {
1150 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
1151 sb
.spt
.DataTransferLength
= datasize
;
1153 sb
.ucDataBuf
[0] = magic
;
1156 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
1157 //sb.spt.DataTransferLength = 0;
1160 // Use pseudo SCSI command followed by registers
1161 sb
.spt
.Cdb
[0] = SCSIOP_ATA_PASSTHROUGH
;
1162 cdbregs
= (IDEREGS
*)(sb
.spt
.Cdb
+2);
1165 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_PASS_THROUGH
,
1166 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1167 long err
= GetLastError();
1168 if (con
->reportataioctl
)
1169 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err
);
1170 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1174 // Cannot check ATA status, because command does not return IDEREGS
1176 // Check and copy data
1178 if ( num_out
!= size
1179 || (sb
.ucDataBuf
[0] == magic
&& !nonempty(sb
.ucDataBuf
+1, datasize
-1))) {
1180 if (con
->reportataioctl
) {
1181 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out
);
1182 print_ide_regs_io(regs
, NULL
);
1187 memcpy(data
, sb
.ucDataBuf
, datasize
);
1190 if (con
->reportataioctl
> 1) {
1191 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1192 print_ide_regs_io(regs
, NULL
);
1198 /////////////////////////////////////////////////////////////////////////////
1200 // SMART IOCTL via SCSI MINIPORT ioctl
1202 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
1203 // miniport driver (via SCSI port driver scsiport.sys).
1204 // It can be used to skip the missing or broken handling of some SMART
1205 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
1207 #define IOCTL_SCSI_MINIPORT \
1208 CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
1210 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
1212 typedef struct _SRB_IO_CONTROL
{
1219 } SRB_IO_CONTROL
, *PSRB_IO_CONTROL
;
1221 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
1223 #define FILE_DEVICE_SCSI 0x001b
1225 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
1226 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
1227 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
1228 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
1229 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
1230 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
1231 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
1232 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
1233 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
1234 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
1235 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
1236 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
1237 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
1239 /////////////////////////////////////////////////////////////////////////////
1241 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
1244 DWORD code
= 0; const char * name
= 0;
1245 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
1246 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
1248 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
1249 case ATA_SMART_READ_VALUES
:
1250 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
1251 case ATA_SMART_READ_THRESHOLDS
:
1252 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
1253 case ATA_SMART_ENABLE
:
1254 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
1255 case ATA_SMART_DISABLE
:
1256 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
1257 case ATA_SMART_STATUS
:
1258 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
1259 case ATA_SMART_AUTOSAVE
:
1260 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
1261 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
1262 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
1263 case ATA_SMART_IMMEDIATE_OFFLINE
:
1264 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
1265 case ATA_SMART_AUTO_OFFLINE
:
1266 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
1267 case ATA_SMART_READ_LOG_SECTOR
:
1268 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
1269 case ATA_SMART_WRITE_LOG_SECTOR
:
1270 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
1279 SRB_IO_CONTROL srbc
;
1282 SENDCMDOUTPARAMS out
;
1286 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
1287 memset(&sb
, 0, sizeof(sb
));
1291 if (datasize
> (int)sizeof(sb
.space
)+1) {
1297 else if (datasize
< 0) {
1298 if (-datasize
> (int)sizeof(sb
.space
)+1) {
1303 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
1305 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1306 size
= sizeof(IDEREGS
);
1309 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1310 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
1311 sb
.srbc
.Timeout
= 60; // seconds
1312 sb
.srbc
.ControlCode
= code
;
1313 //sb.srbc.ReturnCode = 0;
1314 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
1315 sb
.params
.in
.irDriveRegs
= *regs
;
1316 sb
.params
.in
.cBufferSize
= size
;
1318 // Call miniport ioctl
1319 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
1321 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1322 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1323 long err
= GetLastError();
1324 if (con
->reportataioctl
) {
1325 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
1326 print_ide_regs_io(regs
, NULL
);
1328 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1333 if (sb
.srbc
.ReturnCode
) {
1334 if (con
->reportataioctl
) {
1335 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name
, sb
.srbc
.ReturnCode
);
1336 print_ide_regs_io(regs
, NULL
);
1342 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
1343 if (con
->reportataioctl
) {
1344 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1345 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
1346 print_ide_regs_io(regs
, NULL
);
1348 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1352 if (con
->reportataioctl
> 1) {
1353 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1354 num_out
, sb
.params
.out
.cBufferSize
);
1355 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
1356 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
1360 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
1361 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1362 *regs
= *(const IDEREGS
*)(sb
.params
.out
.bBuffer
);
1368 /////////////////////////////////////////////////////////////////////////////
1370 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1372 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1375 SRB_IO_CONTROL srbc
;
1379 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1381 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1385 memset(&sb
, 0, sizeof(sb
));
1386 strcpy((char *)sb
.srbc
.Signature
, "<3ware>");
1387 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1388 sb
.srbc
.Timeout
= 60; // seconds
1389 sb
.srbc
.ControlCode
= 0xA0000000;
1390 sb
.srbc
.ReturnCode
= 0;
1391 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1393 sb
.regs
.bReserved
= port
;
1396 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1397 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1398 long err
= GetLastError();
1399 if (con
->reportataioctl
) {
1400 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1401 print_ide_regs_io(regs
, NULL
);
1403 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1407 if (sb
.srbc
.ReturnCode
) {
1408 if (con
->reportataioctl
) {
1409 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb
.srbc
.ReturnCode
);
1410 print_ide_regs_io(regs
, NULL
);
1418 memcpy(data
, sb
.buffer
, datasize
);
1420 if (con
->reportataioctl
> 1) {
1421 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out
);
1422 print_ide_regs_io(regs
, &sb
.regs
);
1430 /////////////////////////////////////////////////////////////////////////////
1432 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1433 // 3DM/CLI "Rescan Controller" function does not to always update it.
1435 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1437 SRB_IO_CONTROL srbc
;
1438 memset(&srbc
, 0, sizeof(srbc
));
1439 strcpy((char *)srbc
.Signature
, "<3ware>");
1440 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1441 srbc
.Timeout
= 60; // seconds
1442 srbc
.ControlCode
= 0xCC010014;
1443 srbc
.ReturnCode
= 0;
1447 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1448 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1449 long err
= GetLastError();
1450 if (con
->reportataioctl
)
1451 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1452 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1455 if (srbc
.ReturnCode
) {
1456 if (con
->reportataioctl
)
1457 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc
.ReturnCode
);
1461 if (con
->reportataioctl
> 1)
1462 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1468 /////////////////////////////////////////////////////////////////////////////
1470 // Routines for pseudo device /dev/tw_cli/*
1471 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1474 // Get clipboard data
1476 static int get_clipboard(char * data
, int datasize
)
1478 if (!OpenClipboard(NULL
))
1480 HANDLE h
= GetClipboardData(CF_TEXT
);
1485 const void * p
= GlobalLock(h
);
1486 int n
= GlobalSize(h
);
1496 // Run a command, write stdout to dataout
1497 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
1499 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
1501 // Create stdout pipe
1502 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
1503 HANDLE pipe_out_w
, h
;
1504 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
1506 HANDLE self
= GetCurrentProcess();
1508 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
1509 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
1510 CloseHandle(pipe_out_w
);
1514 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
1515 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
1516 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1521 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
1522 si
.hStdInput
= INVALID_HANDLE_VALUE
;
1523 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
1524 si
.dwFlags
= STARTF_USESTDHANDLES
;
1525 PROCESS_INFORMATION pi
;
1527 NULL
, const_cast<char *>(cmd
),
1528 NULL
, NULL
, TRUE
/*inherit*/,
1529 CREATE_NO_WINDOW
/*do not create a new console window*/,
1530 NULL
, NULL
, &si
, &pi
)) {
1531 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1534 CloseHandle(pi
.hThread
);
1535 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
1537 // Copy stdout to output buffer
1539 while (i
< outsize
) {
1541 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
1545 CloseHandle(pipe_out_r
);
1547 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1548 CloseHandle(pi
.hProcess
);
1553 static const char * findstr(const char * str
, const char * sub
)
1555 const char * s
= strstr(str
, sub
);
1556 return (s
? s
+strlen(sub
) : "");
1560 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
1562 int srclen
= strcspn(src
, "\r\n");
1564 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
1565 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
1567 if (i
< destsize
-1 && i
< srclen
)
1572 // TODO: This is OS independent
1574 win_tw_cli_device::win_tw_cli_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
1575 : smart_device(intf
, dev_name
, "tw_cli", req_type
),
1576 m_ident_valid(false), m_smart_valid(false)
1578 memset(&m_ident_buf
, 0, sizeof(m_ident_buf
));
1579 memset(&m_smart_buf
, 0, sizeof(m_smart_buf
));
1583 bool win_tw_cli_device::is_open() const
1585 return (m_ident_valid
|| m_smart_valid
);
1589 bool win_tw_cli_device::open()
1591 m_ident_valid
= m_smart_valid
= false;
1592 const char * name
= skipdev(get_dev_name());
1593 // Read tw_cli or 3DM browser output into buffer
1595 int size
= -1, n1
= -1, n2
= -1;
1596 if (!strcmp(name
, "tw_cli/clip")) { // read clipboard
1597 size
= get_clipboard(buffer
, sizeof(buffer
));
1599 else if (!strcmp(name
, "tw_cli/stdin")) { // read stdin
1600 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
1602 else if (sscanf(name
, "tw_cli/%nc%*u/p%*u%n", &n1
, &n2
) >= 0 && n2
== (int)strlen(name
)) {
1603 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
1605 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
+n1
);
1606 if (con
->reportataioctl
> 1)
1607 pout("%s: Run: \"%s\"\n", name
, cmd
);
1608 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
1611 return set_err(EINVAL
);
1614 if (con
->reportataioctl
> 1)
1615 pout("%s: Read %d bytes\n", name
, size
);
1617 return set_err(ENOENT
);
1618 if (size
>= (int)sizeof(buffer
))
1619 return set_err(EIO
);
1622 if (con
->reportataioctl
> 1)
1623 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
1625 // Fake identify sector
1626 ASSERT_SIZEOF(ata_identify_device
, 512);
1627 ata_identify_device
* id
= &m_ident_buf
;
1628 memset(id
, 0, sizeof(*id
));
1629 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
1630 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
1631 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
1632 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
1633 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
1635 id
->words047_079
[49-47] = 0x0200; // size valid
1636 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
1637 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
1639 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1640 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1642 // Parse smart data hex dump
1643 const char * s
= findstr(buffer
, "Drive Smart Data:");
1645 s
= findstr(buffer
, "Drive SMART Data:"); // tw_cli from 9.5.x
1647 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
1649 const char * s1
= findstr(s
, "<td class"); // html version
1652 s
+= strcspn(s
, "\r\n");
1655 s
= buffer
; // try raw hex dump without header
1657 unsigned char * sd
= (unsigned char *)&m_smart_buf
;
1660 unsigned x
= ~0; int n
= -1;
1661 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
1663 sd
[i
] = (unsigned char)x
;
1664 if (!(++i
< 512 && n
> 0))
1667 if (*s
== '<') // "<br>"
1668 s
+= strcspn(s
, "\r\n");
1671 if (!id
->model
[1]) {
1672 // No useful data found
1673 char * err
= strstr(buffer
, "Error:");
1675 err
= strstr(buffer
, "error :");
1676 if (err
&& (err
= strchr(err
, ':'))) {
1677 // Show tw_cli error message
1679 err
[strcspn(err
, "\r\n")] = 0;
1680 return set_err(EIO
, err
);
1682 return set_err(EIO
);
1687 m_ident_valid
= true;
1688 m_smart_valid
= !!sd
;
1693 bool win_tw_cli_device::close()
1695 m_ident_valid
= m_smart_valid
= false;
1700 int win_tw_cli_device::ata_command_interface(smart_command_set command
, int /*select*/, char * data
)
1706 memcpy(data
, &m_ident_buf
, 512);
1711 memcpy(data
, &m_smart_buf
, 512);
1715 case STATUS_CHECK
: // Fake "good" SMART status
1720 // Arrive here for all unsupported commands
1726 /////////////////////////////////////////////////////////////////////////////
1728 // IOCTL_STORAGE_QUERY_PROPERTY
1730 #define FILE_DEVICE_MASS_STORAGE 0x0000002d
1731 #define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
1732 #define FILE_ANY_ACCESS 0
1734 #define IOCTL_STORAGE_QUERY_PROPERTY \
1735 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
1737 typedef enum _STORAGE_BUS_TYPE
{
1738 BusTypeUnknown
= 0x00,
1740 BusTypeAtapi
= 0x02,
1744 BusTypeFibre
= 0x06,
1747 BusTypeiScsi
= 0x09,
1753 BusTypeMaxReserved
= 0x7F
1754 } STORAGE_BUS_TYPE
, *PSTORAGE_BUS_TYPE
;
1756 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
1760 UCHAR DeviceTypeModifier
;
1761 BOOLEAN RemovableMedia
;
1762 BOOLEAN CommandQueueing
;
1763 ULONG VendorIdOffset
;
1764 ULONG ProductIdOffset
;
1765 ULONG ProductRevisionOffset
;
1766 ULONG SerialNumberOffset
;
1767 STORAGE_BUS_TYPE BusType
;
1768 ULONG RawPropertiesLength
;
1769 UCHAR RawDeviceProperties
[1];
1770 } STORAGE_DEVICE_DESCRIPTOR
, *PSTORAGE_DEVICE_DESCRIPTOR
;
1772 typedef enum _STORAGE_QUERY_TYPE
{
1773 PropertyStandardQuery
= 0,
1774 PropertyExistsQuery
,
1776 PropertyQueryMaxDefined
1777 } STORAGE_QUERY_TYPE
, *PSTORAGE_QUERY_TYPE
;
1779 typedef enum _STORAGE_PROPERTY_ID
{
1780 StorageDeviceProperty
= 0,
1781 StorageAdapterProperty
,
1782 StorageDeviceIdProperty
,
1783 StorageDeviceUniqueIdProperty
,
1784 StorageDeviceWriteCacheProperty
,
1785 StorageMiniportProperty
,
1786 StorageAccessAlignmentProperty
1787 } STORAGE_PROPERTY_ID
, *PSTORAGE_PROPERTY_ID
;
1789 typedef struct _STORAGE_PROPERTY_QUERY
{
1790 STORAGE_PROPERTY_ID PropertyId
;
1791 STORAGE_QUERY_TYPE QueryType
;
1792 UCHAR AdditionalParameters
[1];
1793 } STORAGE_PROPERTY_QUERY
, *PSTORAGE_PROPERTY_QUERY
;
1796 /////////////////////////////////////////////////////////////////////////////
1798 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1799 STORAGE_DEVICE_DESCRIPTOR desc
;
1803 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1804 // (This works without admin rights)
1806 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1808 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, {0} };
1809 memset(data
, 0, sizeof(*data
));
1812 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1813 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1814 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1)
1815 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
1820 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1) {
1821 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1823 " Product: \"%s\"\n"
1824 " Revision: \"%s\"\n"
1826 " BusType: 0x%02x\n",
1827 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: ""),
1828 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: ""),
1829 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: ""),
1830 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1837 /////////////////////////////////////////////////////////////////////////////
1839 // IOCTL_STORAGE_PREDICT_FAILURE
1841 #define IOCTL_STORAGE_PREDICT_FAILURE \
1842 CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
1844 typedef struct _STORAGE_PREDICT_FAILURE
{
1845 ULONG PredictFailure
;
1846 UCHAR VendorSpecific
[512];
1847 } STORAGE_PREDICT_FAILURE
, *PSTORAGE_PREDICT_FAILURE
;
1849 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
1852 /////////////////////////////////////////////////////////////////////////////
1855 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1856 // or -1 on error, opionally return VendorSpecific data.
1857 // (This works without admin rights)
1859 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1861 STORAGE_PREDICT_FAILURE pred
;
1862 memset(&pred
, 0, sizeof(pred
));
1865 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
1866 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
1867 if (con
->reportataioctl
> 1)
1868 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
1873 if (con
->reportataioctl
> 1) {
1874 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1875 " PredictFailure: 0x%08lx\n"
1876 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1877 pred
.PredictFailure
,
1878 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
1879 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
1883 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
1884 return (!pred
.PredictFailure
? 0 : 1);
1888 /////////////////////////////////////////////////////////////////////////////
1890 // get DEV_* for open handle
1891 static win_dev_type
get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
1893 // Try SMART_GET_VERSION first to detect ATA SMART support
1894 // for drivers reporting BusTypeScsi (3ware)
1895 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
1898 // Get BusType from device descriptor
1899 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1900 if (storage_query_property_ioctl(hdevice
, &data
))
1903 switch (data
.desc
.BusType
) {
1907 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
1921 // get DEV_* for device path
1922 static win_dev_type
get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
1925 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
1926 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1927 if (h
== INVALID_HANDLE_VALUE
) {
1929 h
= CreateFileA(path
, 0,
1930 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1931 if (h
== INVALID_HANDLE_VALUE
)
1934 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1)
1935 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
1936 win_dev_type type
= get_controller_type(h
, admin
, ata_version_ex
);
1941 // get DEV_* for physical drive number
1942 static win_dev_type
get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
1945 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
1946 return get_controller_type(path
, ata_version_ex
);
1949 static win_dev_type
get_phy_drive_type(int drive
)
1951 return get_phy_drive_type(drive
, 0);
1954 // get DEV_* for logical drive number
1955 static win_dev_type
get_log_drive_type(int drive
)
1958 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
1959 return get_controller_type(path
);
1962 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1963 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
1965 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1966 if (storage_query_property_ioctl(hdevice
, &data
))
1969 memset(id
, 0, sizeof(*id
));
1970 if (data
.desc
.ProductIdOffset
)
1971 copy_swapped(id
->model
, data
.raw
+data
.desc
.ProductIdOffset
, sizeof(id
->model
));
1972 if (data
.desc
.ProductRevisionOffset
)
1973 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
1974 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1975 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1980 /////////////////////////////////////////////////////////////////////////////
1981 // USB ID detection using WMI
1983 // Run a command, split stdout into lines.
1984 // Return number of lines read, -1 on error.
1985 static int run_cmd(std::vector
<std::string
> & lines
, const char * cmd
, ...)
1989 va_list ap
; va_start(ap
, cmd
);
1990 std::string cmdline
= vstrprintf(cmd
, ap
);
1993 if (con
->reportscsiioctl
> 1)
1994 pout("Run: \"%s\"\n", cmdline
.c_str());
1996 char buffer
[16*1024];
1997 int size
= run_cmd(cmdline
.c_str(), buffer
, sizeof(buffer
));
1999 if (con
->reportscsiioctl
> 1)
2000 pout("Read %d bytes\n", size
);
2001 if (!(0 < size
&& size
< (int)sizeof(buffer
)-1))
2006 for (int i
= 0; buffer
[i
]; ) {
2007 int len
= strcspn(buffer
+i
, "\r\n");
2008 lines
.push_back(std::string(buffer
+i
, len
));
2010 i
+= strspn(buffer
+i
, "\r\n");
2012 if (con
->reportscsiioctl
> 1) {
2013 for (unsigned i
= 0; i
< lines
.size(); i
++)
2014 printf("'%s'\n", lines
[i
].c_str());
2016 return lines
.size();
2019 // Quote string for WMI
2020 static std::string
wmi_quote(const char * s
, int len
)
2023 for (int i
= 0; i
< len
; i
++) {
2032 // Get USB ID for a physical drive number
2033 static bool get_usb_id(int drive
, unsigned short & vendor_id
, unsigned short & product_id
)
2036 std::vector
<std::string
> result
;
2038 "wmic PATH Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\" GET Model",
2042 std::string name
= result
[1];
2044 // Get USB_CONTROLLER -> DEVICE associations
2045 std::vector
<std::string
> assoc
;
2046 int n
= run_cmd(assoc
, "wmic PATH Win32_USBControllerDevice GET Antecedent,Dependent");
2050 regular_expression
regex("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"(USBSTOR\\\\[^\"]*)\" *$",
2052 if (regex
.empty()) // TODO: throw in constructor?
2055 int usbstoridx
= -1;
2056 std::string usbcontr
;
2057 for (int i
= 2; i
< n
; i
++) {
2058 // Find next 'USB_CONTROLLER USBSTORAGE_DEVICE' pair
2059 regmatch_t match
[3];
2060 const char * s
= assoc
[i
].c_str();
2061 if (!regex
.execute(s
, 3, match
))
2064 // USBSTOR device found, compare Name
2066 "wmic PATH Win32_PnPEntity WHERE DeviceID=\"%s\" GET Name",
2067 wmi_quote(s
+ match
[2].rm_so
, match
[2].rm_eo
- match
[2].rm_so
).c_str()
2070 if (result
[1] != name
)
2073 // Name must be uniqe
2074 if (usbstoridx
>= 0)
2078 usbcontr
.assign(s
+ match
[1].rm_so
, match
[1].rm_eo
- match
[1].rm_so
);
2082 if (usbstoridx
<= 0)
2085 // The entry preceding USBSTOR should be the USB bridge device
2086 regex
.compile("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"USB\\\\VID_(....&PID_....)[^\"]*\" *$",
2090 regmatch_t match
[3];
2091 const char * s
= assoc
[usbstoridx
-1].c_str();
2092 if (!regex
.execute(s
, 3, match
))
2095 // Both devices must be associated to same controller
2096 if (usbcontr
!= std::string(s
+ match
[1].rm_so
, match
[1].rm_eo
- match
[1].rm_so
))
2101 if (!(sscanf(s
+ match
[2].rm_so
, "%4hx&PID_%4hx%n",
2102 &vendor_id
, &product_id
, &nc
) == 2 && nc
== 4+5+4))
2105 if (con
->reportscsiioctl
> 1)
2106 pout("USB ID = 0x%04x:0x%04x\n", vendor_id
, product_id
);
2111 /////////////////////////////////////////////////////////////////////////////
2113 // Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
2114 // returns: 1=active, 0=standby, -1=error
2115 // (This would also work for SCSI drives)
2117 static int get_device_power_state(HANDLE hdevice
)
2119 static HINSTANCE h_kernel_dll
= 0;
2121 static DWORD kernel_dll_pid
= 0;
2123 static BOOL (WINAPI
* GetDevicePowerState_p
)(HANDLE
, BOOL
*) = 0;
2127 if (!GetDevicePowerState_p
2129 || kernel_dll_pid
!= GetCurrentProcessId() // detect fork()
2132 if (h_kernel_dll
== INVALID_HANDLE_VALUE
) {
2136 if (!(h_kernel_dll
= LoadLibraryA("KERNEL32.DLL"))) {
2137 pout("Cannot load KERNEL32.DLL, Error=%ld\n", GetLastError());
2138 h_kernel_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2142 if (!(GetDevicePowerState_p
= (BOOL (WINAPI
*)(HANDLE
, BOOL
*))
2143 GetProcAddress(h_kernel_dll
, "GetDevicePowerState"))) {
2144 if (con
->reportataioctl
)
2145 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
2146 FreeLibrary(h_kernel_dll
);
2147 h_kernel_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2152 kernel_dll_pid
= GetCurrentProcessId();
2156 if (!GetDevicePowerState_p(hdevice
, &state
)) {
2157 long err
= GetLastError();
2158 if (con
->reportataioctl
)
2159 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
2160 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
2161 // TODO: This may not work as expected on transient errors,
2162 // because smartd interprets -1 as SLEEP mode regardless of errno.
2166 if (con
->reportataioctl
> 1)
2167 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
2172 /////////////////////////////////////////////////////////////////////////////
2175 // Print SMARTVSD error message, return errno
2177 static int smartvsd_error()
2179 char path
[MAX_PATH
];
2181 if (!(5 <= (len
= GetSystemDirectoryA(path
, MAX_PATH
)) && len
< MAX_PATH
/2))
2183 // SMARTVSD.VXD present?
2184 strcpy(path
+len
, "\\IOSUBSYS\\SMARTVSD.VXD");
2185 if (!access(path
, 0)) {
2186 // Yes, standard IDE driver used?
2188 if ( (h
= CreateFileA("\\\\.\\ESDI_506",
2189 GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2190 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
2191 && GetLastError() == ERROR_FILE_NOT_FOUND
) {
2192 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
2196 if (h
!= INVALID_HANDLE_VALUE
) // should not happen
2198 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
2203 strcpy(path
+len
, "\\SMARTVSD.VXD");
2204 if (!access(path
, 0)) {
2205 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
2206 // (http://support.microsoft.com/kb/265854/en-us).
2208 pout("SMART driver is not properly installed,\n"
2209 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
2210 " and reboot Windows.\n", path
, path
);
2213 // Some Windows versions do not provide SMARTVSD.VXD
2214 // (http://support.microsoft.com/kb/199886/en-us).
2216 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path
);
2222 #endif // WIN9X_SUPPORT
2224 // Get default ATA device options
2226 static const char * ata_get_def_options()
2228 DWORD ver
= GetVersion();
2229 if ((ver
& 0x80000000) || (ver
& 0xff) < 4) // Win9x/ME
2230 return "s"; // SMART_* only
2231 else if ((ver
& 0xff) == 4) // WinNT4
2232 return "sc"; // SMART_*, SCSI_PASS_THROUGH
2233 else // WinXP, 2003, Vista
2234 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
2235 // STORAGE_*, SCSI_MINIPORT_*
2239 // Common routines for devices with HANDLEs
2241 win_smart_device::~win_smart_device() throw()
2243 if (m_fh
!= INVALID_HANDLE_VALUE
)
2244 ::CloseHandle(m_fh
);
2247 bool win_smart_device::is_open() const
2249 return (m_fh
!= INVALID_HANDLE_VALUE
);
2252 bool win_smart_device::close()
2254 if (m_fh
== INVALID_HANDLE_VALUE
)
2256 BOOL rc
= ::CloseHandle(m_fh
);
2257 m_fh
= INVALID_HANDLE_VALUE
;
2263 win_ata_device::win_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
2264 : smart_device(intf
, dev_name
, "ata", req_type
),
2265 m_usr_options(false),
2267 m_id_is_cached(false),
2274 win_ata_device::~win_ata_device() throw()
2281 bool win_ata_device::open()
2283 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
2284 // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
2285 char drive
[1+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
2286 if ( sscanf(name
, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive
, &n1
, options
, &n2
) >= 1
2287 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2288 return open(drive
[0] - 'a', -1, options
, -1);
2290 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
2291 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
2293 if ( sscanf(name
, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
2294 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
2295 return open(drive
[0] - 'a', -1, options
, port
);
2297 // pd<m>,N => Physical drive <m>, RAID port N
2298 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
2299 if ( sscanf(name
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
2300 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
2301 return open(phydrive
, -1, "", (int)port
);
2303 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2304 int logdrive
= drive_letter(name
);
2305 if (logdrive
>= 0) {
2306 return open(-1, logdrive
, "", -1);
2309 return set_err(EINVAL
);
2313 bool win_ata_device::open(int phydrive
, int logdrive
, const char * options
, int port
)
2315 // path depends on Windows Version
2317 if (win9x
&& 0 <= phydrive
&& phydrive
<= 7)
2318 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
2319 strcpy(devpath
, (phydrive
<= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
2320 else if (!win9x
&& 0 <= phydrive
&& phydrive
<= 255)
2321 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", phydrive
);
2322 else if (!win9x
&& 0 <= logdrive
&& logdrive
<= 'Z'-'A')
2323 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
2325 return set_err(ENOENT
);
2328 HANDLE h
= INVALID_HANDLE_VALUE
;
2329 if (win9x
|| !(*options
&& !options
[strspn(options
, "fp")])) {
2330 // Open with admin rights
2332 h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2333 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2334 NULL
, OPEN_EXISTING
, 0, 0);
2336 if (!win9x
&& h
== INVALID_HANDLE_VALUE
) {
2337 // Open without admin rights
2339 h
= CreateFileA(devpath
, 0,
2340 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2341 NULL
, OPEN_EXISTING
, 0, 0);
2343 if (h
== INVALID_HANDLE_VALUE
) {
2344 long err
= GetLastError();
2346 if (win9x
&& phydrive
<= 3 && err
== ERROR_FILE_NOT_FOUND
)
2349 if (err
== ERROR_FILE_NOT_FOUND
)
2350 set_err(ENOENT
, "%s: not found", devpath
);
2351 else if (err
== ERROR_ACCESS_DENIED
)
2352 set_err(EACCES
, "%s: access denied", devpath
);
2354 set_err(EIO
, "%s: Error=%ld", devpath
, err
);
2359 // Warn once if admin rights are missing
2361 static bool noadmin_warning
= false;
2362 if (!noadmin_warning
) {
2363 pout("Warning: Limited functionality due to missing admin rights\n");
2364 noadmin_warning
= true;
2368 if (con
->reportataioctl
> 1)
2369 pout("%s: successfully opened%s\n", devpath
, (!m_admin
? " (without admin rights)" :""));
2371 m_usr_options
= false;
2373 // Save user options
2374 m_options
= options
; m_usr_options
= true;
2377 // RAID: SMART_* and SCSI_MINIPORT
2380 // Set default options according to Windows version
2381 static const char * def_options
= ata_get_def_options();
2382 m_options
= def_options
;
2385 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
2386 m_drive
= 0; m_port
= port
;
2387 if (!win9x
&& port
< 0)
2390 // Win9X/ME: Get drive map
2391 // RAID: Get port map
2392 GETVERSIONINPARAMS_EX vers_ex
;
2393 int devmap
= smart_get_version(h
, (port
>= 0 ? &vers_ex
: 0));
2395 unsigned long portmap
= 0;
2396 if (port
>= 0 && devmap
>= 0) {
2397 // 3ware RAID: check vendor id
2398 if (vers_ex
.wIdentifier
!= SMART_VENDOR_3WARE
) {
2399 pout("SMART_GET_VERSION returns unknown Identifier = %04x\n"
2400 "This is no 3ware 9000 controller or driver has no SMART support.\n",
2401 vers_ex
.wIdentifier
);
2405 portmap
= vers_ex
.dwDeviceMapEx
;
2408 pout("%s: ATA driver has no SMART support\n", devpath
);
2409 if (!is_permissive()) {
2411 return set_err(ENOSYS
);
2415 m_smartver_state
= 1;
2418 // 3ware RAID: update devicemap first
2420 if (!update_3ware_devicemap_ioctl(h
)) {
2421 if ( smart_get_version(h
, &vers_ex
) >= 0
2422 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
2423 portmap
= vers_ex
.dwDeviceMapEx
;
2425 // Check port existence
2426 if (!(portmap
& (1L << port
))) {
2427 if (!is_permissive()) {
2429 return set_err(ENOENT
, "%s: Port %d is empty or does not exist", devpath
, port
);
2435 // Win9x/ME: Check device presence & type
2436 if (((devmap
>> (phydrive
& 0x3)) & 0x11) != 0x01) {
2437 unsigned char atapi
= (devmap
>> (phydrive
& 0x3)) & 0x10;
2438 // Win9x drive existence check may not work as expected
2439 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
2440 // (The related KB Article Q196120 is no longer available)
2441 if (!is_permissive()) {
2443 return set_err((atapi
? ENOSYS
: ENOENT
), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
2444 devpath
, phydrive
, (atapi
?"is an ATAPI device":"does not exist"), devmap
);
2447 // Drive number must be passed to ioctl
2448 m_drive
= (phydrive
& 0x3);
2455 // Scan for ATA drives on Win9x/ME
2457 bool win9x_smart_interface::ata_scan(smart_device_list
& devlist
)
2460 const char devpath
[] = "\\\\.\\SMARTVSD";
2461 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2462 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0);
2463 if (h
== INVALID_HANDLE_VALUE
) {
2464 if (con
->reportataioctl
> 1)
2465 pout(" %s: Open failed, Error=%ld\n", devpath
, GetLastError());
2466 return true; // SMARTVSD.VXD missing or no ATA devices
2470 int devmap
= smart_get_version(h
);
2473 return true; // Should not happen
2475 // Check ATA device presence, remove ATAPI devices
2476 devmap
= (devmap
& 0xf) & ~((devmap
>> 4) & 0xf);
2478 for (int i
= 0; i
< 4; i
++) {
2479 if (!(devmap
& (1 << i
)))
2481 sprintf(name
, "/dev/hd%c", 'a'+i
);
2482 devlist
.push_back( new win_ata_device(this, name
, "ata") );
2487 #endif // WIN9X_SUPPORT
2490 // Scan for ATA drives
2492 bool winnt_smart_interface::ata_scan(smart_device_list
& devlist
)
2494 const int max_raid
= 2;
2495 bool raid_seen
[max_raid
] = {false, false};
2498 for (int i
= 0; i
<= 9; i
++) {
2499 GETVERSIONINPARAMS_EX vers_ex
;
2500 if (get_phy_drive_type(i
, &vers_ex
) != DEV_ATA
)
2503 // Interpret RAID drive map if present
2504 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
2505 // Skip if more than 2 controllers or logical drive from this controller already seen
2506 if (vers_ex
.wControllerId
>= max_raid
|| raid_seen
[vers_ex
.wControllerId
])
2508 raid_seen
[vers_ex
.wControllerId
] = true;
2509 // Add physical drives
2510 for (int pi
= 0; pi
< 32; pi
++) {
2511 if (vers_ex
.dwDeviceMapEx
& (1L << pi
)) {
2512 sprintf(name
, "/dev/sd%c,%u", 'a'+i
, pi
);
2513 devlist
.push_back( new win_ata_device(this, name
, "ata") );
2519 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
2520 sprintf(name
, "/dev/sd%c", 'a'+i
);
2521 devlist
.push_back( new win_ata_device(this, name
, "ata") );
2528 /////////////////////////////////////////////////////////////////////////////
2530 // Interface to ATA devices
2531 bool win_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
2533 // No multi-sector support for now, see above
2534 // warning about IOCTL_ATA_PASS_THROUGH
2535 if (!ata_cmd_is_ok(in
,
2536 true, // data_out_support
2537 false, // !multi_sector_support
2538 true) // ata_48bit_support
2542 // Determine ioctl functions valid for this ATA cmd
2543 const char * valid_options
= 0;
2545 switch (in
.in_regs
.command
) {
2546 case ATA_IDENTIFY_DEVICE
:
2547 case ATA_IDENTIFY_PACKET_DEVICE
:
2548 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2549 // and SCSI_MINIPORT_* if requested by user
2550 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2553 case ATA_CHECK_POWER_MODE
:
2554 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
2555 valid_options
= "pai3";
2559 switch (in
.in_regs
.features
) {
2560 case ATA_SMART_READ_VALUES
:
2561 case ATA_SMART_READ_THRESHOLDS
:
2562 case ATA_SMART_AUTOSAVE
:
2563 case ATA_SMART_ENABLE
:
2564 case ATA_SMART_DISABLE
:
2565 case ATA_SMART_AUTO_OFFLINE
:
2566 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2567 // and SCSI_MINIPORT_* if requested by user
2568 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2571 case ATA_SMART_IMMEDIATE_OFFLINE
:
2572 // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
2573 valid_options
= (m_usr_options
|| in
.in_regs
.lba_low
!= 127/*ABORT*/ || win9x
?
2574 "saicm3" : "aicm3");
2577 case ATA_SMART_READ_LOG_SECTOR
:
2578 // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
2579 // Try SCSI_MINIPORT also to skip buggy class driver
2580 // SMART functions do not support multi sector I/O.
2582 valid_options
= (m_usr_options
|| win9x
? "saicm3" : "aicm3");
2584 valid_options
= "a";
2587 case ATA_SMART_WRITE_LOG_SECTOR
:
2588 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
2589 // but SCSI_MINIPORT_* only if requested by user and single sector.
2590 valid_options
= (in
.size
== 512 && m_usr_options
? "am" : "a");
2593 case ATA_SMART_STATUS
:
2594 // May require lba_mid,lba_high register return
2595 if (in
.out_needed
.is_set())
2596 valid_options
= (m_usr_options
? "saimf" : "saif");
2598 valid_options
= (m_usr_options
? "saicmf" : "saicf");
2602 // Unknown SMART command, handle below
2608 // Other ATA command, handle below
2612 if (!valid_options
) {
2613 // No special ATA command found above, select a generic pass through ioctl.
2614 if (!( in
.direction
== ata_cmd_in::no_data
2615 || (in
.direction
== ata_cmd_in::data_in
&& in
.size
== 512))
2616 || in
.in_regs
.is_48bit_cmd() )
2617 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
2618 valid_options
= "a";
2619 else if (in
.out_needed
.is_set())
2620 // Need output registers: ATA/IDE_PASS_THROUGH
2621 valid_options
= "ai";
2623 valid_options
= "aic";
2627 // Restrict to IOCTL_STORAGE_*
2628 if (strchr(valid_options
, 'f'))
2629 valid_options
= "f";
2630 else if (strchr(valid_options
, 'p'))
2631 valid_options
= "p";
2633 return set_err(ENOSYS
, "Function requires admin rights");
2637 IDEREGS regs
, prev_regs
;
2639 const ata_in_regs
& lo
= in
.in_regs
;
2640 regs
.bFeaturesReg
= lo
.features
;
2641 regs
.bSectorCountReg
= lo
.sector_count
;
2642 regs
.bSectorNumberReg
= lo
.lba_low
;
2643 regs
.bCylLowReg
= lo
.lba_mid
;
2644 regs
.bCylHighReg
= lo
.lba_high
;
2645 regs
.bDriveHeadReg
= lo
.device
;
2646 regs
.bCommandReg
= lo
.command
;
2649 if (in
.in_regs
.is_48bit_cmd()) {
2650 const ata_in_regs
& hi
= in
.in_regs
.prev
;
2651 prev_regs
.bFeaturesReg
= hi
.features
;
2652 prev_regs
.bSectorCountReg
= hi
.sector_count
;
2653 prev_regs
.bSectorNumberReg
= hi
.lba_low
;
2654 prev_regs
.bCylLowReg
= hi
.lba_mid
;
2655 prev_regs
.bCylHighReg
= hi
.lba_high
;
2656 prev_regs
.bDriveHeadReg
= hi
.device
;
2657 prev_regs
.bCommandReg
= hi
.command
;
2658 prev_regs
.bReserved
= 0;
2661 // Set data direction
2664 switch (in
.direction
) {
2665 case ata_cmd_in::no_data
:
2667 case ata_cmd_in::data_in
:
2668 datasize
= (int)in
.size
;
2669 data
= (char *)in
.buffer
;
2671 case ata_cmd_in::data_out
:
2672 datasize
= -(int)in
.size
;
2673 data
= (char *)in
.buffer
;
2676 return set_err(EINVAL
, "win_ata_device::ata_pass_through: invalid direction=%d",
2681 // Try all valid ioctls in the order specified in m_options
2682 bool powered_up
= false;
2683 bool out_regs_set
= false;
2684 bool id_is_cached
= false;
2685 const char * options
= m_options
.c_str();
2687 for (int i
= 0; ; i
++) {
2688 char opt
= options
[i
];
2691 if (in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& powered_up
) {
2692 // Power up reported by GetDevicePowerState() and no ioctl available
2693 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
2694 regs
.bSectorCountReg
= 0xff;
2695 out_regs_set
= true;
2699 return set_err(ENOSYS
);
2701 if (!strchr(valid_options
, opt
))
2702 // Invalid for this command
2706 assert( datasize
== 0 || datasize
== 512
2707 || (datasize
== -512 && strchr("am", opt
))
2708 || (datasize
> 512 && opt
== 'a'));
2713 // call SMART_GET_VERSION once for each drive
2714 if (m_smartver_state
> 1) {
2715 rc
= -1; errno
= ENOSYS
;
2718 if (!m_smartver_state
) {
2719 assert(m_port
== -1);
2720 if (smart_get_version(get_fh()) < 0) {
2721 if (!con
->permissive
) {
2722 m_smartver_state
= 2;
2723 rc
= -1; errno
= ENOSYS
;
2728 m_smartver_state
= 1;
2730 rc
= smart_ioctl(get_fh(), m_drive
, ®s
, data
, datasize
, m_port
);
2731 out_regs_set
= (in
.in_regs
.features
== ATA_SMART_STATUS
);
2732 id_is_cached
= (m_port
< 0 && !win9x
); // Not cached by 3ware or Win9x/ME driver
2735 rc
= ata_via_scsi_miniport_smart_ioctl(get_fh(), ®s
, data
, datasize
);
2736 id_is_cached
= (m_port
< 0 && !win9x
);
2739 rc
= ata_pass_through_ioctl(get_fh(), ®s
,
2740 (in
.in_regs
.is_48bit_cmd() ? &prev_regs
: 0),
2742 out_regs_set
= true;
2745 rc
= ide_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
2746 out_regs_set
= true;
2749 rc
= ata_via_scsi_pass_through_ioctl(get_fh(), ®s
, data
, datasize
);
2752 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
) {
2753 rc
= get_identify_from_device_property(get_fh(), (ata_identify_device
*)data
);
2754 id_is_cached
= true;
2756 else if (in
.in_regs
.command
== ATA_SMART_CMD
) switch (in
.in_regs
.features
) {
2757 case ATA_SMART_READ_VALUES
:
2758 rc
= storage_predict_failure_ioctl(get_fh(), data
);
2762 case ATA_SMART_ENABLE
:
2765 case ATA_SMART_STATUS
:
2766 rc
= storage_predict_failure_ioctl(get_fh());
2768 // Good SMART status
2769 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
2773 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
2778 errno
= ENOSYS
; rc
= -1;
2781 errno
= ENOSYS
; rc
= -1;
2785 rc
= ata_via_3ware_miniport_ioctl(get_fh(), ®s
, data
, datasize
, m_port
);
2786 out_regs_set
= true;
2789 assert(in
.in_regs
.command
== ATA_CHECK_POWER_MODE
&& in
.size
== 0);
2790 rc
= get_device_power_state(get_fh());
2792 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
2793 // spin up the drive => simulate ATA result STANDBY.
2794 regs
.bSectorCountReg
= 0x00;
2795 out_regs_set
= true;
2798 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
2799 // only if it is selected by the device driver => try a passthrough ioctl to get the
2800 // actual mode, if none available simulate ACTIVE/IDLE.
2802 rc
= -1; errno
= ENOSYS
;
2808 // Working ioctl found
2811 if (errno
!= ENOSYS
)
2812 // Abort on I/O error
2813 return set_err(errno
);
2815 out_regs_set
= false;
2816 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
2819 // Return IDEREGS if set
2821 ata_out_regs
& lo
= out
.out_regs
;
2822 lo
.error
= regs
.bFeaturesReg
;
2823 lo
.sector_count
= regs
.bSectorCountReg
;
2824 lo
.lba_low
= regs
.bSectorNumberReg
;
2825 lo
.lba_mid
= regs
.bCylLowReg
;
2826 lo
.lba_high
= regs
.bCylHighReg
;
2827 lo
.device
= regs
.bDriveHeadReg
;
2828 lo
.status
= regs
.bCommandReg
;
2829 if (in
.in_regs
.is_48bit_cmd()) {
2830 ata_out_regs
& hi
= out
.out_regs
.prev
;
2831 hi
.sector_count
= prev_regs
.bSectorCountReg
;
2832 hi
.lba_low
= prev_regs
.bSectorNumberReg
;
2833 hi
.lba_mid
= prev_regs
.bCylLowReg
;
2834 hi
.lba_high
= prev_regs
.bCylHighReg
;
2838 if ( in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
2839 || in
.in_regs
.command
== ATA_IDENTIFY_PACKET_DEVICE
)
2840 // Update ata_identify_is_cached() result according to ioctl used.
2841 m_id_is_cached
= id_is_cached
;
2846 // Return true if OS caches the ATA identify sector
2847 bool win_ata_device::ata_identify_is_cached() const
2849 return m_id_is_cached
;
2853 /////////////////////////////////////////////////////////////////////////////
2854 // ASPI Interface (for SCSI devices on 9x/ME)
2855 /////////////////////////////////////////////////////////////////////////////
2861 #define ASPI_SENSE_SIZE 18
2863 // ASPI SCSI Request block header
2866 unsigned char cmd
; // 00: Command code
2867 unsigned char status
; // 01: ASPI status
2868 unsigned char adapter
; // 02: Host adapter number
2869 unsigned char flags
; // 03: Request flags
2870 unsigned char reserved
[4]; // 04: 0
2873 // SRB for host adapter inquiry
2876 ASPI_SRB_HEAD h
; // 00: Header
2877 unsigned char adapters
; // 08: Number of adapters
2878 unsigned char target_id
; // 09: Target ID ?
2879 char manager_id
[16]; // 10: SCSI manager ID
2880 char adapter_id
[16]; // 26: Host adapter ID
2881 unsigned char parameters
[16]; // 42: Host adapter unique parmameters
2884 // SRB for get device type
2887 ASPI_SRB_HEAD h
; // 00: Header
2888 unsigned char target_id
; // 08: Target ID
2889 unsigned char lun
; // 09: LUN
2890 unsigned char devtype
; // 10: Device type
2891 unsigned char reserved
; // 11: Reserved
2897 ASPI_SRB_HEAD h
; // 00: Header
2898 unsigned char target_id
; // 08: Target ID
2899 unsigned char lun
; // 09: LUN
2900 unsigned char reserved
[2]; // 10: Reserved
2901 unsigned long data_size
; // 12: Data alloc. lenght
2902 void * data_addr
; // 16: Data buffer pointer
2903 unsigned char sense_size
; // 20: Sense alloc. length
2904 unsigned char cdb_size
; // 21: CDB length
2905 unsigned char host_status
; // 22: Host status
2906 unsigned char target_status
; // 23: Target status
2907 void * event_handle
; // 24: Event handle
2908 unsigned char workspace
[20]; // 28: ASPI workspace
2909 unsigned char cdb
[16+ASPI_SENSE_SIZE
];
2912 // Macro to retrieve start of sense information
2913 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
2918 ASPI_SRB_HEAD h
; // Common header
2919 ASPI_SRB_INQUIRY q
; // Inquiry
2920 ASPI_SRB_DEVTYPE t
; // Device type
2921 ASPI_SRB_IO i
; // I/O
2927 #define ASPI_CMD_ADAPTER_INQUIRE 0x00
2928 #define ASPI_CMD_GET_DEVICE_TYPE 0x01
2929 #define ASPI_CMD_EXECUTE_IO 0x02
2930 #define ASPI_CMD_ABORT_IO 0x03
2933 #define ASPI_REQFLAG_DIR_TO_HOST 0x08
2934 #define ASPI_REQFLAG_DIR_TO_TARGET 0x10
2935 #define ASPI_REQFLAG_DIR_NO_XFER 0x18
2936 #define ASPI_REQFLAG_EVENT_NOTIFY 0x40
2939 #define ASPI_STATUS_IN_PROGRESS 0x00
2940 #define ASPI_STATUS_NO_ERROR 0x01
2941 #define ASPI_STATUS_ABORTED 0x02
2942 #define ASPI_STATUS_ABORT_ERR 0x03
2943 #define ASPI_STATUS_ERROR 0x04
2944 #define ASPI_STATUS_INVALID_COMMAND 0x80
2945 #define ASPI_STATUS_INVALID_ADAPTER 0x81
2946 #define ASPI_STATUS_INVALID_TARGET 0x82
2947 #define ASPI_STATUS_NO_ADAPTERS 0xE8
2949 // Adapter (host) status
2950 #define ASPI_HSTATUS_NO_ERROR 0x00
2951 #define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
2952 #define ASPI_HSTATUS_DATA_OVERRUN 0x12
2953 #define ASPI_HSTATUS_BUS_FREE 0x13
2954 #define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
2955 #define ASPI_HSTATUS_BAD_SGLIST 0x1A
2958 #define ASPI_TSTATUS_NO_ERROR 0x00
2959 #define ASPI_TSTATUS_CHECK_CONDITION 0x02
2960 #define ASPI_TSTATUS_BUSY 0x08
2961 #define ASPI_TSTATUS_RESERV_CONFLICT 0x18
2964 static HINSTANCE h_aspi_dll
; // DLL handle
2965 static UINT (* aspi_entry
)(ASPI_SRB
* srb
); // ASPI entrypoint
2966 static unsigned num_aspi_adapters
;
2969 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
2970 static DWORD aspi_dll_pid
; // PID of DLL owner to detect fork()
2971 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
2973 #define aspi_entry_valid() (!!aspi_entry)
2977 static int aspi_call(ASPI_SRB
* srb
)
2982 while (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
2983 if (++i
> 100/*10sek*/) {
2984 pout("ASPI Adapter %u: Timed out\n", srb
->h
.adapter
);
2986 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2990 if (con
->reportscsiioctl
> 1)
2991 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb
->h
.adapter
, i
);
2998 // Get ASPI entrypoint from wnaspi32.dll
3000 static FARPROC
aspi_get_address(const char * name
, int verbose
)
3003 assert(h_aspi_dll
&& h_aspi_dll
!= INVALID_HANDLE_VALUE
);
3005 if (!(addr
= GetProcAddress(h_aspi_dll
, name
))) {
3007 pout("Missing %s() in WNASPI32.DLL\n", name
);
3009 FreeLibrary(h_aspi_dll
);
3010 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3018 static int aspi_open_dll(int verbose
)
3020 UINT (*aspi_info
)(void);
3023 assert(!aspi_entry_valid());
3025 // Check structure layout
3026 assert(sizeof(ASPI_SRB_HEAD
) == 8);
3027 assert(sizeof(ASPI_SRB_INQUIRY
) == 58);
3028 assert(sizeof(ASPI_SRB_DEVTYPE
) == 12);
3029 assert(sizeof(ASPI_SRB_IO
) == 64+ASPI_SENSE_SIZE
);
3030 assert(offsetof(ASPI_SRB
,h
.cmd
) == 0);
3031 assert(offsetof(ASPI_SRB
,h
.flags
) == 3);
3032 assert(offsetof(ASPI_SRB_IO
,lun
) == 9);
3033 assert(offsetof(ASPI_SRB_IO
,data_addr
) == 16);
3034 assert(offsetof(ASPI_SRB_IO
,workspace
) == 28);
3035 assert(offsetof(ASPI_SRB_IO
,cdb
) == 48);
3037 if (h_aspi_dll
== INVALID_HANDLE_VALUE
) {
3044 if (!(h_aspi_dll
= LoadLibraryA("WNASPI32.DLL"))) {
3046 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
3047 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3051 if (con
->reportscsiioctl
> 1) {
3052 // Print full path of WNASPI32.DLL
3053 char path
[MAX_PATH
];
3054 if (!GetModuleFileName(h_aspi_dll
, path
, sizeof(path
)))
3055 strcpy(path
, "*unknown*");
3056 pout("Using ASPI interface \"%s\"\n", path
);
3059 // Get ASPI entrypoints
3060 if (!(aspi_info
= (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose
)))
3062 if (!(aspi_entry
= (UINT (*)(ASPI_SRB
*))aspi_get_address("SendASPI32Command", verbose
)))
3065 // Init ASPI manager and get number of adapters
3066 info
= (aspi_info
)();
3067 if (con
->reportscsiioctl
> 1)
3068 pout("GetASPI32SupportInfo() returns 0x%04x\n", info
);
3069 rc
= (info
>> 8) & 0xff;
3070 if (rc
== ASPI_STATUS_NO_ADAPTERS
) {
3071 num_aspi_adapters
= 0;
3073 else if (rc
== ASPI_STATUS_NO_ERROR
) {
3074 num_aspi_adapters
= info
& 0xff;
3078 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info
);
3080 FreeLibrary(h_aspi_dll
);
3081 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3086 if (con
->reportscsiioctl
)
3087 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3090 // save PID to detect fork() in aspi_entry_valid()
3091 aspi_dll_pid
= GetCurrentProcessId();
3093 assert(aspi_entry_valid());
3098 static int aspi_io_call(ASPI_SRB
* srb
, unsigned timeout
)
3102 if (!(event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
))) {
3103 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO
;
3105 srb
->i
.event_handle
= event
;
3106 srb
->h
.flags
|= ASPI_REQFLAG_EVENT_NOTIFY
;
3107 // Start ASPI request
3109 if (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
3111 DWORD rc
= WaitForSingleObject(event
, timeout
*1000L);
3112 if (rc
!= WAIT_OBJECT_0
) {
3113 if (rc
== WAIT_TIMEOUT
) {
3114 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
3115 srb
->h
.adapter
, srb
->i
.target_id
, timeout
);
3118 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
3119 (unsigned long)event
, rc
, rc
, GetLastError());
3121 // TODO: ASPI_ABORT_IO command
3123 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
3132 win_aspi_device::win_aspi_device(smart_interface
* intf
,
3133 const char * dev_name
, const char * req_type
)
3134 : smart_device(intf
, dev_name
, "scsi", req_type
),
3135 m_adapter(-1), m_id(0)
3139 bool win_aspi_device::is_open() const
3141 return (m_adapter
>= 0);
3144 bool win_aspi_device::open()
3146 // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
3147 unsigned adapter
= ~0, id
= ~0; int n1
= -1;
3148 const char * name
= skipdev(get_dev_name());
3149 if (!(sscanf(name
,"scsi%1u%1x%n", &adapter
, &id
, &n1
) == 2 && n1
== (int)strlen(name
)
3150 && adapter
<= 9 && id
< 16))
3151 return set_err(EINVAL
);
3153 if (!aspi_entry_valid()) {
3154 if (aspi_open_dll(1/*verbose*/))
3155 return set_err(ENOENT
);
3159 if (adapter
>= num_aspi_adapters
) {
3160 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
3161 adapter
, num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
3162 if (!is_permissive())
3163 return set_err(ENOENT
);
3168 memset(&srb
, 0, sizeof(srb
));
3169 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3170 srb
.h
.adapter
= adapter
; srb
.i
.target_id
= id
;
3171 if (aspi_call(&srb
))
3172 return set_err(EIO
);
3173 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3174 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter
, id
, srb
.h
.status
);
3175 if (!is_permissive())
3176 return set_err(srb
.h
.status
== ASPI_STATUS_INVALID_TARGET
? ENOENT
: EIO
);
3178 else if (con
->reportscsiioctl
)
3179 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter
, id
, srb
.t
.devtype
);
3181 m_adapter
= (int)adapter
; m_id
= (unsigned char)id
;
3186 bool win_aspi_device::close()
3188 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
3193 // Scan for ASPI drives
3195 bool win9x_smart_interface::scsi_scan(smart_device_list
& devlist
)
3197 if (!aspi_entry_valid()) {
3198 if (aspi_open_dll(con
->reportscsiioctl
/*default is quiet*/))
3202 for (unsigned ad
= 0; ad
< num_aspi_adapters
; ad
++) {
3206 if (con
->reportscsiioctl
)
3207 pout(" ASPI Adapter %u: Ignored\n", ad
);
3212 memset(&srb
, 0, sizeof(srb
));
3213 srb
.h
.cmd
= ASPI_CMD_ADAPTER_INQUIRE
;
3215 if (aspi_call(&srb
))
3218 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3219 if (con
->reportscsiioctl
)
3220 pout(" ASPI Adapter %u: Status=0x%02x\n", ad
, srb
.h
.status
);
3224 if (con
->reportscsiioctl
) {
3225 for (int i
= 1; i
< 16 && srb
.q
.adapter_id
[i
]; i
++)
3226 if (!(' ' <= srb
.q
.adapter_id
[i
] && srb
.q
.adapter_id
[i
] <= '~'))
3227 srb
.q
.adapter_id
[i
] = '?';
3228 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad
, srb
.q
.adapter_id
);
3231 bool ignore
= !strnicmp(srb
.q
.adapter_id
, "3ware", 5);
3233 for (unsigned id
= 0; id
<= 7; id
++) {
3235 memset(&srb
, 0, sizeof(srb
));
3236 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
3237 srb
.h
.adapter
= ad
; srb
.i
.target_id
= id
;
3238 if (aspi_call(&srb
))
3240 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3241 if (con
->reportscsiioctl
> 1)
3242 pout(" ID %u: No such device (Status=0x%02x)\n", id
, srb
.h
.status
);
3246 if (!ignore
&& srb
.t
.devtype
== 0x00/*HDD*/) {
3247 if (con
->reportscsiioctl
)
3248 pout(" ID %u: Device Type=0x%02x\n", id
, srb
.t
.devtype
);
3250 sprintf(name
, "/dev/scsi%u%u", ad
, id
);
3251 devlist
.push_back( new win_aspi_device(this, name
, "scsi") );
3253 else if (con
->reportscsiioctl
)
3254 pout(" ID %u: Device Type=0x%02x (ignored)\n", id
, srb
.t
.devtype
);
3261 // Interface to ASPI SCSI devices
3262 bool win_aspi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
3264 int report
= con
->reportscsiioctl
; // TODO
3266 if (m_adapter
< 0) {
3271 if (!aspi_entry_valid()) {
3276 if (!(iop
->cmnd_len
== 6 || iop
->cmnd_len
== 10 || iop
->cmnd_len
== 12 || iop
->cmnd_len
== 16)) {
3277 set_err(EINVAL
, "bad CDB length");
3284 const unsigned char * ucp
= iop
->cmnd
;
3287 const int sz
= (int)sizeof(buff
);
3289 np
= scsi_get_opcode_name(ucp
[0]);
3290 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3291 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3292 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3294 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3295 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3297 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3298 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3299 (trunc
? " [only first 256 bytes shown]" : ""));
3300 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3303 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3308 memset(&srb
, 0, sizeof(srb
));
3309 srb
.h
.cmd
= ASPI_CMD_EXECUTE_IO
;
3310 srb
.h
.adapter
= m_adapter
;
3311 srb
.i
.target_id
= m_id
;
3313 srb
.i
.sense_size
= ASPI_SENSE_SIZE
;
3314 srb
.i
.cdb_size
= iop
->cmnd_len
;
3315 memcpy(srb
.i
.cdb
, iop
->cmnd
, iop
->cmnd_len
);
3317 switch (iop
->dxfer_dir
) {
3319 srb
.h
.flags
= ASPI_REQFLAG_DIR_NO_XFER
;
3321 case DXFER_FROM_DEVICE
:
3322 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_HOST
;
3323 srb
.i
.data_size
= iop
->dxfer_len
;
3324 srb
.i
.data_addr
= iop
->dxferp
;
3326 case DXFER_TO_DEVICE
:
3327 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_TARGET
;
3328 srb
.i
.data_size
= iop
->dxfer_len
;
3329 srb
.i
.data_addr
= iop
->dxferp
;
3332 set_err(EINVAL
, "bad dxfer_dir");
3336 iop
->resp_sense_len
= 0;
3337 iop
->scsi_status
= 0;
3340 if (aspi_io_call(&srb
, (iop
->timeout
? iop
->timeout
: 60))) {
3342 set_err(EIO
, "ASPI Timeout"); return false;
3345 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3346 if ( srb
.h
.status
== ASPI_STATUS_ERROR
3347 && srb
.i
.host_status
== ASPI_HSTATUS_NO_ERROR
3348 && srb
.i
.target_status
== ASPI_TSTATUS_CHECK_CONDITION
) {
3350 const unsigned char * sense
= ASPI_SRB_SENSE(&srb
.i
, iop
->cmnd_len
);
3351 int len
= (ASPI_SENSE_SIZE
< iop
->max_sense_len
? ASPI_SENSE_SIZE
: iop
->max_sense_len
);
3352 iop
->scsi_status
= SCSI_STATUS_CHECK_CONDITION
;
3353 if (len
> 0 && iop
->sensep
) {
3354 memcpy(iop
->sensep
, sense
, len
);
3355 iop
->resp_sense_len
= len
;
3357 pout(" >>> Sense buffer, len=%d:\n", (int)len
);
3358 dStrHex(iop
->sensep
, len
, 1);
3362 pout(" sense_key=%x asc=%x ascq=%x\n",
3363 sense
[2] & 0xf, sense
[12], sense
[13]);
3369 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb
.h
.status
, srb
.i
.host_status
, srb
.i
.target_status
);
3378 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
&& report
> 1) {
3379 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3380 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
3381 (trunc
? " [only first 256 bytes shown]" : ""));
3382 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3388 #endif // WIN9X_SUPPORT
3390 /////////////////////////////////////////////////////////////////////////////
3391 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
3392 // Only supported in NT and later
3393 /////////////////////////////////////////////////////////////////////////////
3395 win_scsi_device::win_scsi_device(smart_interface
* intf
,
3396 const char * dev_name
, const char * req_type
)
3397 : smart_device(intf
, dev_name
, "scsi", req_type
)
3401 bool win_scsi_device::open()
3403 const char * name
= skipdev(get_dev_name()); int len
= strlen(name
);
3404 // sd[a-z],N => Physical drive 0-26, RAID port N
3405 char drive
[1+1] = ""; int sub_addr
= -1; int n1
= -1; int n2
= -1;
3406 if ( sscanf(name
, "sd%1[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
3407 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
3408 return open(drive
[0] - 'a', -1, -1, sub_addr
);
3410 // pd<m>,N => Physical drive <m>, RAID port N
3411 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
3412 if ( sscanf(name
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
3413 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
3414 return open(pd_num
, -1, -1, sub_addr
);
3416 // [a-zA-Z]: => Physical drive behind logical drive 0-25
3417 int logdrive
= drive_letter(name
);
3418 if (logdrive
>= 0) {
3419 return open(-1, logdrive
, -1, -1);
3421 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
3422 int tape_num
= -1; n1
= -1;
3423 if (sscanf(name
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3424 return open(-1, -1, tape_num
, -1);
3426 tape_num
= -1; n1
= -1;
3427 if (sscanf(name
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3428 return open(-1, -1, tape_num
, -1);
3430 // tape<m> => tape drive <m>
3431 tape_num
= -1; n1
= -1;
3432 if (sscanf(name
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
3433 return open(-1, -1, tape_num
, -1);
3436 return set_err(EINVAL
);
3439 bool win_scsi_device::open(int pd_num
, int ld_num
, int tape_num
, int /*sub_addr*/)
3442 b
[sizeof(b
) - 1] = '\0';
3444 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
3445 else if (ld_num
>= 0)
3446 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
3447 else if (tape_num
>= 0)
3448 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
3455 HANDLE h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
3456 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
3457 OPEN_EXISTING
, 0, 0);
3458 if (h
== INVALID_HANDLE_VALUE
) {
3459 set_err(ENODEV
, "%s: Open failed, Error=%ld", b
, GetLastError());
3467 bool winnt_smart_interface::scsi_scan(smart_device_list
& devlist
)
3470 for (int i
= 0; i
<= 9; i
++) {
3471 if (get_phy_drive_type(i
) != DEV_SCSI
)
3473 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
3474 sprintf(name
, "/dev/sd%c", 'a'+i
);
3475 devlist
.push_back( new win_scsi_device(this, name
, "scsi") );
3481 #define IOCTL_SCSI_PASS_THROUGH_DIRECT \
3482 CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
3484 typedef struct _SCSI_PASS_THROUGH_DIRECT
{
3491 UCHAR SenseInfoLength
;
3493 ULONG DataTransferLength
;
3496 ULONG SenseInfoOffset
;
3498 } SCSI_PASS_THROUGH_DIRECT
;
3501 SCSI_PASS_THROUGH_DIRECT spt
;
3503 UCHAR ucSenseBuf
[64];
3504 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
3507 // Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
3508 // Used if DataTransferLength not supported by *_DIRECT.
3509 static long scsi_pass_through_indirect(HANDLE h
,
3510 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
* sbd
)
3512 struct SCSI_PASS_THROUGH_WITH_BUFFERS
{
3513 SCSI_PASS_THROUGH spt
;
3515 UCHAR ucSenseBuf
[sizeof(sbd
->ucSenseBuf
)];
3516 UCHAR ucDataBuf
[512];
3519 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
3520 memset(&sb
, 0, sizeof(sb
));
3522 // DATA_OUT not implemented yet
3523 if (!( sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
3524 && sbd
->spt
.DataTransferLength
<= sizeof(sb
.ucDataBuf
)))
3525 return ERROR_INVALID_PARAMETER
;
3527 sb
.spt
.Length
= sizeof(sb
.spt
);
3528 sb
.spt
.CdbLength
= sbd
->spt
.CdbLength
;
3529 memcpy(sb
.spt
.Cdb
, sbd
->spt
.Cdb
, sizeof(sb
.spt
.Cdb
));
3530 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3531 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
3532 sb
.spt
.DataIn
= sbd
->spt
.DataIn
;
3533 sb
.spt
.DataTransferLength
= sbd
->spt
.DataTransferLength
;
3534 sb
.spt
.DataBufferOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
3535 sb
.spt
.TimeOutValue
= sbd
->spt
.TimeOutValue
;
3538 if (!DeviceIoControl(h
, IOCTL_SCSI_PASS_THROUGH
,
3539 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3540 return GetLastError();
3542 sbd
->spt
.ScsiStatus
= sb
.spt
.ScsiStatus
;
3543 if (sb
.spt
.ScsiStatus
& SCSI_STATUS_CHECK_CONDITION
)
3544 memcpy(sbd
->ucSenseBuf
, sb
.ucSenseBuf
, sizeof(sbd
->ucSenseBuf
));
3546 sbd
->spt
.DataTransferLength
= sb
.spt
.DataTransferLength
;
3547 if (sbd
->spt
.DataIn
== SCSI_IOCTL_DATA_IN
&& sb
.spt
.DataTransferLength
> 0)
3548 memcpy(sbd
->spt
.DataBuffer
, sb
.ucDataBuf
, sb
.spt
.DataTransferLength
);
3553 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
3554 bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io
* iop
)
3556 int report
= con
->reportscsiioctl
; // TODO
3560 const unsigned char * ucp
= iop
->cmnd
;
3563 const int sz
= (int)sizeof(buff
);
3565 np
= scsi_get_opcode_name(ucp
[0]);
3566 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3567 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3568 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3570 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3571 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3573 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3574 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3575 (trunc
? " [only first 256 bytes shown]" : ""));
3576 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3579 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3583 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
3584 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
3585 set_err(EINVAL
, "cmnd_len too large");
3589 memset(&sb
, 0, sizeof(sb
));
3590 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
3591 sb
.spt
.CdbLength
= iop
->cmnd_len
;
3592 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
3593 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3594 sb
.spt
.SenseInfoOffset
=
3595 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
3596 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
3599 switch (iop
->dxfer_dir
) {
3601 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
3603 case DXFER_FROM_DEVICE
:
3604 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
3605 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3606 sb
.spt
.DataBuffer
= iop
->dxferp
;
3607 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
3608 // transfers (needed for SMART STATUS check of JMicron USB bridges)
3609 if (sb
.spt
.DataTransferLength
== 1)
3612 case DXFER_TO_DEVICE
:
3613 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
3614 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3615 sb
.spt
.DataBuffer
= iop
->dxferp
;
3618 set_err(EINVAL
, "bad dxfer_dir");
3625 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT
,
3626 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, 0))
3627 err
= GetLastError();
3630 err
= scsi_pass_through_indirect(get_fh(), &sb
);
3633 return set_err((err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
),
3634 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
3635 (direct
? "_DIRECT" : ""), err
);
3637 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
3638 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3639 int slen
= sb
.ucSenseBuf
[7] + 8;
3641 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
3642 slen
= sizeof(sb
.ucSenseBuf
);
3643 if (slen
> (int)iop
->max_sense_len
)
3644 slen
= iop
->max_sense_len
;
3645 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3646 iop
->resp_sense_len
= slen
;
3648 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3649 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3650 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3651 iop
->sensep
[2], iop
->sensep
[3]);
3653 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3654 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3655 iop
->sensep
[12], iop
->sensep
[13]);
3658 iop
->resp_sense_len
= 0;
3660 if ((iop
->dxfer_len
> 0) && (sb
.spt
.DataTransferLength
> 0))
3661 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3665 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3666 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3667 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
3668 (trunc
? " [only first 256 bytes shown]" : ""));
3669 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3675 //////////////////////////////////////////////////////////////////////////////////////////////////
3680 /////////////////////////////////////////////////////////////////////////////
3682 // Initialize platform interface and register with smi()
3683 void smart_interface::init()
3685 // Select interface for Windows flavor
3686 if (GetVersion() & 0x80000000) {
3688 static os_win32::win9x_smart_interface the_win9x_interface
;
3689 smart_interface::set(&the_win9x_interface
);
3691 throw std::runtime_error("Win9x/ME not supported");
3695 static os_win32::winnt_smart_interface the_winnt_interface
;
3696 smart_interface::set(&the_winnt_interface
);