4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2004-8 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, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 extern smartmonctrl
* con
; // con->permissive,reportataioctl
26 extern int64_t bytes
; // malloc() byte count
32 #define assert(x) /**/
34 #define WIN32_LEAN_AND_MEAN
36 #include <stddef.h> // offsetof()
37 #include <io.h> // access()
39 // Macro to check constants at compile time using a dummy typedef
40 #define ASSERT_CONST(c, n) \
41 typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
42 #define ASSERT_SIZEOF(t, n) \
43 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
46 // Needed by '-V' option (CVS versioning) of smartd/smartctl
47 const char *os_XXXX_c_cvsid
="$Id: os_win32.cpp,v 1.61 2008/03/04 22:09:47 ballen4705 Exp $"
48 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID
;
51 // Running on Win9x/ME ?
52 static inline bool is_win9x()
54 return !!(GetVersion() & 0x80000000);
57 // Running on 64-bit Windows as 32-bit app ?
58 static bool is_wow64()
60 HMODULE hk
= GetModuleHandleA("kernel32");
63 BOOL (WINAPI
* IsWow64Process_p
)(HANDLE
, PBOOL
) =
64 (BOOL (WINAPI
*)(HANDLE
, PBOOL
))GetProcAddress(hk
, "IsWow64Process");
65 if (!IsWow64Process_p
)
68 if (!IsWow64Process_p(GetCurrentProcess(), &w64
))
74 #ifndef HAVE_GET_OS_VERSION_STR
75 #error define of HAVE_GET_OS_VERSION_STR missing in config.h
78 // Return build host and OS version as static string
79 const char * get_os_version_str()
81 static char vstr
[sizeof(SMARTMONTOOLS_BUILD_HOST
)-3-1+sizeof("-2003r2(64)-sp2.1")+13];
82 char * const vptr
= vstr
+sizeof(SMARTMONTOOLS_BUILD_HOST
)-3-1;
83 const int vlen
= sizeof(vstr
)-(sizeof(SMARTMONTOOLS_BUILD_HOST
)-3);
85 // remove "-pc" to avoid long lines
86 assert(!strncmp(SMARTMONTOOLS_BUILD_HOST
+5, "pc-", 3));
87 strcpy(vstr
, "i686-"); strcpy(vstr
+5, SMARTMONTOOLS_BUILD_HOST
+5+3);
88 assert(vptr
== vstr
+strlen(vstr
) && vptr
+vlen
+1 == vstr
+sizeof(vstr
));
90 OSVERSIONINFOEXA vi
; memset(&vi
, 0, sizeof(vi
));
91 vi
.dwOSVersionInfoSize
= sizeof(vi
);
92 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
)) {
93 memset(&vi
, 0, sizeof(vi
));
94 vi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
95 if (!GetVersionExA((OSVERSIONINFOA
*)&vi
))
99 if (vi
.dwPlatformId
> 0xff || vi
.dwMajorVersion
> 0xff || vi
.dwMinorVersion
> 0xff)
103 switch (vi
.dwPlatformId
<< 16 | vi
.dwMajorVersion
<< 8 | vi
.dwMinorVersion
) {
104 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400| 0:
105 w
= (vi
.szCSDVersion
[1] == 'B' ||
106 vi
.szCSDVersion
[1] == 'C' ? "95-osr2" : "95"); break;
107 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|10:
108 w
= (vi
.szCSDVersion
[1] == 'A' ? "98se" : "98"); break;
109 case VER_PLATFORM_WIN32_WINDOWS
<<16|0x0400|90: w
= "me"; break;
110 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
111 case VER_PLATFORM_WIN32_NT
<<16|0x0400| 0: w
= "nt4"; break;
112 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 0: w
= "2000"; break;
113 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 1:
114 w
= (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
116 case VER_PLATFORM_WIN32_NT
<<16|0x0500| 2:
117 w
= (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
119 case VER_PLATFORM_WIN32_NT
<<16|0x0600| 0: w
= "vista"; break;
120 default: w
= 0; break;
123 const char * w64
= (is_wow64() ? "(64)" : "");
125 snprintf(vptr
, vlen
, "-%s%lu.%lu%s",
126 (vi
.dwPlatformId
==VER_PLATFORM_WIN32_NT
? "nt" : "9x"),
127 vi
.dwMajorVersion
, vi
.dwMinorVersion
, w64
);
128 else if (vi
.wServicePackMinor
)
129 snprintf(vptr
, vlen
, "-%s%s-sp%u.%u", w
, w64
, vi
.wServicePackMajor
, vi
.wServicePackMinor
);
130 else if (vi
.wServicePackMajor
)
131 snprintf(vptr
, vlen
, "-%s%s-sp%u", w
, w64
, vi
.wServicePackMajor
);
133 snprintf(vptr
, vlen
, "-%s%s", w
, w64
);
138 static int get_phy_drive_type(int drive
);
139 static int get_log_drive_type(int drive
);
141 #define ATARAID_FDOFFSET 0x0200
143 static int ata_open(int phydrive
, int logdrive
, const char * options
, int port
);
144 static void ata_close(int fd
);
145 static int ata_scan_win9x(unsigned long * drives
);
146 static int ata_scan(unsigned long * drives
, int * rdriveno
, unsigned long * rdrives
);
147 static const char * ata_get_def_options(void);
149 #define TW_CLI_FDOFFSET 0x0300
151 static int tw_cli_open(const char * name
);
152 static void tw_cli_close();
154 #define ASPI_FDOFFSET 0x0100
156 static int aspi_open(unsigned adapter
, unsigned id
);
157 static void aspi_close(int fd
);
158 static int aspi_scan(unsigned long * drives
);
160 #define SPT_FDOFFSET 0x0400
162 static int spt_open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
);
163 static void spt_close(int fd
);
164 static int spt_scan(unsigned long * drives
);
167 static int is_permissive()
169 if (!con
->permissive
) {
170 pout("To continue, add one or more '-T permissive' options.\n");
177 // return number for drive letter, -1 on error
178 // "[A-Za-z]:([/\\][.]?)?" => 0-25
179 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
180 static int drive_letter(const char * s
)
182 return ( (('A' <= s
[0] && s
[0] <= 'Z') || ('a' <= s
[0] && s
[0] <= 'z'))
184 && (!s
[2] || ( strchr("/\\\"", s
[2])
185 && (!s
[3] || (s
[3] == '.' && !s
[4]))) ) ?
186 (s
[0] & 0x1f) - 1 : -1);
189 // Skip trailing "/dev/", do not allow "/dev/X:"
190 static const char * skipdev(const char * s
)
192 return (!strncmp(s
, "/dev/", 5) && drive_letter(s
+5) < 0 ? s
+5 : s
);
196 // tries to guess device type given the name (a path). See utility.h
197 // for return values.
198 int guess_device_type (const char * dev_name
)
200 dev_name
= skipdev(dev_name
);
201 if (!strncmp(dev_name
, "scsi", 4))
202 return CONTROLLER_SCSI
;
204 return CONTROLLER_ATA
;
205 if (!strncmp(dev_name
, "hd", 2))
206 return CONTROLLER_ATA
;
207 if (!strncmp(dev_name
, "tw_cli", 6))
208 return CONTROLLER_ATA
;
209 if (!strncmp(dev_name
, "st", 2))
210 return CONTROLLER_SCSI
;
211 if (!strncmp(dev_name
, "nst", 3))
212 return CONTROLLER_SCSI
;
213 if (!strncmp(dev_name
, "tape", 4))
214 return CONTROLLER_SCSI
;
215 int logdrive
= drive_letter(dev_name
);
217 int type
= get_log_drive_type(logdrive
);
218 return (type
!= CONTROLLER_UNKNOWN
? type
: CONTROLLER_SCSI
);
220 char drive
[1+1] = "";
221 if (sscanf(dev_name
, "sd%1[a-z]", drive
) == 1)
222 return get_phy_drive_type(drive
[0]-'a');
224 if (sscanf(dev_name
, "pd%d", &phydrive
) == 1 && phydrive
>= 0)
225 return get_phy_drive_type(phydrive
);
226 return CONTROLLER_UNKNOWN
;
230 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
231 // smartd. Returns number N of devices, or -1 if out of
232 // memory. Allocates N+1 arrays: one of N pointers (devlist), the
233 // others each contain null-terminated character strings.
234 int make_device_names (char*** devlist
, const char* type
)
236 unsigned long drives
[3];
238 unsigned long rdrives
[2];
239 int i
, j
, n
, nmax
, sz
;
242 drives
[0] = drives
[1] = drives
[2] = 0;
243 rdriveno
[0] = rdriveno
[1] = -1;
244 rdrives
[0] = rdrives
[1] = 0;
246 bool win9x
= is_win9x();
247 if (!strcmp(type
, "ATA")) {
248 // bit i set => drive i present
250 n
= ata_scan_win9x(drives
);
254 n
= ata_scan(drives
, rdriveno
, rdrives
);
259 else if (!strcmp(type
, "SCSI")) {
261 // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
262 n
= aspi_scan(drives
);
263 path
= "/dev/scsi00";
267 // bit i set => drive i present
268 n
= spt_scan(drives
);
280 sz
= n
* sizeof(char **);
281 *devlist
= (char **)malloc(sz
); bytes
+= sz
;
284 for (i
= j
= 0; i
< n
; ) {
285 while (j
< nmax
&& !(drives
[j
>> 5] & (1L << (j
& 0x1f))))
289 if (j
== rdriveno
[0] || j
== rdriveno
[1]) {
290 // Add physical drives behind this logical drive
291 int ci
= (j
== rdriveno
[0] ? 0 : 1);
292 for (int pi
= 0; pi
< 32 && i
< n
; pi
++) {
293 if (!(rdrives
[ci
] & (1L << pi
)))
296 sprintf(rpath
, "/dev/sd%c,%u", 'a'+j
, pi
);
297 sz
= strlen(rpath
)+1;
298 char * s
= (char *)malloc(sz
); bytes
+= sz
;
305 char * s
= (char *)malloc(sz
); bytes
+= sz
;
310 s
[sz
-2] += j
; // /dev/hd[a-j]
313 assert((j
>> 3) <= 9);
314 s
[sz
-3] += (j
>> 3); // /dev/scsi[0-9].....
315 s
[sz
-2] += (j
& 0x7); // .....[0-7]
326 // Like open(). Return positive integer handle, only used by
327 // functions below. type="ATA" or "SCSI". If you need to store extra
328 // information about your devices, create a private internal array
329 // within this file (see os_freebsd.cpp for an example).
330 int deviceopen(const char * pathname
, char *type
)
332 pathname
= skipdev(pathname
);
333 int len
= strlen(pathname
);
335 if (!strcmp(type
, "ATA")) {
336 // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
337 char drive
[1+1] = "", options
[8+1] = ""; int n1
= -1, n2
= -1;
338 if ( sscanf(pathname
, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive
, &n1
, options
, &n2
) >= 1
339 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
340 return ata_open(drive
[0] - 'a', -1, options
, -1);
342 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
343 drive
[0] = 0; options
[0] = 0; n1
= -1; n2
= -1;
345 if ( sscanf(pathname
, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive
, &port
, &n1
, options
, &n2
) >= 2
346 && port
< 32 && ((n1
== len
&& !options
[0]) || n2
== len
) ) {
347 return ata_open(drive
[0] - 'a', -1, options
, port
);
349 // pd<m>,N => Physical drive <m>, RAID port N
350 int phydrive
= -1; port
= ~0; n1
= -1; n2
= -1;
351 if ( sscanf(pathname
, "pd%d%n,%u%n", &phydrive
, &n1
, &port
, &n2
) >= 1
352 && phydrive
>= 0 && ((n1
== len
&& (int)port
< 0) || (n2
== len
&& port
< 32))) {
353 return ata_open(phydrive
, -1, "", (int)port
);
355 // [a-zA-Z]: => Physical drive behind logical drive 0-25
356 int logdrive
= drive_letter(pathname
);
358 return ata_open(-1, logdrive
, "", -1);
360 // tw_cli/... => Parse tw_cli output
361 if (!strncmp(pathname
, "tw_cli/", 7)) {
362 return tw_cli_open(pathname
+7);
364 } else if (!strcmp(type
, "SCSI")) {
365 // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
366 unsigned adapter
= ~0, id
= ~0; int n1
= -1;
367 if (sscanf(pathname
,"scsi%1u%1x%n", &adapter
, &id
, &n1
) == 2 && n1
== len
) {
368 return aspi_open(adapter
, id
);
370 // sd[a-z],N => Physical drive 0-25, RAID port N
371 char drive
[1+1] = ""; int sub_addr
= -1; n1
= -1; int n2
= -1;
372 if ( sscanf(pathname
, "sd%1[a-z]%n,%d%n", drive
, &n1
, &sub_addr
, &n2
) >= 1
373 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0)) ) {
374 return spt_open(drive
[0] - 'a', -1, -1, sub_addr
);
376 // pd<m>,N => Physical drive <m>, RAID port N
377 int pd_num
= -1; sub_addr
= -1; n1
= -1; n2
= -1;
378 if ( sscanf(pathname
, "pd%d%n,%d%n", &pd_num
, &n1
, &sub_addr
, &n2
) >= 1
379 && pd_num
>= 0 && ((n1
== len
&& sub_addr
== -1) || (n2
== len
&& sub_addr
>= 0))) {
380 return spt_open(pd_num
, -1, -1, sub_addr
);
382 // [a-zA-Z]: => Physical drive behind logical drive 0-25
383 int logdrive
= drive_letter(pathname
);
385 return spt_open(-1, logdrive
, -1, -1);
387 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
388 int tape_num
= -1; n1
= -1;
389 if (sscanf(pathname
, "st%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
390 return spt_open(-1, -1, tape_num
, -1);
392 tape_num
= -1; n1
= -1;
393 if (sscanf(pathname
, "nst%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
394 return spt_open(-1, -1, tape_num
, -1);
396 // tape<m> => tape drive <m>
397 tape_num
= -1; n1
= -1;
398 if (sscanf(pathname
, "tape%d%n", &tape_num
, &n1
) == 1 && tape_num
>= 0 && n1
== len
) {
399 return spt_open(-1, -1, tape_num
, -1);
408 // Like close(). Acts only on handles returned by above function.
409 int deviceclose(int fd
)
411 if ((fd
& 0xff00) == ASPI_FDOFFSET
)
413 else if (fd
>= SPT_FDOFFSET
)
415 else if (fd
== TW_CLI_FDOFFSET
)
423 // print examples for smartctl
424 void print_smartctl_examples(){
425 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
426 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
427 #ifdef HAVE_GETOPT_LONG
428 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
429 " (Enables SMART on first disk)\n\n"
430 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
431 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
432 " (Prints Self-Test & Attribute errors)\n"
434 " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n"
435 " smartctl -t long /dev/hda (Executes extended disk self-test)\n"
436 " smartctl -A -l selftest -q errorsonly /dev/hda\n"
437 " (Prints Self-Test & Attribute errors)\n"
439 " smartctl -a /dev/scsi21\n"
440 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
441 " smartctl -a /dev/sda\n"
442 " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
443 " smartctl -a /dev/pd3\n"
444 " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
445 " smartctl -a /dev/tape1\n"
446 " (Prints all information for SCSI tape on Tape 1)\n"
447 " smartctl -A /dev/hdb,3\n"
448 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
449 " smartctl -A /dev/tw_cli/c0/p1\n"
450 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
452 " ATA SMART access methods and ordering may be specified by modifiers\n"
453 " following the device name: /dev/hdX:[saicm], where\n"
454 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
455 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
456 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
457 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
462 /////////////////////////////////////////////////////////////////////////////
464 /////////////////////////////////////////////////////////////////////////////
466 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
468 #define FILE_READ_ACCESS 0x0001
469 #define FILE_WRITE_ACCESS 0x0002
470 #define METHOD_BUFFERED 0
471 #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
473 #define FILE_DEVICE_DISK 7
474 #define IOCTL_DISK_BASE FILE_DEVICE_DISK
476 #define SMART_GET_VERSION \
477 CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
479 #define SMART_SEND_DRIVE_COMMAND \
480 CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
482 #define SMART_RCV_DRIVE_DATA \
483 CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
485 ASSERT_CONST(SMART_GET_VERSION
, 0x074080);
486 ASSERT_CONST(SMART_SEND_DRIVE_COMMAND
, 0x07c084);
487 ASSERT_CONST(SMART_RCV_DRIVE_DATA
, 0x07c088);
489 #define SMART_CYL_LOW 0x4F
490 #define SMART_CYL_HI 0xC2
495 typedef struct _GETVERSIONOUTPARAMS
{
502 } GETVERSIONOUTPARAMS
, *PGETVERSIONOUTPARAMS
, *LPGETVERSIONOUTPARAMS
;
504 ASSERT_SIZEOF(GETVERSIONOUTPARAMS
, 24);
507 #define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
509 typedef struct _GETVERSIONINPARAMS_EX
{
515 DWORD dwDeviceMapEx
; // 3ware specific: RAID drive bit map
516 WORD wIdentifier
; // Vendor specific identifier
517 WORD wControllerId
; // 3ware specific: Controller ID (0,1,...)
519 } GETVERSIONINPARAMS_EX
, *PGETVERSIONINPARAMS_EX
, *LPGETVERSIONINPARAMS_EX
;
521 ASSERT_SIZEOF(GETVERSIONINPARAMS_EX
, sizeof(GETVERSIONOUTPARAMS
));
524 typedef struct _IDEREGS
{
526 UCHAR bSectorCountReg
;
527 UCHAR bSectorNumberReg
;
533 } IDEREGS
, *PIDEREGS
, *LPIDEREGS
;
535 typedef struct _SENDCMDINPARAMS
{
542 } SENDCMDINPARAMS
, *PSENDCMDINPARAMS
, *LPSENDCMDINPARAMS
;
544 ASSERT_SIZEOF(SENDCMDINPARAMS
, 32+1);
546 typedef struct _SENDCMDINPARAMS_EX
{
550 BYTE bPortNumber
; // 3ware specific: port number
551 WORD wIdentifier
; // Vendor specific identifier
554 } SENDCMDINPARAMS_EX
, *PSENDCMDINPARAMS_EX
, *LPSENDCMDINPARAMS_EX
;
556 ASSERT_SIZEOF(SENDCMDINPARAMS_EX
, sizeof(SENDCMDINPARAMS
));
559 /* DRIVERSTATUS.bDriverError constants (just for info, not used)
560 #define SMART_NO_ERROR 0
561 #define SMART_IDE_ERROR 1
562 #define SMART_INVALID_FLAG 2
563 #define SMART_INVALID_COMMAND 3
564 #define SMART_INVALID_BUFFER 4
565 #define SMART_INVALID_DRIVE 5
566 #define SMART_INVALID_IOCTL 6
567 #define SMART_ERROR_NO_MEM 7
568 #define SMART_INVALID_REGISTER 8
569 #define SMART_NOT_SUPPORTED 9
570 #define SMART_NO_IDE_DEVICE 10
573 typedef struct _DRIVERSTATUS
{
578 } DRIVERSTATUS
, *PDRIVERSTATUS
, *LPDRIVERSTATUS
;
580 typedef struct _SENDCMDOUTPARAMS
{
582 DRIVERSTATUS DriverStatus
;
584 } SENDCMDOUTPARAMS
, *PSENDCMDOUTPARAMS
, *LPSENDCMDOUTPARAMS
;
586 ASSERT_SIZEOF(SENDCMDOUTPARAMS
, 16+1);
591 /////////////////////////////////////////////////////////////////////////////
593 static void print_ide_regs(const IDEREGS
* r
, int out
)
595 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
596 (out
?"STS":"CMD"), r
->bCommandReg
, (out
?"ERR":" FR"), r
->bFeaturesReg
,
597 r
->bSectorCountReg
, r
->bSectorNumberReg
, r
->bCylLowReg
, r
->bCylHighReg
, r
->bDriveHeadReg
);
600 static void print_ide_regs_io(const IDEREGS
* ri
, const IDEREGS
* ro
)
602 pout(" Input : "); print_ide_regs(ri
, 0);
604 pout(" Output: "); print_ide_regs(ro
, 1);
608 /////////////////////////////////////////////////////////////////////////////
610 // call SMART_GET_VERSION, return device map or -1 on error
612 static int smart_get_version(HANDLE hdevice
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
614 GETVERSIONOUTPARAMS vers
; memset(&vers
, 0, sizeof(vers
));
615 const GETVERSIONINPARAMS_EX
& vers_ex
= (const GETVERSIONINPARAMS_EX
&)vers
;
618 if (!DeviceIoControl(hdevice
, SMART_GET_VERSION
,
619 NULL
, 0, &vers
, sizeof(vers
), &num_out
, NULL
)) {
620 if (con
->reportataioctl
)
621 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
625 assert(num_out
== sizeof(GETVERSIONOUTPARAMS
));
627 if (con
->reportataioctl
> 1) {
628 pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
629 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
630 num_out
, vers
.bVersion
, vers
.bRevision
,
631 vers
.fCapabilities
, vers
.bIDEDeviceMap
);
632 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
633 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
634 vers_ex
.wIdentifier
, vers_ex
.wControllerId
, vers_ex
.dwDeviceMapEx
);
638 *ata_version_ex
= vers_ex
;
640 // TODO: Check vers.fCapabilities here?
641 return vers
.bIDEDeviceMap
;
645 // call SMART_* ioctl
647 static int smart_ioctl(HANDLE hdevice
, int drive
, IDEREGS
* regs
, char * data
, unsigned datasize
, int port
)
649 SENDCMDINPARAMS inpar
;
650 SENDCMDINPARAMS_EX
& inpar_ex
= (SENDCMDINPARAMS_EX
&)inpar
;
652 unsigned char outbuf
[sizeof(SENDCMDOUTPARAMS
)-1 + 512];
653 const SENDCMDOUTPARAMS
* outpar
;
655 unsigned int size_out
;
658 memset(&inpar
, 0, sizeof(inpar
));
659 inpar
.irDriveRegs
= *regs
;
660 // drive is set to 0-3 on Win9x only
661 inpar
.irDriveRegs
.bDriveHeadReg
= 0xA0 | ((drive
& 1) << 4);
662 inpar
.bDriveNumber
= drive
;
666 inpar_ex
.wIdentifier
= SMART_VENDOR_3WARE
;
667 inpar_ex
.bPortNumber
= port
;
670 assert(datasize
== 0 || datasize
== 512);
672 code
= SMART_RCV_DRIVE_DATA
; name
= "SMART_RCV_DRIVE_DATA";
673 inpar
.cBufferSize
= size_out
= 512;
676 code
= SMART_SEND_DRIVE_COMMAND
; name
= "SMART_SEND_DRIVE_COMMAND";
677 if (regs
->bFeaturesReg
== ATA_SMART_STATUS
)
678 size_out
= sizeof(IDEREGS
); // ioctl returns new IDEREGS as data
679 // Note: cBufferSize must be 0 on Win9x
684 memset(&outbuf
, 0, sizeof(outbuf
));
686 if (!DeviceIoControl(hdevice
, code
, &inpar
, sizeof(SENDCMDINPARAMS
)-1,
687 outbuf
, sizeof(SENDCMDOUTPARAMS
)-1 + size_out
, &num_out
, NULL
)) {
688 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
689 long err
= GetLastError();
690 if (con
->reportataioctl
&& (err
!= ERROR_INVALID_PARAMETER
|| con
->reportataioctl
> 1)) {
691 pout(" %s failed, Error=%ld\n", name
, err
);
692 print_ide_regs_io(regs
, NULL
);
694 errno
= ( err
== ERROR_INVALID_FUNCTION
/*9x*/
695 || err
== ERROR_INVALID_PARAMETER
/*NT/2K/XP*/
696 || err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
699 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
701 outpar
= (const SENDCMDOUTPARAMS
*)outbuf
;
703 if (outpar
->DriverStatus
.bDriverError
) {
704 if (con
->reportataioctl
) {
705 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
706 outpar
->DriverStatus
.bDriverError
, outpar
->DriverStatus
.bIDEError
);
707 print_ide_regs_io(regs
, NULL
);
709 errno
= (!outpar
->DriverStatus
.bIDEError
? ENOSYS
: EIO
);
713 if (con
->reportataioctl
> 1) {
714 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
715 num_out
, outpar
->cBufferSize
);
716 print_ide_regs_io(regs
, (regs
->bFeaturesReg
== ATA_SMART_STATUS
?
717 (const IDEREGS
*)(outpar
->bBuffer
) : NULL
));
721 memcpy(data
, outpar
->bBuffer
, 512);
722 else if (regs
->bFeaturesReg
== ATA_SMART_STATUS
) {
723 if (nonempty(const_cast<unsigned char *>(outpar
->bBuffer
), sizeof(IDEREGS
)))
724 *regs
= *(const IDEREGS
*)(outpar
->bBuffer
);
725 else { // Workaround for driver not returning regs
726 if (con
->reportataioctl
)
727 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
728 *regs
= inpar
.irDriveRegs
;
736 /////////////////////////////////////////////////////////////////////////////
738 // IDE PASS THROUGH (2000, XP, undocumented)
740 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
741 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
743 #define FILE_DEVICE_CONTROLLER 4
744 #define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
746 #define IOCTL_IDE_PASS_THROUGH \
747 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
749 ASSERT_CONST(IOCTL_IDE_PASS_THROUGH
, 0x04d028);
755 ULONG DataBufferSize
;
759 ASSERT_SIZEOF(ATA_PASS_THROUGH
, 12+1);
764 /////////////////////////////////////////////////////////////////////////////
766 static int ide_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
768 if (datasize
> 512) {
772 unsigned int size
= sizeof(ATA_PASS_THROUGH
)-1 + datasize
;
773 ATA_PASS_THROUGH
* buf
= (ATA_PASS_THROUGH
*)VirtualAlloc(NULL
, size
, MEM_COMMIT
, PAGE_READWRITE
);
775 const unsigned char magic
= 0xcf;
783 buf
->DataBufferSize
= datasize
;
785 buf
->DataBuffer
[0] = magic
;
787 if (!DeviceIoControl(hdevice
, IOCTL_IDE_PASS_THROUGH
,
788 buf
, size
, buf
, size
, &num_out
, NULL
)) {
789 long err
= GetLastError();
790 if (con
->reportataioctl
) {
791 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err
);
792 print_ide_regs_io(regs
, NULL
);
794 VirtualFree(buf
, 0, MEM_RELEASE
);
795 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
800 if (buf
->IdeReg
.bCommandReg
/*Status*/ & 0x01) {
801 if (con
->reportataioctl
) {
802 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
803 print_ide_regs_io(regs
, &buf
->IdeReg
);
805 VirtualFree(buf
, 0, MEM_RELEASE
);
810 // Check and copy data
813 || (buf
->DataBuffer
[0] == magic
&& !nonempty(buf
->DataBuffer
+1, datasize
-1))) {
814 if (con
->reportataioctl
) {
815 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
816 num_out
, buf
->DataBufferSize
);
817 print_ide_regs_io(regs
, &buf
->IdeReg
);
819 VirtualFree(buf
, 0, MEM_RELEASE
);
823 memcpy(data
, buf
->DataBuffer
, datasize
);
826 if (con
->reportataioctl
> 1) {
827 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
828 num_out
, buf
->DataBufferSize
);
829 print_ide_regs_io(regs
, &buf
->IdeReg
);
833 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
834 VirtualFree(buf
, 0, MEM_RELEASE
);
839 /////////////////////////////////////////////////////////////////////////////
841 // ATA PASS THROUGH (Win2003, XP SP2)
843 #define IOCTL_ATA_PASS_THROUGH \
844 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
846 ASSERT_CONST(IOCTL_ATA_PASS_THROUGH
, 0x04d02c);
848 typedef struct _ATA_PASS_THROUGH_EX
{
854 UCHAR ReservedAsUchar
;
855 ULONG DataTransferLength
;
857 ULONG ReservedAsUlong
;
858 ULONG
/*_PTR*/ DataBufferOffset
;
859 UCHAR PreviousTaskFile
[8];
860 UCHAR CurrentTaskFile
[8];
861 } ATA_PASS_THROUGH_EX
, *PATA_PASS_THROUGH_EX
;
863 ASSERT_SIZEOF(ATA_PASS_THROUGH_EX
, 40);
865 #define ATA_FLAGS_DRDY_REQUIRED 0x01
866 #define ATA_FLAGS_DATA_IN 0x02
867 #define ATA_FLAGS_DATA_OUT 0x04
868 #define ATA_FLAGS_48BIT_COMMAND 0x08
871 /////////////////////////////////////////////////////////////////////////////
873 static int ata_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
876 ATA_PASS_THROUGH_EX apt
;
878 UCHAR ucDataBuf
[512];
879 } ATA_PASS_THROUGH_EX_WITH_BUFFERS
;
881 const unsigned char magic
= 0xcf;
883 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab
; memset(&ab
, 0, sizeof(ab
));
884 ab
.apt
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
886 //ab.apt.TargetId = 0;
888 ab
.apt
.TimeOutValue
= 10;
889 unsigned size
= offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS
, ucDataBuf
);
890 ab
.apt
.DataBufferOffset
= size
;
893 if (datasize
> (int)sizeof(ab
.ucDataBuf
)) {
897 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_IN
;
898 ab
.apt
.DataTransferLength
= datasize
;
900 ab
.ucDataBuf
[0] = magic
;
902 else if (datasize
< 0) {
903 if (-datasize
> (int)sizeof(ab
.ucDataBuf
)) {
907 ab
.apt
.AtaFlags
= ATA_FLAGS_DATA_OUT
;
908 ab
.apt
.DataTransferLength
= -datasize
;
910 memcpy(ab
.ucDataBuf
, data
, -datasize
);
913 assert(ab
.apt
.AtaFlags
== 0);
914 assert(ab
.apt
.DataTransferLength
== 0);
917 assert(sizeof(ab
.apt
.CurrentTaskFile
) == sizeof(IDEREGS
));
918 IDEREGS
* ctfregs
= (IDEREGS
*)ab
.apt
.CurrentTaskFile
;
922 if (!DeviceIoControl(hdevice
, IOCTL_ATA_PASS_THROUGH
,
923 &ab
, size
, &ab
, size
, &num_out
, NULL
)) {
924 long err
= GetLastError();
925 if (con
->reportataioctl
) {
926 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err
);
927 print_ide_regs_io(regs
, NULL
);
929 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
934 if (ctfregs
->bCommandReg
/*Status*/ & 0x01) {
935 if (con
->reportataioctl
) {
936 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
937 print_ide_regs_io(regs
, ctfregs
);
943 // Check and copy data
946 || (ab
.ucDataBuf
[0] == magic
&& !nonempty(ab
.ucDataBuf
+1, datasize
-1))) {
947 if (con
->reportataioctl
) {
948 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out
);
949 print_ide_regs_io(regs
, ctfregs
);
954 memcpy(data
, ab
.ucDataBuf
, datasize
);
957 if (con
->reportataioctl
> 1) {
958 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
959 print_ide_regs_io(regs
, ctfregs
);
967 /////////////////////////////////////////////////////////////////////////////
969 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
971 #define IOCTL_SCSI_PASS_THROUGH \
972 CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
974 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH
, 0x04d004);
976 #define SCSI_IOCTL_DATA_OUT 0
977 #define SCSI_IOCTL_DATA_IN 1
978 #define SCSI_IOCTL_DATA_UNSPECIFIED 2
979 // undocumented SCSI opcode to for ATA passthrough
980 #define SCSIOP_ATA_PASSTHROUGH 0xCC
982 typedef struct _SCSI_PASS_THROUGH
{
989 UCHAR SenseInfoLength
;
991 ULONG DataTransferLength
;
993 ULONG
/*_PTR*/ DataBufferOffset
;
994 ULONG SenseInfoOffset
;
996 } SCSI_PASS_THROUGH
, *PSCSI_PASS_THROUGH
;
998 ASSERT_SIZEOF(SCSI_PASS_THROUGH
, 44);
1001 /////////////////////////////////////////////////////////////////////////////
1003 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, unsigned datasize
)
1006 SCSI_PASS_THROUGH spt
;
1008 UCHAR ucSenseBuf
[32];
1009 UCHAR ucDataBuf
[512];
1010 } SCSI_PASS_THROUGH_WITH_BUFFERS
;
1012 SCSI_PASS_THROUGH_WITH_BUFFERS sb
;
1016 const unsigned char magic
= 0xcf;
1018 memset(&sb
, 0, sizeof(sb
));
1019 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH
);
1020 //sb.spt.PathId = 0;
1021 sb
.spt
.TargetId
= 1;
1023 sb
.spt
.CdbLength
= 10; sb
.spt
.SenseInfoLength
= 24;
1024 sb
.spt
.TimeOutValue
= 10;
1025 sb
.spt
.SenseInfoOffset
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucSenseBuf
);
1026 size
= offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS
, ucDataBuf
);
1027 sb
.spt
.DataBufferOffset
= size
;
1030 if (datasize
> sizeof(sb
.ucDataBuf
)) {
1034 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
1035 sb
.spt
.DataTransferLength
= datasize
;
1037 sb
.ucDataBuf
[0] = magic
;
1040 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
1041 //sb.spt.DataTransferLength = 0;
1044 // Use pseudo SCSI command followed by registers
1045 sb
.spt
.Cdb
[0] = SCSIOP_ATA_PASSTHROUGH
;
1046 cdbregs
= (IDEREGS
*)(sb
.spt
.Cdb
+2);
1049 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_PASS_THROUGH
,
1050 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1051 long err
= GetLastError();
1052 if (con
->reportataioctl
)
1053 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err
);
1054 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1058 // Cannot check ATA status, because command does not return IDEREGS
1060 // Check and copy data
1062 if ( num_out
!= size
1063 || (sb
.ucDataBuf
[0] == magic
&& !nonempty(sb
.ucDataBuf
+1, datasize
-1))) {
1064 if (con
->reportataioctl
) {
1065 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out
);
1066 print_ide_regs_io(regs
, NULL
);
1071 memcpy(data
, sb
.ucDataBuf
, datasize
);
1074 if (con
->reportataioctl
> 1) {
1075 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out
);
1076 print_ide_regs_io(regs
, NULL
);
1082 /////////////////////////////////////////////////////////////////////////////
1084 // SMART IOCTL via SCSI MINIPORT ioctl
1086 // This function is handled by ATAPI port driver (atapi.sys) or by SCSI
1087 // miniport driver (via SCSI port driver scsiport.sys).
1088 // It can be used to skip the missing or broken handling of some SMART
1089 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
1091 #define IOCTL_SCSI_MINIPORT \
1092 CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
1094 ASSERT_CONST(IOCTL_SCSI_MINIPORT
, 0x04d008);
1096 typedef struct _SRB_IO_CONTROL
{
1103 } SRB_IO_CONTROL
, *PSRB_IO_CONTROL
;
1105 ASSERT_SIZEOF(SRB_IO_CONTROL
, 28);
1107 #define FILE_DEVICE_SCSI 0x001b
1109 #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
1110 #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
1111 #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
1112 #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
1113 #define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
1114 #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
1115 #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
1116 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
1117 #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
1118 #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
1119 #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
1120 #define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
1121 #define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
1123 /////////////////////////////////////////////////////////////////////////////
1125 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
)
1128 DWORD code
= 0; const char * name
= 0;
1129 if (regs
->bCommandReg
== ATA_IDENTIFY_DEVICE
) {
1130 code
= IOCTL_SCSI_MINIPORT_IDENTIFY
; name
= "IDENTIFY";
1132 else if (regs
->bCommandReg
== ATA_SMART_CMD
) switch (regs
->bFeaturesReg
) {
1133 case ATA_SMART_READ_VALUES
:
1134 code
= IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS
; name
= "READ_SMART_ATTRIBS"; break;
1135 case ATA_SMART_READ_THRESHOLDS
:
1136 code
= IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS
; name
= "READ_SMART_THRESHOLDS"; break;
1137 case ATA_SMART_ENABLE
:
1138 code
= IOCTL_SCSI_MINIPORT_ENABLE_SMART
; name
= "ENABLE_SMART"; break;
1139 case ATA_SMART_DISABLE
:
1140 code
= IOCTL_SCSI_MINIPORT_DISABLE_SMART
; name
= "DISABLE_SMART"; break;
1141 case ATA_SMART_STATUS
:
1142 code
= IOCTL_SCSI_MINIPORT_RETURN_STATUS
; name
= "RETURN_STATUS"; break;
1143 case ATA_SMART_AUTOSAVE
:
1144 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE
; name
= "ENABLE_DISABLE_AUTOSAVE"; break;
1145 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
1146 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
1147 case ATA_SMART_IMMEDIATE_OFFLINE
:
1148 code
= IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS
; name
= "EXECUTE_OFFLINE_DIAGS"; break;
1149 case ATA_SMART_AUTO_OFFLINE
:
1150 code
= IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE
; name
= "ENABLE_DISABLE_AUTO_OFFLINE"; break;
1151 case ATA_SMART_READ_LOG_SECTOR
:
1152 code
= IOCTL_SCSI_MINIPORT_READ_SMART_LOG
; name
= "READ_SMART_LOG"; break;
1153 case ATA_SMART_WRITE_LOG_SECTOR
:
1154 code
= IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG
; name
= "WRITE_SMART_LOG"; break;
1163 SRB_IO_CONTROL srbc
;
1166 SENDCMDOUTPARAMS out
;
1170 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(SENDCMDINPARAMS
)-1+512);
1171 memset(&sb
, 0, sizeof(sb
));
1175 if (datasize
> (int)sizeof(sb
.space
)+1) {
1181 else if (datasize
< 0) {
1182 if (-datasize
> (int)sizeof(sb
.space
)+1) {
1187 memcpy(sb
.params
.in
.bBuffer
, data
, size
);
1189 else if (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1190 size
= sizeof(IDEREGS
);
1193 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1194 memcpy(sb
.srbc
.Signature
, "SCSIDISK", 8); // atapi.sys
1195 sb
.srbc
.Timeout
= 60; // seconds
1196 sb
.srbc
.ControlCode
= code
;
1197 //sb.srbc.ReturnCode = 0;
1198 sb
.srbc
.Length
= sizeof(SENDCMDINPARAMS
)-1 + size
;
1199 sb
.params
.in
.irDriveRegs
= *regs
;
1200 sb
.params
.in
.cBufferSize
= size
;
1202 // Call miniport ioctl
1203 size
+= sizeof(SRB_IO_CONTROL
) + sizeof(SENDCMDINPARAMS
)-1;
1205 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1206 &sb
, size
, &sb
, size
, &num_out
, NULL
)) {
1207 long err
= GetLastError();
1208 if (con
->reportataioctl
) {
1209 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name
, err
);
1210 print_ide_regs_io(regs
, NULL
);
1212 errno
= (err
== ERROR_INVALID_FUNCTION
|| err
== ERROR_NOT_SUPPORTED
? ENOSYS
: EIO
);
1217 if (sb
.srbc
.ReturnCode
) {
1218 if (con
->reportataioctl
) {
1219 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name
, sb
.srbc
.ReturnCode
);
1220 print_ide_regs_io(regs
, NULL
);
1226 if (sb
.params
.out
.DriverStatus
.bDriverError
) {
1227 if (con
->reportataioctl
) {
1228 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name
,
1229 sb
.params
.out
.DriverStatus
.bDriverError
, sb
.params
.out
.DriverStatus
.bIDEError
);
1230 print_ide_regs_io(regs
, NULL
);
1232 errno
= (!sb
.params
.out
.DriverStatus
.bIDEError
? ENOSYS
: EIO
);
1236 if (con
->reportataioctl
> 1) {
1237 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name
,
1238 num_out
, sb
.params
.out
.cBufferSize
);
1239 print_ide_regs_io(regs
, (code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
?
1240 (const IDEREGS
*)(sb
.params
.out
.bBuffer
) : 0));
1244 memcpy(data
, sb
.params
.out
.bBuffer
, datasize
);
1245 else if (datasize
== 0 && code
== IOCTL_SCSI_MINIPORT_RETURN_STATUS
)
1246 *regs
= *(const IDEREGS
*)(sb
.params
.out
.bBuffer
);
1252 /////////////////////////////////////////////////////////////////////////////
1254 // ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1256 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice
, IDEREGS
* regs
, char * data
, int datasize
, int port
)
1259 SRB_IO_CONTROL srbc
;
1263 ASSERT_SIZEOF(sb
, sizeof(SRB_IO_CONTROL
)+sizeof(IDEREGS
)+512);
1265 if (!(0 <= datasize
&& datasize
<= (int)sizeof(sb
.buffer
) && port
>= 0)) {
1269 memset(&sb
, 0, sizeof(sb
));
1270 strcpy((char *)sb
.srbc
.Signature
, "<3ware>");
1271 sb
.srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1272 sb
.srbc
.Timeout
= 60; // seconds
1273 sb
.srbc
.ControlCode
= 0xA0000000;
1274 sb
.srbc
.ReturnCode
= 0;
1275 sb
.srbc
.Length
= sizeof(IDEREGS
) + (datasize
> 0 ? datasize
: 1);
1277 sb
.regs
.bReserved
= port
;
1280 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1281 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
1282 long err
= GetLastError();
1283 if (con
->reportataioctl
) {
1284 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1285 print_ide_regs_io(regs
, NULL
);
1287 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1291 if (sb
.srbc
.ReturnCode
) {
1292 if (con
->reportataioctl
) {
1293 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb
.srbc
.ReturnCode
);
1294 print_ide_regs_io(regs
, NULL
);
1302 memcpy(data
, sb
.buffer
, datasize
);
1304 if (con
->reportataioctl
> 1) {
1305 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out
);
1306 print_ide_regs_io(regs
, &sb
.regs
);
1314 /////////////////////////////////////////////////////////////////////////////
1316 // 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1317 // 3DM/CLI "Rescan Controller" function does not to always update it.
1319 static int update_3ware_devicemap_ioctl(HANDLE hdevice
)
1321 SRB_IO_CONTROL srbc
;
1322 memset(&srbc
, 0, sizeof(srbc
));
1323 strcpy((char *)srbc
.Signature
, "<3ware>");
1324 srbc
.HeaderLength
= sizeof(SRB_IO_CONTROL
);
1325 srbc
.Timeout
= 60; // seconds
1326 srbc
.ControlCode
= 0xCC010014;
1327 srbc
.ReturnCode
= 0;
1331 if (!DeviceIoControl(hdevice
, IOCTL_SCSI_MINIPORT
,
1332 &srbc
, sizeof(srbc
), &srbc
, sizeof(srbc
), &num_out
, NULL
)) {
1333 long err
= GetLastError();
1334 if (con
->reportataioctl
)
1335 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err
);
1336 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1339 if (srbc
.ReturnCode
) {
1340 if (con
->reportataioctl
)
1341 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc
.ReturnCode
);
1345 if (con
->reportataioctl
> 1)
1346 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1352 /////////////////////////////////////////////////////////////////////////////
1354 // Routines for pseudo device /dev/tw_cli/*
1355 // Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1358 // Get clipboard data
1360 static int get_clipboard(char * data
, int datasize
)
1362 if (!OpenClipboard(NULL
))
1364 HANDLE h
= GetClipboardData(CF_TEXT
);
1369 const void * p
= GlobalLock(h
);
1370 int n
= GlobalSize(h
);
1380 // Run a command, write stdout to dataout
1381 // TODO: Combine with daemon_win32.cpp:daemon_spawn()
1383 static int run_cmd(const char * cmd
, char * dataout
, int outsize
)
1385 // Create stdout pipe
1386 SECURITY_ATTRIBUTES sa
= {sizeof(sa
), 0, TRUE
};
1387 HANDLE pipe_out_w
, h
;
1388 if (!CreatePipe(&h
, &pipe_out_w
, &sa
/*inherit*/, outsize
))
1390 HANDLE self
= GetCurrentProcess();
1392 if (!DuplicateHandle(self
, h
, self
, &pipe_out_r
,
1393 GENERIC_READ
, FALSE
/*!inherit*/, DUPLICATE_CLOSE_SOURCE
)) {
1394 CloseHandle(pipe_out_w
);
1398 if (!DuplicateHandle(self
, pipe_out_w
, self
, &pipe_err_w
,
1399 0, TRUE
/*inherit*/, DUPLICATE_SAME_ACCESS
)) {
1400 CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1405 STARTUPINFO si
; memset(&si
, 0, sizeof(si
)); si
.cb
= sizeof(si
);
1406 si
.hStdInput
= INVALID_HANDLE_VALUE
;
1407 si
.hStdOutput
= pipe_out_w
; si
.hStdError
= pipe_err_w
;
1408 si
.dwFlags
= STARTF_USESTDHANDLES
;
1409 PROCESS_INFORMATION pi
;
1411 NULL
, const_cast<char *>(cmd
),
1412 NULL
, NULL
, TRUE
/*inherit*/,
1413 CREATE_NO_WINDOW
/*do not create a new console window*/,
1414 NULL
, NULL
, &si
, &pi
)) {
1415 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_r
); CloseHandle(pipe_out_w
);
1418 CloseHandle(pi
.hThread
);
1419 CloseHandle(pipe_err_w
); CloseHandle(pipe_out_w
);
1421 // Copy stdout to output buffer
1423 while (i
< outsize
) {
1425 if (!ReadFile(pipe_out_r
, dataout
+i
, outsize
-i
, &num_read
, NULL
) || num_read
== 0)
1429 CloseHandle(pipe_out_r
);
1431 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1432 CloseHandle(pi
.hProcess
);
1437 static const char * findstr(const char * str
, const char * sub
)
1439 const char * s
= strstr(str
, sub
);
1440 return (s
? s
+strlen(sub
) : "");
1444 static void copy_swapped(unsigned char * dest
, const char * src
, int destsize
)
1446 int srclen
= strcspn(src
, "\r\n");
1448 for (i
= 0; i
< destsize
-1 && i
< srclen
-1; i
+=2) {
1449 dest
[i
] = src
[i
+1]; dest
[i
+1] = src
[i
];
1451 if (i
< destsize
-1 && i
< srclen
)
1456 static ata_identify_device
* tw_cli_identbuf
= 0;
1457 static ata_smart_values
* tw_cli_smartbuf
= 0;
1459 static int tw_cli_open(const char * name
)
1461 // Read tw_cli or 3DM browser output into buffer
1463 int size
= -1, n1
= -1;
1464 if (!strcmp(name
, "clip")) { // tw_cli/clip => read clipboard
1465 size
= get_clipboard(buffer
, sizeof(buffer
));
1467 else if (!strcmp(name
, "stdin")) { // tw_cli/stdin => read stdin
1468 size
= fread(buffer
, 1, sizeof(buffer
), stdin
);
1470 else if (sscanf(name
, "c%*u/p%*u%n", &n1
) >= 0 && n1
== (int)strlen(name
)) {
1471 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
1473 snprintf(cmd
, sizeof(cmd
), "tw_cli /%s show all", name
);
1474 if (con
->reportataioctl
> 1)
1475 pout("tw_cli/%s: Run: \"%s\"\n", name
, cmd
);
1476 size
= run_cmd(cmd
, buffer
, sizeof(buffer
));
1479 errno
= EINVAL
; return -1;
1482 if (con
->reportataioctl
> 1)
1483 pout("tw_cli/%s: Read %d bytes\n", name
, size
);
1485 errno
= ENOENT
; return -1;
1487 if (size
>= (int)sizeof(buffer
)) {
1488 errno
= EIO
; return -1;
1491 if (con
->reportataioctl
> 1)
1492 pout("[\n%.100s%s\n]\n", buffer
, (size
>100?"...":""));
1494 // Fake identify sector
1495 ASSERT_SIZEOF(ata_identify_device
, 512);
1496 ata_identify_device
* id
= (ata_identify_device
*)malloc(sizeof(ata_identify_device
));
1497 memset(id
, 0, sizeof(*id
));
1498 copy_swapped(id
->model
, findstr(buffer
, " Model = " ), sizeof(id
->model
));
1499 copy_swapped(id
->fw_rev
, findstr(buffer
, " Firmware Version = "), sizeof(id
->fw_rev
));
1500 copy_swapped(id
->serial_no
, findstr(buffer
, " Serial = " ), sizeof(id
->serial_no
));
1501 unsigned long nblocks
= 0; // "Capacity = N.N GB (N Blocks)"
1502 sscanf(findstr(buffer
, "Capacity = "), "%*[^(\r\n](%lu", &nblocks
);
1504 id
->words047_079
[49-47] = 0x0200; // size valid
1505 id
->words047_079
[60-47] = (unsigned short)(nblocks
); // secs_16
1506 id
->words047_079
[61-47] = (unsigned short)(nblocks
>>16); // secs_32
1508 id
->major_rev_num
= 0x1<<3; // ATA-3
1509 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1510 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1512 // Parse smart data hex dump
1513 const char * s
= findstr(buffer
, "Drive Smart Data:");
1515 s
= findstr(buffer
, "S.M.A.R.T. (Controller"); // from 3DM browser window
1517 const char * s1
= findstr(s
, "<td class"); // html version
1520 s
+= strcspn(s
, "\r\n");
1523 s
= buffer
; // try raw hex dump without header
1525 unsigned char * sd
= (unsigned char *)malloc(512);
1528 unsigned x
= ~0; int n
= -1;
1529 if (!(sscanf(s
, "%x %n", &x
, &n
) == 1 && !(x
& ~0xff)))
1531 sd
[i
] = (unsigned char)x
;
1532 if (!(++i
< 512 && n
> 0))
1535 if (*s
== '<') // "<br>"
1536 s
+= strcspn(s
, "\r\n");
1540 if (!id
->model
[1]) {
1541 // No useful data found
1543 char * err
= strstr(buffer
, "Error:");
1545 err
= strstr(buffer
, "error :");
1547 // Print tw_cli error message
1548 err
[strcspn(err
, "\r\n")] = 0;
1557 tw_cli_identbuf
= id
;
1558 tw_cli_smartbuf
= (ata_smart_values
*)sd
;
1559 return TW_CLI_FDOFFSET
;
1563 static void tw_cli_close()
1565 if (tw_cli_identbuf
) {
1566 free(tw_cli_identbuf
); tw_cli_identbuf
= 0;
1568 if (tw_cli_smartbuf
) {
1569 free(tw_cli_smartbuf
); tw_cli_smartbuf
= 0;
1574 static int tw_cli_command_interface(smart_command_set command
, int /*select*/, char * data
)
1578 if (!tw_cli_identbuf
)
1580 memcpy(data
, tw_cli_identbuf
, 512);
1583 if (!tw_cli_smartbuf
)
1585 memcpy(data
, tw_cli_smartbuf
, 512);
1587 case READ_THRESHOLDS
:
1588 if (!tw_cli_smartbuf
)
1590 // Fake zero thresholds
1592 const ata_smart_values
* sv
= tw_cli_smartbuf
;
1593 ata_smart_thresholds_pvt
* tr
= (ata_smart_thresholds_pvt
*)data
;
1595 // TODO: Indicate missing thresholds in ataprint.cpp:PrintSmartAttribWithThres()
1596 // (ATA_SMART_READ_THRESHOLDS is marked obsolete since ATA-5)
1597 for (int i
= 0; i
< NUMBER_ATA_SMART_ATTRIBUTES
; i
++)
1598 tr
->chksum
-= tr
->thres_entries
[i
].id
= sv
->vendor_attributes
[i
].id
;
1603 case STATUS_CHECK
: // Fake "good" SMART status
1608 // Arrive here for all unsupported commands
1614 /////////////////////////////////////////////////////////////////////////////
1616 // IOCTL_STORAGE_QUERY_PROPERTY
1618 #define FILE_DEVICE_MASS_STORAGE 0x0000002d
1619 #define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
1620 #define FILE_ANY_ACCESS 0
1622 #define IOCTL_STORAGE_QUERY_PROPERTY \
1623 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
1625 typedef enum _STORAGE_BUS_TYPE
{
1626 BusTypeUnknown
= 0x00,
1628 BusTypeAtapi
= 0x02,
1632 BusTypeFibre
= 0x06,
1635 BusTypeiScsi
= 0x09,
1641 BusTypeMaxReserved
= 0x7F
1642 } STORAGE_BUS_TYPE
, *PSTORAGE_BUS_TYPE
;
1644 typedef struct _STORAGE_DEVICE_DESCRIPTOR
{
1648 UCHAR DeviceTypeModifier
;
1649 BOOLEAN RemovableMedia
;
1650 BOOLEAN CommandQueueing
;
1651 ULONG VendorIdOffset
;
1652 ULONG ProductIdOffset
;
1653 ULONG ProductRevisionOffset
;
1654 ULONG SerialNumberOffset
;
1655 STORAGE_BUS_TYPE BusType
;
1656 ULONG RawPropertiesLength
;
1657 UCHAR RawDeviceProperties
[1];
1658 } STORAGE_DEVICE_DESCRIPTOR
, *PSTORAGE_DEVICE_DESCRIPTOR
;
1660 typedef enum _STORAGE_QUERY_TYPE
{
1661 PropertyStandardQuery
= 0,
1662 PropertyExistsQuery
,
1664 PropertyQueryMaxDefined
1665 } STORAGE_QUERY_TYPE
, *PSTORAGE_QUERY_TYPE
;
1667 typedef enum _STORAGE_PROPERTY_ID
{
1668 StorageDeviceProperty
= 0,
1669 StorageAdapterProperty
,
1670 StorageDeviceIdProperty
,
1671 StorageDeviceUniqueIdProperty
,
1672 StorageDeviceWriteCacheProperty
,
1673 StorageMiniportProperty
,
1674 StorageAccessAlignmentProperty
1675 } STORAGE_PROPERTY_ID
, *PSTORAGE_PROPERTY_ID
;
1677 typedef struct _STORAGE_PROPERTY_QUERY
{
1678 STORAGE_PROPERTY_ID PropertyId
;
1679 STORAGE_QUERY_TYPE QueryType
;
1680 UCHAR AdditionalParameters
[1];
1681 } STORAGE_PROPERTY_QUERY
, *PSTORAGE_PROPERTY_QUERY
;
1684 /////////////////////////////////////////////////////////////////////////////
1686 union STORAGE_DEVICE_DESCRIPTOR_DATA
{
1687 STORAGE_DEVICE_DESCRIPTOR desc
;
1691 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1692 // (This works without admin rights)
1694 static int storage_query_property_ioctl(HANDLE hdevice
, STORAGE_DEVICE_DESCRIPTOR_DATA
* data
)
1696 STORAGE_PROPERTY_QUERY query
= {StorageDeviceProperty
, PropertyStandardQuery
, 0};
1697 memset(data
, 0, sizeof(*data
));
1700 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1701 &query
, sizeof(query
), data
, sizeof(*data
), &num_out
, NULL
)) {
1702 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1)
1703 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
1708 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1) {
1709 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1711 " Product: \"%s\"\n"
1712 " Revision: \"%s\"\n"
1714 " BusType: 0x%02x\n",
1715 (data
->desc
.VendorIdOffset
? data
->raw
+data
->desc
.VendorIdOffset
: ""),
1716 (data
->desc
.ProductIdOffset
? data
->raw
+data
->desc
.ProductIdOffset
: ""),
1717 (data
->desc
.ProductRevisionOffset
? data
->raw
+data
->desc
.ProductRevisionOffset
: ""),
1718 (data
->desc
.RemovableMedia
? "Yes":"No"), data
->desc
.BusType
1725 /////////////////////////////////////////////////////////////////////////////
1727 // IOCTL_STORAGE_PREDICT_FAILURE
1729 #define IOCTL_STORAGE_PREDICT_FAILURE \
1730 CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
1732 typedef struct _STORAGE_PREDICT_FAILURE
{
1733 ULONG PredictFailure
;
1734 UCHAR VendorSpecific
[512];
1735 } STORAGE_PREDICT_FAILURE
, *PSTORAGE_PREDICT_FAILURE
;
1737 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE
, 4+512);
1740 /////////////////////////////////////////////////////////////////////////////
1743 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1744 // or -1 on error, opionally return VendorSpecific data.
1745 // (This works without admin rights)
1747 static int storage_predict_failure_ioctl(HANDLE hdevice
, char * data
= 0)
1749 STORAGE_PREDICT_FAILURE pred
;
1750 memset(&pred
, 0, sizeof(pred
));
1753 if (!DeviceIoControl(hdevice
, IOCTL_STORAGE_PREDICT_FAILURE
,
1754 0, 0, &pred
, sizeof(pred
), &num_out
, NULL
)) {
1755 if (con
->reportataioctl
> 1)
1756 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
1761 if (con
->reportataioctl
> 1) {
1762 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1763 " PredictFailure: 0x%08lx\n"
1764 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1765 pred
.PredictFailure
,
1766 pred
.VendorSpecific
[0], pred
.VendorSpecific
[1], pred
.VendorSpecific
[2],
1767 pred
.VendorSpecific
[sizeof(pred
.VendorSpecific
)-1]
1771 memcpy(data
, pred
.VendorSpecific
, sizeof(pred
.VendorSpecific
));
1772 return (!pred
.PredictFailure
? 0 : 1);
1776 /////////////////////////////////////////////////////////////////////////////
1778 // get CONTROLLER_* for open handle
1779 static int get_controller_type(HANDLE hdevice
, bool admin
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
1781 // Try SMART_GET_VERSION first to detect ATA SMART support
1782 // for drivers reporting BusTypeScsi (3ware)
1783 if (admin
&& smart_get_version(hdevice
, ata_version_ex
) >= 0)
1784 return CONTROLLER_ATA
;
1786 // Get BusType from device descriptor
1787 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1788 if (storage_query_property_ioctl(hdevice
, &data
))
1789 return CONTROLLER_UNKNOWN
;
1791 switch (data
.desc
.BusType
) {
1795 memset(ata_version_ex
, 0, sizeof(*ata_version_ex
));
1796 return CONTROLLER_ATA
;
1800 return CONTROLLER_SCSI
;
1802 return CONTROLLER_UNKNOWN
;
1807 // get CONTROLLER_* for device path
1808 static int get_controller_type(const char * path
, GETVERSIONINPARAMS_EX
* ata_version_ex
= 0)
1811 HANDLE h
= CreateFileA(path
, GENERIC_READ
|GENERIC_WRITE
,
1812 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1813 if (h
== INVALID_HANDLE_VALUE
) {
1815 h
= CreateFileA(path
, 0,
1816 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1817 if (h
== INVALID_HANDLE_VALUE
)
1818 return CONTROLLER_UNKNOWN
;
1820 if (con
->reportataioctl
> 1 || con
->reportscsiioctl
> 1)
1821 pout(" %s: successfully opened%s\n", path
, (!admin
? " (without admin rights)" :""));
1822 int type
= get_controller_type(h
, admin
, ata_version_ex
);
1827 // get CONTROLLER_* for physical drive number
1828 static int get_phy_drive_type(int drive
, GETVERSIONINPARAMS_EX
* ata_version_ex
)
1831 snprintf(path
, sizeof(path
)-1, "\\\\.\\PhysicalDrive%d", drive
);
1832 return get_controller_type(path
, ata_version_ex
);
1835 static int get_phy_drive_type(int drive
)
1837 return get_phy_drive_type(drive
, 0);
1840 // get CONTROLLER_* for logical drive number
1841 static int get_log_drive_type(int drive
)
1844 snprintf(path
, sizeof(path
)-1, "\\\\.\\%c:", 'A'+drive
);
1845 return get_controller_type(path
);
1848 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1849 static int get_identify_from_device_property(HANDLE hdevice
, ata_identify_device
* id
)
1851 STORAGE_DEVICE_DESCRIPTOR_DATA data
;
1852 if (storage_query_property_ioctl(hdevice
, &data
))
1855 memset(id
, 0, sizeof(*id
));
1856 if (data
.desc
.ProductIdOffset
)
1857 copy_swapped(id
->model
, data
.raw
+data
.desc
.ProductIdOffset
, sizeof(id
->model
));
1858 if (data
.desc
.ProductRevisionOffset
)
1859 copy_swapped(id
->fw_rev
, data
.raw
+data
.desc
.ProductRevisionOffset
, sizeof(id
->fw_rev
));
1860 id
->major_rev_num
= 0x1<<3; // ATA-3
1861 id
->command_set_1
= 0x0001; id
->command_set_2
= 0x4000; // SMART supported, words 82,83 valid
1862 id
->cfs_enable_1
= 0x0001; id
->csf_default
= 0x4000; // SMART enabled, words 85,87 valid
1867 /////////////////////////////////////////////////////////////////////////////
1869 // Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
1870 // returns: 1=active, 0=standby, -1=error
1871 // (This would also work for SCSI drives)
1873 static int get_device_power_state(HANDLE hdevice
)
1875 static HINSTANCE h_kernel_dll
= 0;
1877 static DWORD kernel_dll_pid
= 0;
1879 static BOOL (WINAPI
* GetDevicePowerState_p
)(HANDLE
, BOOL
*) = 0;
1883 if (!GetDevicePowerState_p
1885 || kernel_dll_pid
!= GetCurrentProcessId() // detect fork()
1888 if (h_kernel_dll
== INVALID_HANDLE_VALUE
) {
1892 if (!(h_kernel_dll
= LoadLibraryA("KERNEL32.DLL"))) {
1893 pout("Cannot load KERNEL32.DLL, Error=%ld\n", GetLastError());
1894 h_kernel_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
1898 if (!(GetDevicePowerState_p
= (BOOL (WINAPI
*)(HANDLE
, BOOL
*))
1899 GetProcAddress(h_kernel_dll
, "GetDevicePowerState"))) {
1900 if (con
->reportataioctl
)
1901 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
1902 FreeLibrary(h_kernel_dll
);
1903 h_kernel_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
1908 kernel_dll_pid
= GetCurrentProcessId();
1912 if (!GetDevicePowerState_p(hdevice
, &state
)) {
1913 long err
= GetLastError();
1914 if (con
->reportataioctl
)
1915 pout(" GetDevicePowerState() failed, Error=%ld\n", err
);
1916 errno
= (err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
1917 // TODO: This may not work as expected on transient errors,
1918 // because smartd interprets -1 as SLEEP mode regardless of errno.
1922 if (con
->reportataioctl
> 1)
1923 pout(" GetDevicePowerState() succeeded, state=%d\n", state
);
1928 /////////////////////////////////////////////////////////////////////////////
1930 // TODO: Put in a struct indexed by fd (or better a C++ object of course ;-)
1931 static HANDLE h_ata_ioctl
= 0;
1932 static bool ata_admin
; // true if opened with admin rights
1933 static const char * ata_def_options
;
1934 static char * ata_usr_options
;
1935 const int max_ata_driveno
= 25;
1936 static int ata_driveno
; // Drive number
1937 static int ata_driveno_is_log
= -1; // 0=physical drivenumber, 1=logical drive number, -1=unknown
1938 static char ata_smartver_state
[max_ata_driveno
+1]; // SMART_GET_VERSION: 0=unknown, 1=OK, 2=failed
1940 // Print SMARTVSD error message, return errno
1942 static int smartvsd_error()
1944 char path
[MAX_PATH
];
1946 if (!(5 <= (len
= GetSystemDirectoryA(path
, MAX_PATH
)) && len
< MAX_PATH
/2))
1948 // SMARTVSD.VXD present?
1949 strcpy(path
+len
, "\\IOSUBSYS\\SMARTVSD.VXD");
1950 if (!access(path
, 0)) {
1951 // Yes, standard IDE driver used?
1953 if ( (h
= CreateFileA("\\\\.\\ESDI_506",
1954 GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1955 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
1956 && GetLastError() == ERROR_FILE_NOT_FOUND
) {
1957 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
1961 if (h
!= INVALID_HANDLE_VALUE
) // should not happen
1963 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
1968 strcpy(path
+len
, "\\SMARTVSD.VXD");
1969 if (!access(path
, 0)) {
1970 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
1971 // (http://support.microsoft.com/kb/265854/en-us).
1973 pout("SMART driver is not properly installed,\n"
1974 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
1975 " and reboot Windows.\n", path
, path
);
1978 // Some Windows versions do not provide SMARTVSD.VXD
1979 // (http://support.microsoft.com/kb/199886/en-us).
1981 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path
);
1988 // Get default ATA device options
1990 static const char * ata_get_def_options()
1992 DWORD ver
= GetVersion();
1993 if ((ver
& 0x80000000) || (ver
& 0xff) < 4) // Win9x/ME
1994 return "s"; // SMART_* only
1995 else if ((ver
& 0xff) == 4) // WinNT4
1996 return "sc"; // SMART_*, SCSI_PASS_THROUGH
1997 else // WinXP, 2003, Vista
1998 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
1999 // STORAGE_*, SCSI_MINIPORT_*
2005 static int ata_open(int phydrive
, int logdrive
, const char * options
, int port
)
2007 // TODO: This version does not allow to open more than 1 ATA devices
2013 // Using both physical and logical drive names (in smartd.conf) not supported yet
2014 if (!( ata_driveno_is_log
< 0
2015 || (phydrive
>= 0 && !ata_driveno_is_log
)
2016 || (logdrive
>= 0 && ata_driveno_is_log
))) {
2017 pout("Using both /dev/hdX and X: is not supported\n");
2022 // path depends on Windows Version
2023 bool win9x
= is_win9x();
2025 if (win9x
&& 0 <= phydrive
&& phydrive
<= 7)
2026 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
2027 strcpy(devpath
, (phydrive
<= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
2028 else if (!win9x
&& 0 <= phydrive
&& phydrive
<= max_ata_driveno
)
2029 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\PhysicalDrive%d", phydrive
);
2030 else if (!win9x
&& 0 <= logdrive
&& logdrive
<= max_ata_driveno
) {
2031 snprintf(devpath
, sizeof(devpath
)-1, "\\\\.\\%c:", 'A'+logdrive
);
2039 h_ata_ioctl
= INVALID_HANDLE_VALUE
;
2040 if (win9x
|| !(*options
&& !options
[strspn(options
, "fp")])) {
2041 // Open with admin rights
2043 h_ata_ioctl
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2044 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2045 NULL
, OPEN_EXISTING
, 0, 0);
2047 if (!win9x
&& h_ata_ioctl
== INVALID_HANDLE_VALUE
) {
2048 // Open without admin rights
2050 h_ata_ioctl
= CreateFileA(devpath
, 0,
2051 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2052 NULL
, OPEN_EXISTING
, 0, 0);
2054 if (h_ata_ioctl
== INVALID_HANDLE_VALUE
) {
2055 long err
= GetLastError();
2056 pout("Cannot open device %s, Error=%ld\n", devpath
, err
);
2057 if (err
== ERROR_FILE_NOT_FOUND
)
2058 errno
= (win9x
&& phydrive
<= 3 ? smartvsd_error() : ENOENT
);
2059 else if (err
== ERROR_ACCESS_DENIED
)
2067 if (con
->reportataioctl
> 1)
2068 pout("%s: successfully opened%s\n", devpath
, (!ata_admin
? " (without admin rights)" :""));
2070 // Set default options according to Windows version
2071 if (!ata_def_options
)
2072 ata_def_options
= ata_get_def_options();
2073 // Save user options
2074 if (port
>= 0 && !*options
)
2075 options
= "s3"; // RAID: SMART_* and SCSI_MINIPORT
2076 assert(!ata_usr_options
);
2078 ata_usr_options
= strdup(options
);
2080 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
2081 if (phydrive
>= 0) {
2082 ata_driveno
= phydrive
; ata_driveno_is_log
= 0;
2085 assert(logdrive
>= 0);
2086 ata_driveno
= logdrive
; ata_driveno_is_log
= 1;
2088 if (!win9x
&& port
< 0)
2091 // Win9X/ME: Get drive map
2092 // RAID: Get port map
2093 GETVERSIONINPARAMS_EX vers_ex
;
2094 int devmap
= smart_get_version(h_ata_ioctl
, (port
>= 0 ? &vers_ex
: 0));
2096 unsigned long portmap
= 0;
2097 if (port
>= 0 && devmap
>= 0) {
2098 // 3ware RAID: check vendor id
2099 if (vers_ex
.wIdentifier
!= SMART_VENDOR_3WARE
) {
2100 pout("SMART_GET_VERSION returns unknown Identifier = %04x\n"
2101 "This is no 3ware 9000 controller or driver has no SMART support.\n",
2102 vers_ex
.wIdentifier
);
2106 portmap
= vers_ex
.dwDeviceMapEx
;
2109 pout("%s: ATA driver has no SMART support\n", devpath
);
2110 if (!is_permissive()) {
2117 ata_smartver_state
[ata_driveno
] = 1;
2120 // 3ware RAID: update devicemap first
2121 if (!update_3ware_devicemap_ioctl(h_ata_ioctl
)) {
2122 if ( smart_get_version(h_ata_ioctl
, &vers_ex
) >= 0
2123 && vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
)
2124 portmap
= vers_ex
.dwDeviceMapEx
;
2126 // Check port existence
2127 if (!(portmap
& (1L << port
))) {
2128 pout("%s: Port %d is empty or does not exist\n", devpath
, port
);
2129 if (!is_permissive()) {
2135 // Encode port into pseudo fd
2136 return (ATARAID_FDOFFSET
| port
);
2139 // Win9x/ME: Check device presence & type
2140 if (((devmap
>> (ata_driveno
& 0x3)) & 0x11) != 0x01) {
2141 unsigned char atapi
= (devmap
>> (ata_driveno
& 0x3)) & 0x10;
2142 pout("%s: Drive %d %s (IDEDeviceMap=0x%02x).\n", devpath
,
2143 ata_driveno
, (atapi
?"is an ATAPI device":"does not exist"), devmap
);
2144 // Win9x drive existence check may not work as expected
2145 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
2146 // (The related KB Article Q196120 is no longer available)
2147 if (!is_permissive()) {
2149 errno
= (atapi
? ENOSYS
: ENOENT
);
2153 // Use drive number as fd for ioctl
2154 return (ata_driveno
& 0x3);
2158 static void ata_close(int /*fd*/)
2160 CloseHandle(h_ata_ioctl
);
2162 if (ata_usr_options
) {
2163 free(ata_usr_options
);
2164 ata_usr_options
= 0;
2169 // Scan for ATA drives on Win9x/ME, fill bitmask of drives present, return #drives
2171 static int ata_scan_win9x(unsigned long * drives
)
2174 const char devpath
[] = "\\\\.\\SMARTVSD";
2175 HANDLE h
= CreateFileA(devpath
, GENERIC_READ
|GENERIC_WRITE
,
2176 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0);
2177 if (h
== INVALID_HANDLE_VALUE
) {
2178 if (con
->reportataioctl
> 1)
2179 pout(" %s: Open failed, Error=%ld\n", devpath
, GetLastError());
2180 return 0; // SMARTVSD.VXD missing or no ATA devices
2184 int devmap
= smart_get_version(h
);
2187 return 0; // Should not happen
2189 // Check ATA device presence, remove ATAPI devices
2190 drives
[0] = (devmap
& 0xf) & ~((devmap
>> 4) & 0xf);
2191 return (drives
[0]&1) + ((drives
[0]>>1)&1) + ((drives
[0]>>2)&1) + ((drives
[0]>>3)&1);
2195 // Scan for ATA drives, fill bitmask of drives present, return #drives
2197 static int ata_scan(unsigned long * drives
, int * rdriveno
, unsigned long * rdrives
)
2200 for (int i
= 0; i
<= 9; i
++) {
2201 GETVERSIONINPARAMS_EX vers_ex
;
2202 if (get_phy_drive_type(i
, &vers_ex
) != CONTROLLER_ATA
)
2205 // Interpret RAID drive map if present
2206 if (vers_ex
.wIdentifier
== SMART_VENDOR_3WARE
) {
2207 // Skip if more than 2 controllers or logical drive from this controller already seen
2208 if (vers_ex
.wControllerId
>= 2 || rdriveno
[vers_ex
.wControllerId
] >= 0)
2210 assert(rdrives
[vers_ex
.wControllerId
] == 0);
2211 // Count physical drives
2213 for (int pi
= 0; pi
< 32; pi
++) {
2214 if (vers_ex
.dwDeviceMapEx
& (1L << pi
))
2218 continue; // Should not happen
2219 rdrives
[vers_ex
.wControllerId
] = vers_ex
.dwDeviceMapEx
;
2220 rdriveno
[vers_ex
.wControllerId
] = i
;
2224 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
2225 drives
[0] |= (1L << i
);
2233 /////////////////////////////////////////////////////////////////////////////
2235 // Interface to ATA devices. See os_linux.c
2236 int ata_command_interface(int fd
, smart_command_set command
, int select
, char * data
)
2238 if (fd
== TW_CLI_FDOFFSET
) // Parse tw_cli output
2239 return tw_cli_command_interface(command
, select
, data
);
2242 if ((fd
& ~0x1f) == ATARAID_FDOFFSET
) {
2243 // RAID Port encoded into pseudo fd
2248 if (!(0 <= fd
&& fd
<= 3)) {
2253 // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
2254 IDEREGS regs
; memset(®s
, 0, sizeof(regs
));
2255 regs
.bCommandReg
= ATA_SMART_CMD
;
2256 regs
.bCylHighReg
= SMART_CYL_HI
; regs
.bCylLowReg
= SMART_CYL_LOW
;
2259 // Try by default: SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2260 // and SCSI_MINIPORT_* if requested by user
2261 const char * valid_options
= (ata_usr_options
? "saicmf" : "saicf");
2264 case CHECK_POWER_MODE
:
2265 // Not a SMART command, needs IDE register return
2266 regs
.bCommandReg
= ATA_CHECK_POWER_MODE
;
2267 regs
.bCylLowReg
= regs
.bCylHighReg
= 0;
2268 valid_options
= "pai3"; // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
2269 // Note: returns SectorCountReg in data[0]
2272 regs
.bFeaturesReg
= ATA_SMART_READ_VALUES
;
2273 regs
.bSectorNumberReg
= regs
.bSectorCountReg
= 1;
2276 case READ_THRESHOLDS
:
2277 regs
.bFeaturesReg
= ATA_SMART_READ_THRESHOLDS
;
2278 regs
.bSectorNumberReg
= regs
.bSectorCountReg
= 1;
2282 regs
.bFeaturesReg
= ATA_SMART_READ_LOG_SECTOR
;
2283 regs
.bSectorNumberReg
= select
;
2284 regs
.bSectorCountReg
= 1;
2285 // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
2286 // Try SCSI_MINIPORT also to skip buggy class driver
2287 valid_options
= (ata_usr_options
|| is_win9x() ? "saicm3" : "aicm3");
2291 regs
.bFeaturesReg
= ATA_SMART_WRITE_LOG_SECTOR
;
2292 regs
.bSectorNumberReg
= select
;
2293 regs
.bSectorCountReg
= 1;
2294 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
2295 // but SCSI_MINIPORT_* only if requested by user
2296 valid_options
= (ata_usr_options
? "am" : "a");
2297 datasize
= -512; // DATA_OUT!
2300 // Note: WinNT4/2000/XP return identify data cached during boot
2301 // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH)
2302 regs
.bCommandReg
= ATA_IDENTIFY_DEVICE
;
2303 regs
.bCylLowReg
= regs
.bCylHighReg
= 0;
2304 regs
.bSectorCountReg
= 1;
2308 regs
.bCommandReg
= ATA_IDENTIFY_PACKET_DEVICE
;
2309 regs
.bCylLowReg
= regs
.bCylHighReg
= 0;
2310 regs
.bSectorCountReg
= 1;
2314 regs
.bFeaturesReg
= ATA_SMART_ENABLE
;
2315 regs
.bSectorNumberReg
= 1;
2318 regs
.bFeaturesReg
= ATA_SMART_DISABLE
;
2319 regs
.bSectorNumberReg
= 1;
2322 // Requires CL,CH register return
2323 valid_options
= (ata_usr_options
? "saimf" : "saif");
2325 regs
.bFeaturesReg
= ATA_SMART_STATUS
;
2328 regs
.bFeaturesReg
= ATA_SMART_AUTO_OFFLINE
;
2329 regs
.bSectorCountReg
= select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
2332 regs
.bFeaturesReg
= ATA_SMART_AUTOSAVE
;
2333 regs
.bSectorCountReg
= select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
2335 case IMMEDIATE_OFFLINE
:
2336 regs
.bFeaturesReg
= ATA_SMART_IMMEDIATE_OFFLINE
;
2337 regs
.bSectorNumberReg
= select
;
2338 // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
2339 valid_options
= (ata_usr_options
|| select
!= 127/*ABORT*/ || is_win9x() ?
2340 "saicm3" : "aicm3");
2343 pout("Unrecognized command %d in win32_ata_command_interface()\n"
2344 "Please contact " PACKAGE_BUGREPORT
"\n", command
);
2350 // Restrict to IOCTL_STORAGE_*
2351 if (strchr(valid_options
, 'f'))
2352 valid_options
= "f";
2353 else if (strchr(valid_options
, 'p'))
2354 valid_options
= "p";
2361 // Try all valid ioctls in the order specified in ata_*_options
2362 bool powered_up
= false;
2363 assert(ata_def_options
);
2364 const char * options
= (ata_usr_options
? ata_usr_options
: ata_def_options
);
2365 for (int i
= 0; ; i
++) {
2366 char opt
= options
[i
];
2369 if (command
== CHECK_POWER_MODE
&& powered_up
) {
2370 // Power up reported by GetDevicePowerState() and no ioctl available
2371 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
2372 regs
.bSectorCountReg
= 0xff;
2379 if (!strchr(valid_options
, opt
))
2380 // Invalid for this command
2384 assert(datasize
== 0 || datasize
== 512 || (strchr("am", opt
) && datasize
== -512));
2389 // call SMART_GET_VERSION once for each drive
2390 assert(0 <= ata_driveno
&& ata_driveno
< sizeof(ata_smartver_state
));
2391 if (ata_smartver_state
[ata_driveno
] > 1) {
2392 rc
= -1; errno
= ENOSYS
;
2395 if (!ata_smartver_state
[ata_driveno
]) {
2397 if (smart_get_version(h_ata_ioctl
) < 0) {
2398 if (!con
->permissive
) {
2399 pout("ATA/SATA driver is possibly a SCSI driver not supporting SMART.\n");
2400 pout("If this is a SCSI disk, please try adding '-d scsi'.\n");
2401 ata_smartver_state
[ata_driveno
] = 2;
2402 rc
= -1; errno
= ENOSYS
;
2407 ata_smartver_state
[ata_driveno
] = 1;
2409 rc
= smart_ioctl(h_ata_ioctl
, fd
, ®s
, data
, datasize
, port
);
2412 rc
= ata_via_scsi_miniport_smart_ioctl(h_ata_ioctl
, ®s
, data
, datasize
);
2415 rc
= ata_pass_through_ioctl(h_ata_ioctl
, ®s
, data
, datasize
);
2418 rc
= ide_pass_through_ioctl(h_ata_ioctl
, ®s
, data
, datasize
);
2421 rc
= ata_via_scsi_pass_through_ioctl(h_ata_ioctl
, ®s
, data
, datasize
);
2426 rc
= storage_predict_failure_ioctl(h_ata_ioctl
, data
);
2430 case READ_THRESHOLDS
:
2432 ata_smart_values sv
;
2433 rc
= storage_predict_failure_ioctl(h_ata_ioctl
, (char *)&sv
);
2437 // Fake zero thresholds
2438 ata_smart_thresholds_pvt
* tr
= (ata_smart_thresholds_pvt
*)data
;
2440 for (int i
= 0; i
< NUMBER_ATA_SMART_ATTRIBUTES
; i
++)
2441 tr
->chksum
-= tr
->thres_entries
[i
].id
= sv
.vendor_attributes
[i
].id
;
2445 rc
= get_identify_from_device_property(h_ata_ioctl
, (ata_identify_device
*)data
);
2452 rc
= storage_predict_failure_ioctl(h_ata_ioctl
);
2454 if (command
== STATUS_CHECK
) {
2455 regs
.bCylHighReg
= 0x2c; regs
.bCylLowReg
= 0xf4;
2461 errno
= ENOSYS
; rc
= -1;
2465 rc
= ata_via_3ware_miniport_ioctl(h_ata_ioctl
, ®s
, data
, datasize
, port
);
2468 assert(command
== CHECK_POWER_MODE
&& datasize
== 0);
2469 rc
= get_device_power_state(h_ata_ioctl
);
2471 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
2472 // spin up the drive => simulate ATA result STANDBY.
2473 regs
.bSectorCountReg
= 0x00;
2476 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
2477 // only if it is selected by the device driver => try a passthrough ioctl to get the
2478 // actual mode, if none available simulate ACTIVE/IDLE.
2480 errno
= ENOSYS
; rc
= -1;
2486 // Working ioctl found
2489 if (errno
!= ENOSYS
)
2490 // Abort on I/O error
2493 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
2497 case CHECK_POWER_MODE
:
2498 // Return power mode from SectorCountReg in data[0]
2499 data
[0] = regs
.bSectorCountReg
;
2503 // Cyl low and Cyl high unchanged means "Good SMART status"
2504 if (regs
.bCylHighReg
== SMART_CYL_HI
&& regs
.bCylLowReg
== SMART_CYL_LOW
)
2507 // These values mean "Bad SMART status"
2508 if (regs
.bCylHighReg
== 0x2c && regs
.bCylLowReg
== 0xf4)
2511 // We haven't gotten output that makes sense; print out some debugging info
2512 syserror("Error SMART Status command failed");
2513 pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE
);
2514 print_ide_regs(®s
, 1);
2525 #ifndef HAVE_ATA_IDENTIFY_IS_CACHED
2526 #error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
2529 // Return true if OS caches the ATA identify sector
2530 int ata_identify_is_cached(int fd
)
2532 // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false
2533 return (!(fd
& 0xff00) && !is_win9x());
2537 // Print not implemeted warning once
2538 static void pr_not_impl(const char * what
, int * warned
)
2543 "#######################################################################\n"
2545 "NOT IMPLEMENTED under Win32.\n"
2546 "Please contact " PACKAGE_BUGREPORT
" if\n"
2547 "you want to help in porting smartmontools to Win32.\n"
2548 "#######################################################################\n"
2554 // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c
2555 int escalade_command_interface(int /*fd*/, int disknum
, int /*escalade_type*/, smart_command_set
/*command*/, int /*select*/, char * /*data*/)
2557 static int warned
= 0;
2559 pout("Option '-d 3ware,%d' does not work on Windows.\n"
2560 "Controller port can be specified in the device name: '/dev/hd%c,%d'.\n\n",
2561 disknum
, 'a'+ata_driveno
, disknum
);
2568 // Interface to ATA devices behind Marvell chip-set based controllers. See os_linux.c
2569 int marvell_command_interface(int /*fd*/, smart_command_set
/*command*/, int /*select*/, char * /*data*/)
2571 static int warned
= 0;
2572 pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned
);
2577 // Interface to ATA devices behind HighPoint Raid controllers. See os_linux.c
2578 int highpoint_command_interface(int /*fd*/, smart_command_set
/*command*/, int /*select*/, char * /*data*/)
2580 static int warned
= 0;
2581 pr_not_impl("HighPoint raid controller command routine highpoint_command_interface()", &warned
);
2587 /////////////////////////////////////////////////////////////////////////////
2588 // ASPI Interface (for SCSI devices)
2589 /////////////////////////////////////////////////////////////////////////////
2593 #define ASPI_SENSE_SIZE 18
2595 // ASPI SCSI Request block header
2598 unsigned char cmd
; // 00: Command code
2599 unsigned char status
; // 01: ASPI status
2600 unsigned char adapter
; // 02: Host adapter number
2601 unsigned char flags
; // 03: Request flags
2602 unsigned char reserved
[4]; // 04: 0
2605 // SRB for host adapter inquiry
2608 ASPI_SRB_HEAD h
; // 00: Header
2609 unsigned char adapters
; // 08: Number of adapters
2610 unsigned char target_id
; // 09: Target ID ?
2611 char manager_id
[16]; // 10: SCSI manager ID
2612 char adapter_id
[16]; // 26: Host adapter ID
2613 unsigned char parameters
[16]; // 42: Host adapter unique parmameters
2616 // SRB for get device type
2619 ASPI_SRB_HEAD h
; // 00: Header
2620 unsigned char target_id
; // 08: Target ID
2621 unsigned char lun
; // 09: LUN
2622 unsigned char devtype
; // 10: Device type
2623 unsigned char reserved
; // 11: Reserved
2629 ASPI_SRB_HEAD h
; // 00: Header
2630 unsigned char target_id
; // 08: Target ID
2631 unsigned char lun
; // 09: LUN
2632 unsigned char reserved
[2]; // 10: Reserved
2633 unsigned long data_size
; // 12: Data alloc. lenght
2634 void * data_addr
; // 16: Data buffer pointer
2635 unsigned char sense_size
; // 20: Sense alloc. length
2636 unsigned char cdb_size
; // 21: CDB length
2637 unsigned char host_status
; // 22: Host status
2638 unsigned char target_status
; // 23: Target status
2639 void * event_handle
; // 24: Event handle
2640 unsigned char workspace
[20]; // 28: ASPI workspace
2641 unsigned char cdb
[16+ASPI_SENSE_SIZE
];
2644 // Macro to retrieve start of sense information
2645 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
2650 ASPI_SRB_HEAD h
; // Common header
2651 ASPI_SRB_INQUIRY q
; // Inquiry
2652 ASPI_SRB_DEVTYPE t
; // Device type
2653 ASPI_SRB_IO i
; // I/O
2659 #define ASPI_CMD_ADAPTER_INQUIRE 0x00
2660 #define ASPI_CMD_GET_DEVICE_TYPE 0x01
2661 #define ASPI_CMD_EXECUTE_IO 0x02
2662 #define ASPI_CMD_ABORT_IO 0x03
2665 #define ASPI_REQFLAG_DIR_TO_HOST 0x08
2666 #define ASPI_REQFLAG_DIR_TO_TARGET 0x10
2667 #define ASPI_REQFLAG_DIR_NO_XFER 0x18
2668 #define ASPI_REQFLAG_EVENT_NOTIFY 0x40
2671 #define ASPI_STATUS_IN_PROGRESS 0x00
2672 #define ASPI_STATUS_NO_ERROR 0x01
2673 #define ASPI_STATUS_ABORTED 0x02
2674 #define ASPI_STATUS_ABORT_ERR 0x03
2675 #define ASPI_STATUS_ERROR 0x04
2676 #define ASPI_STATUS_INVALID_COMMAND 0x80
2677 #define ASPI_STATUS_INVALID_ADAPTER 0x81
2678 #define ASPI_STATUS_INVALID_TARGET 0x82
2679 #define ASPI_STATUS_NO_ADAPTERS 0xE8
2681 // Adapter (host) status
2682 #define ASPI_HSTATUS_NO_ERROR 0x00
2683 #define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
2684 #define ASPI_HSTATUS_DATA_OVERRUN 0x12
2685 #define ASPI_HSTATUS_BUS_FREE 0x13
2686 #define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
2687 #define ASPI_HSTATUS_BAD_SGLIST 0x1A
2690 #define ASPI_TSTATUS_NO_ERROR 0x00
2691 #define ASPI_TSTATUS_CHECK_CONDITION 0x02
2692 #define ASPI_TSTATUS_BUSY 0x08
2693 #define ASPI_TSTATUS_RESERV_CONFLICT 0x18
2696 static HINSTANCE h_aspi_dll
; // DLL handle
2697 static UINT (* aspi_entry
)(ASPI_SRB
* srb
); // ASPI entrypoint
2698 static unsigned num_aspi_adapters
;
2701 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
2702 static DWORD aspi_dll_pid
; // PID of DLL owner to detect fork()
2703 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
2705 #define aspi_entry_valid() (!!aspi_entry)
2709 static int aspi_call(ASPI_SRB
* srb
)
2714 while (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
2715 if (++i
> 100/*10sek*/) {
2716 pout("ASPI Adapter %u: Timed out\n", srb
->h
.adapter
);
2718 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2722 if (con
->reportscsiioctl
> 1)
2723 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb
->h
.adapter
, i
);
2730 // Get ASPI entrypoint from wnaspi32.dll
2732 static FARPROC
aspi_get_address(const char * name
, int verbose
)
2735 assert(h_aspi_dll
&& h_aspi_dll
!= INVALID_HANDLE_VALUE
);
2737 if (!(addr
= GetProcAddress(h_aspi_dll
, name
))) {
2739 pout("Missing %s() in WNASPI32.DLL\n", name
);
2741 FreeLibrary(h_aspi_dll
);
2742 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2750 static int aspi_open_dll(int verbose
)
2752 UINT (*aspi_info
)(void);
2755 assert(!aspi_entry_valid());
2757 // Check structure layout
2758 assert(sizeof(ASPI_SRB_HEAD
) == 8);
2759 assert(sizeof(ASPI_SRB_INQUIRY
) == 58);
2760 assert(sizeof(ASPI_SRB_DEVTYPE
) == 12);
2761 assert(sizeof(ASPI_SRB_IO
) == 64+ASPI_SENSE_SIZE
);
2762 assert(offsetof(ASPI_SRB
,h
.cmd
) == 0);
2763 assert(offsetof(ASPI_SRB
,h
.flags
) == 3);
2764 assert(offsetof(ASPI_SRB_IO
,lun
) == 9);
2765 assert(offsetof(ASPI_SRB_IO
,data_addr
) == 16);
2766 assert(offsetof(ASPI_SRB_IO
,workspace
) == 28);
2767 assert(offsetof(ASPI_SRB_IO
,cdb
) == 48);
2769 if (h_aspi_dll
== INVALID_HANDLE_VALUE
) {
2776 if (!(h_aspi_dll
= LoadLibraryA("WNASPI32.DLL"))) {
2778 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
2779 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2783 if (con
->reportscsiioctl
> 1) {
2784 // Print full path of WNASPI32.DLL
2785 char path
[MAX_PATH
];
2786 if (!GetModuleFileName(h_aspi_dll
, path
, sizeof(path
)))
2787 strcpy(path
, "*unknown*");
2788 pout("Using ASPI interface \"%s\"\n", path
);
2791 // Get ASPI entrypoints
2792 if (!(aspi_info
= (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose
)))
2794 if (!(aspi_entry
= (UINT (*)(ASPI_SRB
*))aspi_get_address("SendASPI32Command", verbose
)))
2797 // Init ASPI manager and get number of adapters
2798 info
= (aspi_info
)();
2799 if (con
->reportscsiioctl
> 1)
2800 pout("GetASPI32SupportInfo() returns 0x%04x\n", info
);
2801 rc
= (info
>> 8) & 0xff;
2802 if (rc
== ASPI_STATUS_NO_ADAPTERS
) {
2803 num_aspi_adapters
= 0;
2805 else if (rc
== ASPI_STATUS_NO_ERROR
) {
2806 num_aspi_adapters
= info
& 0xff;
2810 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info
);
2812 FreeLibrary(h_aspi_dll
);
2813 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2818 if (con
->reportscsiioctl
)
2819 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
2822 // save PID to detect fork() in aspi_entry_valid()
2823 aspi_dll_pid
= GetCurrentProcessId();
2825 assert(aspi_entry_valid());
2830 static int aspi_io_call(ASPI_SRB
* srb
, unsigned timeout
)
2834 if (!(event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
))) {
2835 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO
;
2837 srb
->i
.event_handle
= event
;
2838 srb
->h
.flags
|= ASPI_REQFLAG_EVENT_NOTIFY
;
2839 // Start ASPI request
2841 if (((volatile ASPI_SRB
*)srb
)->h
.status
== ASPI_STATUS_IN_PROGRESS
) {
2843 DWORD rc
= WaitForSingleObject(event
, timeout
*1000L);
2844 if (rc
!= WAIT_OBJECT_0
) {
2845 if (rc
== WAIT_TIMEOUT
) {
2846 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
2847 srb
->h
.adapter
, srb
->i
.target_id
, timeout
);
2850 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
2851 (unsigned long)event
, rc
, rc
, GetLastError());
2853 // TODO: ASPI_ABORT_IO command
2855 h_aspi_dll
= (HINSTANCE
)INVALID_HANDLE_VALUE
;
2864 static int aspi_open(unsigned adapter
, unsigned id
)
2867 if (!(adapter
<= 9 && id
< 16)) {
2872 if (!aspi_entry_valid()) {
2873 if (aspi_open_dll(1/*verbose*/))
2878 if (adapter
>= num_aspi_adapters
) {
2879 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
2880 adapter
, num_aspi_adapters
, (num_aspi_adapters
!=1?"s":""));
2881 if (!is_permissive()) {
2888 memset(&srb
, 0, sizeof(srb
));
2889 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
2890 srb
.h
.adapter
= adapter
; srb
.i
.target_id
= id
;
2891 if (aspi_call(&srb
)) {
2895 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
2896 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter
, id
, srb
.h
.status
);
2897 if (!is_permissive()) {
2898 errno
= (srb
.h
.status
== ASPI_STATUS_INVALID_TARGET
? ENOENT
: EIO
);
2902 else if (con
->reportscsiioctl
)
2903 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter
, id
, srb
.t
.devtype
);
2905 return (ASPI_FDOFFSET
| ((adapter
& 0xf)<<4) | (id
& 0xf));
2909 static void aspi_close(int /*fd*/)
2911 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
2915 // Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
2918 static int aspi_scan(unsigned long * drives
)
2923 if (!aspi_entry_valid()) {
2924 if (aspi_open_dll(con
->reportscsiioctl
/*default is quiet*/))
2928 for (ad
= 0; ad
< num_aspi_adapters
; ad
++) {
2929 ASPI_SRB srb
; unsigned id
;
2932 if (con
->reportscsiioctl
)
2933 pout(" ASPI Adapter %u: Ignored\n", ad
);
2938 memset(&srb
, 0, sizeof(srb
));
2939 srb
.h
.cmd
= ASPI_CMD_ADAPTER_INQUIRE
;
2941 if (aspi_call(&srb
))
2944 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
2945 if (con
->reportscsiioctl
)
2946 pout(" ASPI Adapter %u: Status=0x%02x\n", ad
, srb
.h
.status
);
2950 if (con
->reportscsiioctl
) {
2952 for (i
= 1; i
< 16 && srb
.q
.adapter_id
[i
]; i
++)
2953 if (!(' ' <= srb
.q
.adapter_id
[i
] && srb
.q
.adapter_id
[i
] <= '~'))
2954 srb
.q
.adapter_id
[i
] = '?';
2955 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad
, srb
.q
.adapter_id
);
2958 bool ignore
= !strnicmp(srb
.q
.adapter_id
, "3ware", 5);
2960 for (id
= 0; id
<= 7; id
++) {
2962 memset(&srb
, 0, sizeof(srb
));
2963 srb
.h
.cmd
= ASPI_CMD_GET_DEVICE_TYPE
;
2964 srb
.h
.adapter
= ad
; srb
.i
.target_id
= id
;
2965 if (aspi_call(&srb
))
2967 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
2968 if (con
->reportscsiioctl
> 1)
2969 pout(" ID %u: No such device (Status=0x%02x)\n", id
, srb
.h
.status
);
2973 if (!ignore
&& srb
.t
.devtype
== 0x00/*HDD*/) {
2974 if (con
->reportscsiioctl
)
2975 pout(" ID %u: Device Type=0x%02x\n", id
, srb
.t
.devtype
);
2976 drives
[ad
>> 2] |= (1L << (((ad
& 0x3) << 3) + id
));
2979 else if (con
->reportscsiioctl
)
2980 pout(" ID %u: Device Type=0x%02x (ignored)\n", id
, srb
.t
.devtype
);
2987 /////////////////////////////////////////////////////////////////////////////
2989 // Interface to ASPI SCSI devices. See scsicmds.h and os_linux.c
2990 static int do_aspi_cmnd_io(int fd
, struct scsi_cmnd_io
* iop
, int report
)
2994 if (!aspi_entry_valid())
2996 if (!((fd
& ~0xff) == ASPI_FDOFFSET
))
2999 if (!(iop
->cmnd_len
== 6 || iop
->cmnd_len
== 10 || iop
->cmnd_len
== 12 || iop
->cmnd_len
== 16)) {
3000 pout("do_aspi_cmnd_io: bad CDB length\n");
3007 const unsigned char * ucp
= iop
->cmnd
;
3010 const int sz
= (int)sizeof(buff
);
3012 np
= scsi_get_opcode_name(ucp
[0]);
3013 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3014 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3015 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3017 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3018 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3020 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3021 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3022 (trunc
? " [only first 256 bytes shown]" : ""));
3023 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3026 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3030 memset(&srb
, 0, sizeof(srb
));
3031 srb
.h
.cmd
= ASPI_CMD_EXECUTE_IO
;
3032 srb
.h
.adapter
= ((fd
>> 4) & 0xf);
3033 srb
.i
.target_id
= (fd
& 0xf);
3035 srb
.i
.sense_size
= ASPI_SENSE_SIZE
;
3036 srb
.i
.cdb_size
= iop
->cmnd_len
;
3037 memcpy(srb
.i
.cdb
, iop
->cmnd
, iop
->cmnd_len
);
3039 switch (iop
->dxfer_dir
) {
3041 srb
.h
.flags
= ASPI_REQFLAG_DIR_NO_XFER
;
3043 case DXFER_FROM_DEVICE
:
3044 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_HOST
;
3045 srb
.i
.data_size
= iop
->dxfer_len
;
3046 srb
.i
.data_addr
= iop
->dxferp
;
3048 case DXFER_TO_DEVICE
:
3049 srb
.h
.flags
= ASPI_REQFLAG_DIR_TO_TARGET
;
3050 srb
.i
.data_size
= iop
->dxfer_len
;
3051 srb
.i
.data_addr
= iop
->dxferp
;
3054 pout("do_aspi_cmnd_io: bad dxfer_dir\n");
3058 iop
->resp_sense_len
= 0;
3059 iop
->scsi_status
= 0;
3062 if (aspi_io_call(&srb
, (iop
->timeout
? iop
->timeout
: 60))) {
3067 if (srb
.h
.status
!= ASPI_STATUS_NO_ERROR
) {
3068 if ( srb
.h
.status
== ASPI_STATUS_ERROR
3069 && srb
.i
.host_status
== ASPI_HSTATUS_NO_ERROR
3070 && srb
.i
.target_status
== ASPI_TSTATUS_CHECK_CONDITION
) {
3072 const unsigned char * sense
= ASPI_SRB_SENSE(&srb
.i
, iop
->cmnd_len
);
3073 int len
= (ASPI_SENSE_SIZE
< iop
->max_sense_len
? ASPI_SENSE_SIZE
: iop
->max_sense_len
);
3074 iop
->scsi_status
= SCSI_STATUS_CHECK_CONDITION
;
3075 if (len
> 0 && iop
->sensep
) {
3076 memcpy(iop
->sensep
, sense
, len
);
3077 iop
->resp_sense_len
= len
;
3079 pout(" >>> Sense buffer, len=%d:\n", (int)len
);
3080 dStrHex(iop
->sensep
, len
, 1);
3084 pout(" sense_key=%x asc=%x ascq=%x\n",
3085 sense
[2] & 0xf, sense
[12], sense
[13]);
3091 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb
.h
.status
, srb
.i
.host_status
, srb
.i
.target_status
);
3099 if (iop
->dxfer_dir
== DXFER_FROM_DEVICE
&& report
> 1) {
3100 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3101 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
3102 (trunc
? " [only first 256 bytes shown]" : ""));
3103 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3110 /////////////////////////////////////////////////////////////////////////////
3111 // SPT Interface (for SCSI devices and ATA devices behind SATLs)
3112 // Only supported in NT and later
3113 /////////////////////////////////////////////////////////////////////////////
3115 #define SPT_MAXDEV 64
3117 struct spt_dev_info
{
3119 int sub_addr
; // addressing disks within a RAID, for example
3122 // Private table of open devices: guaranteed zero on startup since
3123 // part of static data.
3124 static struct spt_dev_info
* spt_dev_arr
[SPT_MAXDEV
];
3127 static int spt_open(int pd_num
, int ld_num
, int tape_num
, int sub_addr
)
3130 struct spt_dev_info
* sdip
;
3134 for (k
= 0; k
< SPT_MAXDEV
; k
++)
3135 if (! spt_dev_arr
[k
])
3138 // If no free entry found, return error. We have max allowed number
3139 // of "file descriptors" already allocated.
3140 if (k
== SPT_MAXDEV
) {
3141 if (con
->reportscsiioctl
)
3142 pout("spt_open: too many open file descriptors (%d)\n",
3147 sdip
= (struct spt_dev_info
*)malloc(sizeof(struct spt_dev_info
));
3152 spt_dev_arr
[k
] = sdip
;
3153 sdip
->sub_addr
= sub_addr
;
3155 b
[sizeof(b
) - 1] = '\0';
3157 snprintf(b
, sizeof(b
) - 1, "\\\\.\\PhysicalDrive%d", pd_num
);
3158 else if (ld_num
>= 0)
3159 snprintf(b
, sizeof(b
) - 1, "\\\\.\\%c:", 'A' + ld_num
);
3160 else if (tape_num
>= 0)
3161 snprintf(b
, sizeof(b
) - 1, "\\\\.\\TAPE%d", tape_num
);
3163 if (con
->reportscsiioctl
)
3164 pout("spt_open: bad parameters\n");
3170 if ((h
= CreateFileA(b
, GENERIC_READ
|GENERIC_WRITE
,
3171 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
,
3172 OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
) {
3173 if (con
->reportscsiioctl
)
3174 pout(" %s: Open failed, Error=%ld\n", b
, GetLastError());
3178 sdip
->h_spt_ioctl
= h
;
3179 return k
+ SPT_FDOFFSET
;
3182 spt_dev_arr
[k
] = NULL
;
3188 static void spt_close(int fd
)
3190 struct spt_dev_info
* sdip
;
3191 int index
= fd
- SPT_FDOFFSET
;
3193 if ((index
< 0) || (index
>= SPT_MAXDEV
)) {
3194 if (con
->reportscsiioctl
)
3195 pout("spt_close: bad fd range\n");
3198 sdip
= spt_dev_arr
[index
];
3200 if (con
->reportscsiioctl
)
3201 pout("spt_close: fd already closed\n");
3205 spt_dev_arr
[index
] = NULL
;
3209 static int spt_scan(unsigned long * drives
)
3212 for (int i
= 0; i
<= 9; i
++) {
3213 if (get_phy_drive_type(i
) != CONTROLLER_SCSI
)
3215 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
3216 drives
[0] |= (1L << i
);
3223 #define IOCTL_SCSI_PASS_THROUGH_DIRECT \
3224 CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
3226 typedef struct _SCSI_PASS_THROUGH_DIRECT
{
3233 UCHAR SenseInfoLength
;
3235 ULONG DataTransferLength
;
3238 ULONG SenseInfoOffset
;
3240 } SCSI_PASS_THROUGH_DIRECT
;
3243 SCSI_PASS_THROUGH_DIRECT spt
;
3245 UCHAR ucSenseBuf
[64];
3246 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
;
3249 // Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
3250 static int do_spt_cmnd_io(int fd
, struct scsi_cmnd_io
* iop
, int report
)
3252 struct spt_dev_info
* sdip
;
3253 int index
= fd
- SPT_FDOFFSET
;
3254 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb
;
3257 if ((index
< 0) || (index
>= SPT_MAXDEV
)) {
3259 pout("do_spt_cmnd_io: bad fd range\n");
3262 sdip
= spt_dev_arr
[index
];
3265 pout("do_spt_cmnd_io: fd already closed\n");
3271 const unsigned char * ucp
= iop
->cmnd
;
3274 const int sz
= (int)sizeof(buff
);
3276 np
= scsi_get_opcode_name(ucp
[0]);
3277 j
= snprintf(buff
, sz
, " [%s: ", np
? np
: "<unknown opcode>");
3278 for (k
= 0; k
< (int)iop
->cmnd_len
; ++k
)
3279 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "%02x ", ucp
[k
]);
3281 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
3282 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3284 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n Outgoing "
3285 "data, len=%d%s:\n", (int)iop
->dxfer_len
,
3286 (trunc
? " [only first 256 bytes shown]" : ""));
3287 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3290 j
+= snprintf(&buff
[j
], (sz
> j
? (sz
- j
) : 0), "]\n");
3293 if (iop
->cmnd_len
> (int)sizeof(sb
.spt
.Cdb
)) {
3295 pout("do_spt_cmnd_io: cmnd_len too large\n");
3299 memset(&sb
, 0, sizeof(sb
));
3300 sb
.spt
.Length
= sizeof(SCSI_PASS_THROUGH_DIRECT
);
3301 sb
.spt
.CdbLength
= iop
->cmnd_len
;
3302 memcpy(sb
.spt
.Cdb
, iop
->cmnd
, iop
->cmnd_len
);
3303 sb
.spt
.SenseInfoLength
= sizeof(sb
.ucSenseBuf
);
3304 sb
.spt
.SenseInfoOffset
=
3305 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
, ucSenseBuf
);
3306 sb
.spt
.TimeOutValue
= (iop
->timeout
? iop
->timeout
: 60);
3307 switch (iop
->dxfer_dir
) {
3309 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_UNSPECIFIED
;
3311 case DXFER_FROM_DEVICE
:
3312 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_IN
;
3313 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3314 sb
.spt
.DataBuffer
= iop
->dxferp
;
3316 case DXFER_TO_DEVICE
:
3317 sb
.spt
.DataIn
= SCSI_IOCTL_DATA_OUT
;
3318 sb
.spt
.DataTransferLength
= iop
->dxfer_len
;
3319 sb
.spt
.DataBuffer
= iop
->dxferp
;
3322 pout("do_spt_cmnd_io: bad dxfer_dir\n");
3326 if (! DeviceIoControl(sdip
->h_spt_ioctl
, IOCTL_SCSI_PASS_THROUGH_DIRECT
,
3327 &sb
, sizeof(sb
), &sb
, sizeof(sb
), &num_out
, NULL
)) {
3328 long err
= GetLastError();
3331 pout(" IOCTL_SCSI_PASS_THROUGH_DIRECT failed, Error=%ld\n", err
);
3332 return -(err
== ERROR_INVALID_FUNCTION
? ENOSYS
: EIO
);
3335 iop
->scsi_status
= sb
.spt
.ScsiStatus
;
3336 if (SCSI_STATUS_CHECK_CONDITION
& iop
->scsi_status
) {
3337 int slen
= sb
.ucSenseBuf
[7] + 8;
3339 if (slen
> (int)sizeof(sb
.ucSenseBuf
))
3340 slen
= sizeof(sb
.ucSenseBuf
);
3341 if (slen
> (int)iop
->max_sense_len
)
3342 slen
= iop
->max_sense_len
;
3343 memcpy(iop
->sensep
, sb
.ucSenseBuf
, slen
);
3344 iop
->resp_sense_len
= slen
;
3346 if ((iop
->sensep
[0] & 0x7f) > 0x71)
3347 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3348 iop
->scsi_status
, iop
->sensep
[1] & 0xf,
3349 iop
->sensep
[2], iop
->sensep
[3]);
3351 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3352 iop
->scsi_status
, iop
->sensep
[2] & 0xf,
3353 iop
->sensep
[12], iop
->sensep
[13]);
3356 iop
->resp_sense_len
= 0;
3358 if ((iop
->dxfer_len
> 0) && (sb
.spt
.DataTransferLength
> 0))
3359 iop
->resid
= iop
->dxfer_len
- sb
.spt
.DataTransferLength
;
3363 if ((iop
->dxfer_dir
== DXFER_FROM_DEVICE
) && (report
> 1)) {
3364 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
3365 pout(" Incoming data, len=%d%s:\n", (int)iop
->dxfer_len
,
3366 (trunc
? " [only first 256 bytes shown]" : ""));
3367 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
3373 // Decides which SCSI implementation based on pseudo fd.
3374 // Declaration and explanation in scsicmds.h
3375 int do_scsi_cmnd_io(int fd
, struct scsi_cmnd_io
* iop
, int report
)
3377 if ((fd
& ~0xff) == ASPI_FDOFFSET
)
3378 return do_aspi_cmnd_io(fd
, iop
, report
);
3380 return do_spt_cmnd_io(fd
, iop
, report
);