]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_win32.cpp
Imported Upstream version 5.38
[mirror_smartmontools-debian.git] / os_win32.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * os_win32.cpp
832b75ed
GG
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
34ad0c5f 6 * Copyright (C) 2004-8 Christian Franke <smartmontools-support@lists.sourceforge.net>
832b75ed
GG
7 *
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)
11 * any later version.
12 *
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.
16 *
17 */
18
19#include "config.h"
20#include "int64.h"
21#include "atacmds.h"
22#include "extern.h"
23extern smartmonctrl * con; // con->permissive,reportataioctl
24#include "scsicmds.h"
25#include "utility.h"
26extern int64_t bytes; // malloc() byte count
27
28#include <errno.h>
29#ifdef _DEBUG
30#include <assert.h>
31#else
32#define assert(x) /**/
33#endif
34#define WIN32_LEAN_AND_MEAN
35#include <windows.h>
36#include <stddef.h> // offsetof()
37#include <io.h> // access()
38
4d59bff9
GG
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]
44
45
832b75ed 46// Needed by '-V' option (CVS versioning) of smartd/smartctl
34ad0c5f 47const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.61 2008/03/04 22:09:47 ballen4705 Exp $"
832b75ed
GG
48ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
49
50
a37e7145
GG
51// Running on Win9x/ME ?
52static inline bool is_win9x()
53{
54 return !!(GetVersion() & 0x80000000);
55}
56
57// Running on 64-bit Windows as 32-bit app ?
58static bool is_wow64()
59{
60 HMODULE hk = GetModuleHandleA("kernel32");
61 if (!hk)
62 return false;
63 BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
64 (BOOL (WINAPI *)(HANDLE, PBOOL))GetProcAddress(hk, "IsWow64Process");
65 if (!IsWow64Process_p)
66 return false;
67 BOOL w64 = FALSE;
68 if (!IsWow64Process_p(GetCurrentProcess(), &w64))
69 return false;
70 return !!w64;
71}
72
73
832b75ed
GG
74#ifndef HAVE_GET_OS_VERSION_STR
75#error define of HAVE_GET_OS_VERSION_STR missing in config.h
76#endif
77
78// Return build host and OS version as static string
79const char * get_os_version_str()
80{
a37e7145 81 static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2(64)-sp2.1")+13];
832b75ed
GG
82 char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
83 const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
84
832b75ed
GG
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));
89
a37e7145 90 OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
832b75ed
GG
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))
96 return vstr;
97 }
98
99 if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
100 return vstr;
101
a37e7145 102 const char * w;
832b75ed
GG
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"
115 : "xp-mc"); break;
116 case VER_PLATFORM_WIN32_NT <<16|0x0500| 2:
117 w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
118 : "2003r2"); break;
119 case VER_PLATFORM_WIN32_NT <<16|0x0600| 0: w = "vista"; break;
120 default: w = 0; break;
121 }
122
a37e7145 123 const char * w64 = (is_wow64() ? "(64)" : "");
832b75ed 124 if (!w)
a37e7145 125 snprintf(vptr, vlen, "-%s%lu.%lu%s",
832b75ed 126 (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
a37e7145 127 vi.dwMajorVersion, vi.dwMinorVersion, w64);
832b75ed 128 else if (vi.wServicePackMinor)
a37e7145 129 snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
832b75ed 130 else if (vi.wServicePackMajor)
a37e7145 131 snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
832b75ed 132 else
a37e7145 133 snprintf(vptr, vlen, "-%s%s", w, w64);
832b75ed
GG
134 return vstr;
135}
136
137
a37e7145
GG
138static int get_phy_drive_type(int drive);
139static int get_log_drive_type(int drive);
140
ba59cff1
GG
141#define ATARAID_FDOFFSET 0x0200
142
a37e7145 143static int ata_open(int phydrive, int logdrive, const char * options, int port);
832b75ed 144static void ata_close(int fd);
a37e7145 145static int ata_scan_win9x(unsigned long * drives);
4d59bff9
GG
146static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives);
147static const char * ata_get_def_options(void);
832b75ed 148
9ebc753d
GG
149#define TW_CLI_FDOFFSET 0x0300
150
151static int tw_cli_open(const char * name);
152static void tw_cli_close();
153
ba59cff1
GG
154#define ASPI_FDOFFSET 0x0100
155
832b75ed
GG
156static int aspi_open(unsigned adapter, unsigned id);
157static void aspi_close(int fd);
158static int aspi_scan(unsigned long * drives);
159
ba59cff1
GG
160#define SPT_FDOFFSET 0x0400
161
a37e7145 162static int spt_open(int pd_num, int ld_num, int tape_num, int sub_addr);
ba59cff1 163static void spt_close(int fd);
a37e7145 164static int spt_scan(unsigned long * drives);
ba59cff1 165
832b75ed
GG
166
167static int is_permissive()
168{
4d59bff9 169 if (!con->permissive) {
832b75ed
GG
170 pout("To continue, add one or more '-T permissive' options.\n");
171 return 0;
172 }
173 con->permissive--;
174 return 1;
175}
176
a37e7145
GG
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
180static int drive_letter(const char * s)
181{
182 return ( (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
183 && s[1] == ':'
184 && (!s[2] || ( strchr("/\\\"", s[2])
185 && (!s[3] || (s[3] == '.' && !s[4]))) ) ?
186 (s[0] & 0x1f) - 1 : -1);
187}
188
189// Skip trailing "/dev/", do not allow "/dev/X:"
832b75ed
GG
190static const char * skipdev(const char * s)
191{
a37e7145 192 return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
832b75ed
GG
193}
194
195
196// tries to guess device type given the name (a path). See utility.h
197// for return values.
198int guess_device_type (const char * dev_name)
199{
200 dev_name = skipdev(dev_name);
a37e7145
GG
201 if (!strncmp(dev_name, "scsi", 4))
202 return CONTROLLER_SCSI;
203 if (is_win9x())
204 return CONTROLLER_ATA;
832b75ed
GG
205 if (!strncmp(dev_name, "hd", 2))
206 return CONTROLLER_ATA;
9ebc753d
GG
207 if (!strncmp(dev_name, "tw_cli", 6))
208 return CONTROLLER_ATA;
a37e7145 209 if (!strncmp(dev_name, "st", 2))
ba59cff1 210 return CONTROLLER_SCSI;
a37e7145 211 if (!strncmp(dev_name, "nst", 3))
ba59cff1
GG
212 return CONTROLLER_SCSI;
213 if (!strncmp(dev_name, "tape", 4))
214 return CONTROLLER_SCSI;
a37e7145
GG
215 int logdrive = drive_letter(dev_name);
216 if (logdrive >= 0) {
217 int type = get_log_drive_type(logdrive);
218 return (type != CONTROLLER_UNKNOWN ? type : CONTROLLER_SCSI);
219 }
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');
223 int phydrive = -1;
224 if (sscanf(dev_name, "pd%d", &phydrive) == 1 && phydrive >= 0)
225 return get_phy_drive_type(phydrive);
832b75ed
GG
226 return CONTROLLER_UNKNOWN;
227}
228
229
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.
234int make_device_names (char*** devlist, const char* type)
235{
236 unsigned long drives[3];
4d59bff9
GG
237 int rdriveno[2];
238 unsigned long rdrives[2];
832b75ed
GG
239 int i, j, n, nmax, sz;
240 const char * path;
241
242 drives[0] = drives[1] = drives[2] = 0;
4d59bff9
GG
243 rdriveno[0] = rdriveno[1] = -1;
244 rdrives[0] = rdrives[1] = 0;
245
a37e7145 246 bool win9x = is_win9x();
832b75ed
GG
247 if (!strcmp(type, "ATA")) {
248 // bit i set => drive i present
a37e7145
GG
249 if (win9x) {
250 n = ata_scan_win9x(drives);
251 path = "/dev/hda";
252 }
253 else {
254 n = ata_scan(drives, rdriveno, rdrives);
255 path = "/dev/sda";
256 }
832b75ed
GG
257 nmax = 10;
258 }
259 else if (!strcmp(type, "SCSI")) {
a37e7145
GG
260 if (win9x) {
261 // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
262 n = aspi_scan(drives);
263 path = "/dev/scsi00";
264 nmax = 10*8;
265 }
266 else {
267 // bit i set => drive i present
268 n = spt_scan(drives);
269 path = "/dev/sda";
270 nmax = 10;
271 }
832b75ed
GG
272 }
273 else
274 return -1;
275
276 if (n <= 0)
277 return 0;
278
279 // Alloc devlist
280 sz = n * sizeof(char **);
281 *devlist = (char **)malloc(sz); bytes += sz;
282
283 // Add devices
4d59bff9 284 for (i = j = 0; i < n; ) {
832b75ed
GG
285 while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f))))
286 j++;
287 assert(j < nmax);
4d59bff9
GG
288
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)))
294 continue;
295 char rpath[20];
a37e7145 296 sprintf(rpath, "/dev/sd%c,%u", 'a'+j, pi);
4d59bff9
GG
297 sz = strlen(rpath)+1;
298 char * s = (char *)malloc(sz); bytes += sz;
299 strcpy(s, rpath);
300 (*devlist)[i++] = s;
301 }
832b75ed
GG
302 }
303 else {
4d59bff9
GG
304 sz = strlen(path)+1;
305 char * s = (char *)malloc(sz); bytes += sz;
306 strcpy(s, path);
307
308 if (nmax <= 10) {
309 assert(j <= 9);
310 s[sz-2] += j; // /dev/hd[a-j]
311 }
312 else {
313 assert((j >> 3) <= 9);
314 s[sz-3] += (j >> 3); // /dev/scsi[0-9].....
315 s[sz-2] += (j & 0x7); // .....[0-7]
316 }
317 (*devlist)[i++] = s;
832b75ed 318 }
832b75ed
GG
319 j++;
320 }
4d59bff9 321
832b75ed
GG
322 return n;
323}
324
325
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
4d59bff9 329// within this file (see os_freebsd.cpp for an example).
832b75ed
GG
330int deviceopen(const char * pathname, char *type)
331{
832b75ed 332 pathname = skipdev(pathname);
ba59cff1 333 int len = strlen(pathname);
832b75ed
GG
334
335 if (!strcmp(type, "ATA")) {
1953ff6d
GG
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
a37e7145
GG
339 && ((n1 == len && !options[0]) || n2 == len) ) {
340 return ata_open(drive[0] - 'a', -1, options, -1);
4d59bff9 341 }
1953ff6d 342 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
4d59bff9
GG
343 drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
344 unsigned port = ~0;
1953ff6d 345 if ( sscanf(pathname, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
a37e7145
GG
346 && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) {
347 return ata_open(drive[0] - 'a', -1, options, port);
348 }
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);
354 }
355 // [a-zA-Z]: => Physical drive behind logical drive 0-25
356 int logdrive = drive_letter(pathname);
357 if (logdrive >= 0) {
358 return ata_open(-1, logdrive, "", -1);
832b75ed 359 }
9ebc753d
GG
360 // tw_cli/... => Parse tw_cli output
361 if (!strncmp(pathname, "tw_cli/", 7)) {
362 return tw_cli_open(pathname+7);
363 }
ba59cff1
GG
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) {
4d59bff9 368 return aspi_open(adapter, id);
832b75ed 369 }
a37e7145 370 // sd[a-z],N => Physical drive 0-25, RAID port N
ba59cff1
GG
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)) ) {
a37e7145 374 return spt_open(drive[0] - 'a', -1, -1, sub_addr);
ba59cff1
GG
375 }
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))) {
a37e7145 380 return spt_open(pd_num, -1, -1, sub_addr);
ba59cff1 381 }
a37e7145
GG
382 // [a-zA-Z]: => Physical drive behind logical drive 0-25
383 int logdrive = drive_letter(pathname);
384 if (logdrive >= 0) {
385 return spt_open(-1, logdrive, -1, -1);
386 }
387 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
ba59cff1 388 int tape_num = -1; n1 = -1;
a37e7145
GG
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);
391 }
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);
395 }
396 // tape<m> => tape drive <m>
397 tape_num = -1; n1 = -1;
ba59cff1 398 if (sscanf(pathname, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
a37e7145 399 return spt_open(-1, -1, tape_num, -1);
ba59cff1 400 }
832b75ed 401 }
4d59bff9
GG
402
403 errno = EINVAL;
832b75ed
GG
404 return -1;
405}
406
407
408// Like close(). Acts only on handles returned by above function.
832b75ed
GG
409int deviceclose(int fd)
410{
ba59cff1 411 if ((fd & 0xff00) == ASPI_FDOFFSET)
832b75ed 412 aspi_close(fd);
ba59cff1
GG
413 else if (fd >= SPT_FDOFFSET)
414 spt_close(fd);
9ebc753d
GG
415 else if (fd == TW_CLI_FDOFFSET)
416 tw_cli_close();
ba59cff1
GG
417 else
418 ata_close(fd);
832b75ed
GG
419 return 0;
420}
421
422
423// print examples for smartctl
424void 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"
433#else
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"
438#endif
439 " smartctl -a /dev/scsi21\n"
440 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
ba59cff1
GG
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"
4d59bff9
GG
447 " smartctl -A /dev/hdb,3\n"
448 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
9ebc753d
GG
449 " smartctl -A /dev/tw_cli/c0/p1\n"
450 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
4d59bff9
GG
451 "\n"
452 " ATA SMART access methods and ordering may be specified by modifiers\n"
a37e7145 453 " following the device name: /dev/hdX:[saicm], where\n"
4d59bff9 454 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
a37e7145 455 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
1953ff6d
GG
456 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
457 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
832b75ed
GG
458 );
459}
460
461
462/////////////////////////////////////////////////////////////////////////////
463// ATA Interface
464/////////////////////////////////////////////////////////////////////////////
465
466// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
467
832b75ed
GG
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))
472
473#define FILE_DEVICE_DISK 7
474#define IOCTL_DISK_BASE FILE_DEVICE_DISK
475
476#define SMART_GET_VERSION \
477 CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
478
4d59bff9
GG
479#define SMART_SEND_DRIVE_COMMAND \
480 CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
481
832b75ed
GG
482#define SMART_RCV_DRIVE_DATA \
483 CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
484
4d59bff9
GG
485ASSERT_CONST(SMART_GET_VERSION , 0x074080);
486ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
487ASSERT_CONST(SMART_RCV_DRIVE_DATA , 0x07c088);
832b75ed
GG
488
489#define SMART_CYL_LOW 0x4F
490#define SMART_CYL_HI 0xC2
491
4d59bff9 492
832b75ed
GG
493#pragma pack(1)
494
495typedef struct _GETVERSIONOUTPARAMS {
496 UCHAR bVersion;
497 UCHAR bRevision;
498 UCHAR bReserved;
499 UCHAR bIDEDeviceMap;
500 ULONG fCapabilities;
501 ULONG dwReserved[4];
502} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
503
4d59bff9
GG
504ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24);
505
506
507#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
508
509typedef struct _GETVERSIONINPARAMS_EX {
510 BYTE bVersion;
511 BYTE bRevision;
512 BYTE bReserved;
513 BYTE bIDEDeviceMap;
514 DWORD fCapabilities;
515 DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map
516 WORD wIdentifier; // Vendor specific identifier
517 WORD wControllerId; // 3ware specific: Controller ID (0,1,...)
518 ULONG dwReserved[2];
519} GETVERSIONINPARAMS_EX, *PGETVERSIONINPARAMS_EX, *LPGETVERSIONINPARAMS_EX;
520
521ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONOUTPARAMS));
522
523
832b75ed
GG
524typedef struct _IDEREGS {
525 UCHAR bFeaturesReg;
526 UCHAR bSectorCountReg;
527 UCHAR bSectorNumberReg;
528 UCHAR bCylLowReg;
529 UCHAR bCylHighReg;
530 UCHAR bDriveHeadReg;
531 UCHAR bCommandReg;
532 UCHAR bReserved;
533} IDEREGS, *PIDEREGS, *LPIDEREGS;
534
535typedef struct _SENDCMDINPARAMS {
536 ULONG cBufferSize;
537 IDEREGS irDriveRegs;
538 UCHAR bDriveNumber;
539 UCHAR bReserved[3];
540 ULONG dwReserved[4];
541 UCHAR bBuffer[1];
542} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
543
4d59bff9
GG
544ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
545
546typedef struct _SENDCMDINPARAMS_EX {
547 DWORD cBufferSize;
548 IDEREGS irDriveRegs;
549 BYTE bDriveNumber;
550 BYTE bPortNumber; // 3ware specific: port number
551 WORD wIdentifier; // Vendor specific identifier
552 DWORD dwReserved[4];
553 BYTE bBuffer[1];
554} SENDCMDINPARAMS_EX, *PSENDCMDINPARAMS_EX, *LPSENDCMDINPARAMS_EX;
555
556ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
557
558
832b75ed
GG
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
571*/
572
573typedef struct _DRIVERSTATUS {
574 UCHAR bDriverError;
575 UCHAR bIDEError;
576 UCHAR bReserved[2];
577 ULONG dwReserved[2];
578} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
579
580typedef struct _SENDCMDOUTPARAMS {
581 ULONG cBufferSize;
582 DRIVERSTATUS DriverStatus;
583 UCHAR bBuffer[1];
584} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
585
4d59bff9
GG
586ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
587
832b75ed
GG
588#pragma pack()
589
590
591/////////////////////////////////////////////////////////////////////////////
592
593static void print_ide_regs(const IDEREGS * r, int out)
594{
a37e7145 595 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
832b75ed
GG
596 (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
597 r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
598}
599
600static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
601{
602 pout(" Input : "); print_ide_regs(ri, 0);
603 if (ro) {
604 pout(" Output: "); print_ide_regs(ro, 1);
605 }
606}
607
608/////////////////////////////////////////////////////////////////////////////
609
4d59bff9
GG
610// call SMART_GET_VERSION, return device map or -1 on error
611
a37e7145 612static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
4d59bff9 613{
a37e7145 614 GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
4d59bff9
GG
615 const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
616 DWORD num_out;
617
4d59bff9
GG
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());
622 errno = ENOSYS;
623 return -1;
624 }
625 assert(num_out == sizeof(GETVERSIONOUTPARAMS));
626
4d59bff9
GG
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);
635 }
636
a37e7145
GG
637 if (ata_version_ex)
638 *ata_version_ex = vers_ex;
639
4d59bff9
GG
640 // TODO: Check vers.fCapabilities here?
641 return vers.bIDEDeviceMap;
642}
643
644
832b75ed
GG
645// call SMART_* ioctl
646
4d59bff9 647static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
832b75ed
GG
648{
649 SENDCMDINPARAMS inpar;
4d59bff9
GG
650 SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
651
832b75ed
GG
652 unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
653 const SENDCMDOUTPARAMS * outpar;
654 DWORD code, num_out;
655 unsigned int size_out;
656 const char * name;
657
832b75ed
GG
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;
663
4d59bff9
GG
664 if (port >= 0) {
665 // Set RAID port
666 inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
667 inpar_ex.bPortNumber = port;
668 }
669
832b75ed
GG
670 assert(datasize == 0 || datasize == 512);
671 if (datasize) {
672 code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
673 inpar.cBufferSize = size_out = 512;
674 }
675 else {
676 code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
a37e7145 677 if (regs->bFeaturesReg == ATA_SMART_STATUS)
832b75ed
GG
678 size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
679 // Note: cBufferSize must be 0 on Win9x
680 else
681 size_out = 0;
682 }
683
684 memset(&outbuf, 0, sizeof(outbuf));
685
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);
693 }
a37e7145
GG
694 errno = ( err == ERROR_INVALID_FUNCTION/*9x*/
695 || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
696 || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
832b75ed
GG
697 return -1;
698 }
699 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
700
701 outpar = (const SENDCMDOUTPARAMS *)outbuf;
702
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);
708 }
4d59bff9 709 errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
832b75ed
GG
710 return -1;
711 }
712
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));
718 }
719
720 if (datasize)
721 memcpy(data, outpar->bBuffer, 512);
4d59bff9
GG
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;
729 }
730 }
832b75ed
GG
731
732 return 0;
733}
734
735
736/////////////////////////////////////////////////////////////////////////////
737
4d59bff9 738// IDE PASS THROUGH (2000, XP, undocumented)
832b75ed
GG
739//
740// Based on WinATA.cpp, 2002 c't/Matthias Withopf
741// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
742
743#define FILE_DEVICE_CONTROLLER 4
744#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
745
746#define IOCTL_IDE_PASS_THROUGH \
747 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
748
4d59bff9
GG
749ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
750
832b75ed
GG
751#pragma pack(1)
752
753typedef struct {
754 IDEREGS IdeReg;
755 ULONG DataBufferSize;
756 UCHAR DataBuffer[1];
757} ATA_PASS_THROUGH;
758
4d59bff9
GG
759ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
760
832b75ed
GG
761#pragma pack()
762
763
764/////////////////////////////////////////////////////////////////////////////
765
766static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
767{
ba59cff1
GG
768 if (datasize > 512) {
769 errno = EINVAL;
770 return -1;
771 }
832b75ed
GG
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);
774 DWORD num_out;
775 const unsigned char magic = 0xcf;
832b75ed
GG
776
777 if (!buf) {
778 errno = ENOMEM;
779 return -1;
780 }
781
782 buf->IdeReg = *regs;
783 buf->DataBufferSize = datasize;
784 if (datasize)
785 buf->DataBuffer[0] = magic;
786
787 if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
788 buf, size, buf, size, &num_out, NULL)) {
789 long err = GetLastError();
4d59bff9 790 if (con->reportataioctl) {
832b75ed 791 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
4d59bff9
GG
792 print_ide_regs_io(regs, NULL);
793 }
832b75ed 794 VirtualFree(buf, 0, MEM_RELEASE);
a37e7145 795 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
832b75ed
GG
796 return -1;
797 }
798
799 // Check ATA status
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);
804 }
805 VirtualFree(buf, 0, MEM_RELEASE);
806 errno = EIO;
807 return -1;
808 }
809
810 // Check and copy data
811 if (datasize) {
812 if ( num_out != size
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);
818 }
819 VirtualFree(buf, 0, MEM_RELEASE);
820 errno = EIO;
821 return -1;
822 }
823 memcpy(data, buf->DataBuffer, datasize);
824 }
825
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);
830 }
831 *regs = buf->IdeReg;
832
833 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
834 VirtualFree(buf, 0, MEM_RELEASE);
835 return 0;
836}
837
838
4d59bff9
GG
839/////////////////////////////////////////////////////////////////////////////
840
841// ATA PASS THROUGH (Win2003, XP SP2)
842
843#define IOCTL_ATA_PASS_THROUGH \
844 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
845
846ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
847
848typedef struct _ATA_PASS_THROUGH_EX {
849 USHORT Length;
850 USHORT AtaFlags;
851 UCHAR PathId;
852 UCHAR TargetId;
853 UCHAR Lun;
854 UCHAR ReservedAsUchar;
855 ULONG DataTransferLength;
856 ULONG TimeOutValue;
857 ULONG ReservedAsUlong;
858 ULONG/*_PTR*/ DataBufferOffset;
859 UCHAR PreviousTaskFile[8];
860 UCHAR CurrentTaskFile[8];
861} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX;
862
863ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40);
864
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
869
870
871/////////////////////////////////////////////////////////////////////////////
872
873static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
874{
875 typedef struct {
876 ATA_PASS_THROUGH_EX apt;
877 ULONG Filler;
878 UCHAR ucDataBuf[512];
879 } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
880
4d59bff9
GG
881 const unsigned char magic = 0xcf;
882
ba59cff1 883 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
4d59bff9
GG
884 ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
885 //ab.apt.PathId = 0;
886 //ab.apt.TargetId = 0;
887 //ab.apt.Lun = 0;
888 ab.apt.TimeOutValue = 10;
ba59cff1 889 unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
4d59bff9
GG
890 ab.apt.DataBufferOffset = size;
891
ba59cff1
GG
892 if (datasize > 0) {
893 if (datasize > (int)sizeof(ab.ucDataBuf)) {
4d59bff9
GG
894 errno = EINVAL;
895 return -1;
896 }
897 ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
898 ab.apt.DataTransferLength = datasize;
899 size += datasize;
900 ab.ucDataBuf[0] = magic;
901 }
ba59cff1
GG
902 else if (datasize < 0) {
903 if (-datasize > (int)sizeof(ab.ucDataBuf)) {
904 errno = EINVAL;
905 return -1;
906 }
907 ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
908 ab.apt.DataTransferLength = -datasize;
909 size += -datasize;
910 memcpy(ab.ucDataBuf, data, -datasize);
911 }
4d59bff9 912 else {
ba59cff1
GG
913 assert(ab.apt.AtaFlags == 0);
914 assert(ab.apt.DataTransferLength == 0);
4d59bff9
GG
915 }
916
917 assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
ba59cff1 918 IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
4d59bff9
GG
919 *ctfregs = *regs;
920
ba59cff1 921 DWORD num_out;
4d59bff9
GG
922 if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
923 &ab, size, &ab, size, &num_out, NULL)) {
924 long err = GetLastError();
925 if (con->reportataioctl) {
ba59cff1 926 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
4d59bff9
GG
927 print_ide_regs_io(regs, NULL);
928 }
a37e7145 929 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
4d59bff9
GG
930 return -1;
931 }
932
ba59cff1
GG
933 // Check ATA status
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);
938 }
939 errno = EIO;
940 return -1;
941 }
942
4d59bff9 943 // Check and copy data
ba59cff1 944 if (datasize > 0) {
4d59bff9
GG
945 if ( num_out != size
946 || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
947 if (con->reportataioctl) {
ba59cff1 948 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);
4d59bff9
GG
949 print_ide_regs_io(regs, ctfregs);
950 }
951 errno = EIO;
952 return -1;
953 }
954 memcpy(data, ab.ucDataBuf, datasize);
955 }
956
957 if (con->reportataioctl > 1) {
ba59cff1 958 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
4d59bff9
GG
959 print_ide_regs_io(regs, ctfregs);
960 }
961 *regs = *ctfregs;
962
963 return 0;
964}
965
832b75ed
GG
966
967/////////////////////////////////////////////////////////////////////////////
968
4d59bff9 969// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
832b75ed 970
832b75ed
GG
971#define IOCTL_SCSI_PASS_THROUGH \
972 CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
973
4d59bff9
GG
974ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
975
832b75ed
GG
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
981
982typedef struct _SCSI_PASS_THROUGH {
983 USHORT Length;
984 UCHAR ScsiStatus;
985 UCHAR PathId;
986 UCHAR TargetId;
987 UCHAR Lun;
988 UCHAR CdbLength;
989 UCHAR SenseInfoLength;
990 UCHAR DataIn;
991 ULONG DataTransferLength;
992 ULONG TimeOutValue;
993 ULONG/*_PTR*/ DataBufferOffset;
994 ULONG SenseInfoOffset;
995 UCHAR Cdb[16];
996} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
997
4d59bff9
GG
998ASSERT_SIZEOF(SCSI_PASS_THROUGH, 44);
999
832b75ed
GG
1000
1001/////////////////////////////////////////////////////////////////////////////
1002
1003static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
1004{
1005 typedef struct {
1006 SCSI_PASS_THROUGH spt;
1007 ULONG Filler;
1008 UCHAR ucSenseBuf[32];
1009 UCHAR ucDataBuf[512];
1010 } SCSI_PASS_THROUGH_WITH_BUFFERS;
1011
1012 SCSI_PASS_THROUGH_WITH_BUFFERS sb;
1013 IDEREGS * cdbregs;
1014 unsigned int size;
1015 DWORD num_out;
1016 const unsigned char magic = 0xcf;
1017
832b75ed
GG
1018 memset(&sb, 0, sizeof(sb));
1019 sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
1020 //sb.spt.PathId = 0;
1021 sb.spt.TargetId = 1;
1022 //sb.spt.Lun = 0;
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;
1028
1029 if (datasize) {
1030 if (datasize > sizeof(sb.ucDataBuf)) {
1031 errno = EINVAL;
1032 return -1;
1033 }
1034 sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
1035 sb.spt.DataTransferLength = datasize;
1036 size += datasize;
1037 sb.ucDataBuf[0] = magic;
1038 }
1039 else {
1040 sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
1041 //sb.spt.DataTransferLength = 0;
1042 }
1043
1044 // Use pseudo SCSI command followed by registers
1045 sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
1046 cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
1047 *cdbregs = *regs;
1048
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);
a37e7145 1054 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
832b75ed
GG
1055 return -1;
1056 }
1057
1058 // Cannot check ATA status, because command does not return IDEREGS
1059
1060 // Check and copy data
1061 if (datasize) {
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);
1067 }
1068 errno = EIO;
1069 return -1;
1070 }
1071 memcpy(data, sb.ucDataBuf, datasize);
1072 }
1073
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);
1077 }
1078 return 0;
1079}
1080
ba59cff1
GG
1081
1082/////////////////////////////////////////////////////////////////////////////
1083
a37e7145
GG
1084// SMART IOCTL via SCSI MINIPORT ioctl
1085
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)
ba59cff1
GG
1090
1091#define IOCTL_SCSI_MINIPORT \
1092 CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
1093
1094ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
1095
1096typedef struct _SRB_IO_CONTROL {
1097 ULONG HeaderLength;
1098 UCHAR Signature[8];
1099 ULONG Timeout;
1100 ULONG ControlCode;
1101 ULONG ReturnCode;
1102 ULONG Length;
1103} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
1104
1105ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
1106
a37e7145
GG
1107#define FILE_DEVICE_SCSI 0x001b
1108
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)
1122
1123/////////////////////////////////////////////////////////////////////////////
1124
1125static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
1126{
1127 // Select code
1128 DWORD code = 0; const char * name = 0;
1129 if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
1130 code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
1131 }
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;
1155 }
1156 if (!code) {
1157 errno = ENOSYS;
1158 return -1;
1159 }
1160
1161 // Set SRB
1162 struct {
1163 SRB_IO_CONTROL srbc;
1164 union {
1165 SENDCMDINPARAMS in;
1166 SENDCMDOUTPARAMS out;
1167 } params;
1168 char space[512-1];
1169 } sb;
1170 ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
1171 memset(&sb, 0, sizeof(sb));
1172
1173 unsigned size;
1174 if (datasize > 0) {
1175 if (datasize > (int)sizeof(sb.space)+1) {
1176 errno = EINVAL;
1177 return -1;
1178 }
1179 size = datasize;
1180 }
1181 else if (datasize < 0) {
1182 if (-datasize > (int)sizeof(sb.space)+1) {
1183 errno = EINVAL;
1184 return -1;
1185 }
1186 size = -datasize;
1187 memcpy(sb.params.in.bBuffer, data, size);
1188 }
1189 else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
1190 size = sizeof(IDEREGS);
1191 else
1192 size = 0;
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;
1201
1202 // Call miniport ioctl
1203 size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
1204 DWORD num_out;
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);
1211 }
1212 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
1213 return -1;
1214 }
1215
1216 // Check result
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);
1221 }
1222 errno = EIO;
1223 return -1;
1224 }
1225
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);
1231 }
1232 errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
1233 return -1;
1234 }
1235
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));
1241 }
1242
1243 if (datasize > 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);
1247
1248 return 0;
1249}
1250
1251
ba59cff1
GG
1252/////////////////////////////////////////////////////////////////////////////
1253
a37e7145
GG
1254// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1255
ba59cff1
GG
1256static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
1257{
1258 struct {
1259 SRB_IO_CONTROL srbc;
1260 IDEREGS regs;
1261 UCHAR buffer[512];
1262 } sb;
1263 ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
1264
1265 if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
1266 errno = EINVAL;
1267 return -1;
1268 }
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);
1276 sb.regs = *regs;
1277 sb.regs.bReserved = port;
1278
1279 DWORD num_out;
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);
1286 }
1287 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
1288 return -1;
1289 }
1290
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);
1295 }
1296 errno = EIO;
1297 return -1;
1298 }
1299
1300 // Copy data
1301 if (datasize > 0)
1302 memcpy(data, sb.buffer, datasize);
1303
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);
1307 }
1308 *regs = sb.regs;
1309
1310 return 0;
1311}
1312
1313
1314/////////////////////////////////////////////////////////////////////////////
1315
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.
1318
1319static int update_3ware_devicemap_ioctl(HANDLE hdevice)
1320{
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;
1328 srbc.Length = 0;
1329
1330 DWORD num_out;
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);
1337 return -1;
1338 }
1339 if (srbc.ReturnCode) {
1340 if (con->reportataioctl)
1341 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
1342 errno = EIO;
1343 return -1;
1344 }
1345 if (con->reportataioctl > 1)
1346 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1347 return 0;
1348}
1349
1350
9ebc753d
GG
1351
1352/////////////////////////////////////////////////////////////////////////////
1353
1354// Routines for pseudo device /dev/tw_cli/*
1355// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1356
1357
1358// Get clipboard data
1359
1360static int get_clipboard(char * data, int datasize)
1361{
1362 if (!OpenClipboard(NULL))
1363 return -1;
1364 HANDLE h = GetClipboardData(CF_TEXT);
1365 if (!h) {
1366 CloseClipboard();
1367 return 0;
1368 }
1369 const void * p = GlobalLock(h);
1370 int n = GlobalSize(h);
1371 if (n > datasize)
1372 n = datasize;
1373 memcpy(data, p, n);
1374 GlobalFree(h);
1375 CloseClipboard();
1376 return n;
1377}
1378
1379
1380// Run a command, write stdout to dataout
1381// TODO: Combine with daemon_win32.cpp:daemon_spawn()
1382
1383static int run_cmd(const char * cmd, char * dataout, int outsize)
1384{
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))
1389 return -1;
1390 HANDLE self = GetCurrentProcess();
1391 HANDLE pipe_out_r;
1392 if (!DuplicateHandle(self, h, self, &pipe_out_r,
1393 GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
1394 CloseHandle(pipe_out_w);
1395 return -1;
1396 }
1397 HANDLE pipe_err_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);
1401 return -1;
1402 }
1403
1404 // Create process
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;
1410 if (!CreateProcess(
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);
1416 return -1;
1417 }
1418 CloseHandle(pi.hThread);
1419 CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
1420
1421 // Copy stdout to output buffer
1422 int i = 0;
1423 while (i < outsize) {
1424 DWORD num_read;
1425 if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
1426 break;
1427 i += num_read;
1428 }
1429 CloseHandle(pipe_out_r);
1430 // Wait for process
1431 WaitForSingleObject(pi.hProcess, INFINITE);
1432 CloseHandle(pi.hProcess);
1433 return i;
1434}
1435
1436
1437static const char * findstr(const char * str, const char * sub)
1438{
1439 const char * s = strstr(str, sub);
1440 return (s ? s+strlen(sub) : "");
1441}
1442
1443
1444static void copy_swapped(unsigned char * dest, const char * src, int destsize)
1445{
1446 int srclen = strcspn(src, "\r\n");
1447 int i;
1448 for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
1449 dest[i] = src[i+1]; dest[i+1] = src[i];
1450 }
1451 if (i < destsize-1 && i < srclen)
1452 dest[i+1] = src[i];
1453}
1454
1455
1456static ata_identify_device * tw_cli_identbuf = 0;
1457static ata_smart_values * tw_cli_smartbuf = 0;
1458
1459static int tw_cli_open(const char * name)
1460{
1461 // Read tw_cli or 3DM browser output into buffer
1462 char buffer[4096];
1463 int size = -1, n1 = -1;
1464 if (!strcmp(name, "clip")) { // tw_cli/clip => read clipboard
1465 size = get_clipboard(buffer, sizeof(buffer));
1466 }
1467 else if (!strcmp(name, "stdin")) { // tw_cli/stdin => read stdin
1468 size = fread(buffer, 1, sizeof(buffer), stdin);
1469 }
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"
1472 char cmd[100];
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));
1477 }
1478 else {
1479 errno = EINVAL; return -1;
1480 }
1481
1482 if (con->reportataioctl > 1)
1483 pout("tw_cli/%s: Read %d bytes\n", name, size);
1484 if (size <= 0) {
1485 errno = ENOENT; return -1;
1486 }
1487 if (size >= (int)sizeof(buffer)) {
1488 errno = EIO; return -1;
1489 }
1490 buffer[size] = 0;
1491 if (con->reportataioctl > 1)
1492 pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
1493
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);
1503 if (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
1507 }
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
1511
1512 // Parse smart data hex dump
1513 const char * s = findstr(buffer, "Drive Smart Data:");
1514 if (!*s) {
1515 s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
1516 if (*s) {
1517 const char * s1 = findstr(s, "<td class"); // html version
1518 if (*s1)
1519 s = s1;
1520 s += strcspn(s, "\r\n");
1521 }
1522 else
1523 s = buffer; // try raw hex dump without header
1524 }
1525 unsigned char * sd = (unsigned char *)malloc(512);
1526 int i = 0;
1527 for (;;) {
1528 unsigned x = ~0; int n = -1;
1529 if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
1530 break;
1531 sd[i] = (unsigned char)x;
1532 if (!(++i < 512 && n > 0))
1533 break;
1534 s += n;
1535 if (*s == '<') // "<br>"
1536 s += strcspn(s, "\r\n");
1537 }
1538 if (i < 512) {
1539 free(sd);
1540 if (!id->model[1]) {
1541 // No useful data found
1542 free(id);
1543 char * err = strstr(buffer, "Error:");
1544 if (!err)
1545 err = strstr(buffer, "error :");
1546 if (err) {
1547 // Print tw_cli error message
1548 err[strcspn(err, "\r\n")] = 0;
1549 pout("%s\n", err);
1550 }
1551 errno = EIO;
1552 return -1;
1553 }
1554 sd = 0;
1555 }
1556
1557 tw_cli_identbuf = id;
1558 tw_cli_smartbuf = (ata_smart_values *)sd;
1559 return TW_CLI_FDOFFSET;
1560}
1561
1562
1563static void tw_cli_close()
1564{
1565 if (tw_cli_identbuf) {
1566 free(tw_cli_identbuf); tw_cli_identbuf = 0;
1567 }
1568 if (tw_cli_smartbuf) {
1569 free(tw_cli_smartbuf); tw_cli_smartbuf = 0;
1570 }
1571}
1572
1573
1574static int tw_cli_command_interface(smart_command_set command, int /*select*/, char * data)
1575{
1576 switch (command) {
1577 case IDENTIFY:
1578 if (!tw_cli_identbuf)
1579 break;
1580 memcpy(data, tw_cli_identbuf, 512);
1581 return 0;
1582 case READ_VALUES:
1583 if (!tw_cli_smartbuf)
1584 break;
1585 memcpy(data, tw_cli_smartbuf, 512);
1586 return 0;
1587 case READ_THRESHOLDS:
1588 if (!tw_cli_smartbuf)
1589 break;
1590 // Fake zero thresholds
1591 {
1592 const ata_smart_values * sv = tw_cli_smartbuf;
1593 ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
1594 memset(tr, 0, 512);
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;
1599 }
1600 return 0;
1601 case ENABLE:
1602 case STATUS:
1603 case STATUS_CHECK: // Fake "good" SMART status
1604 return 0;
1605 default:
1606 break;
1607 }
1608 // Arrive here for all unsupported commands
1609 errno = ENOSYS;
1610 return -1;
1611}
1612
1613
a37e7145
GG
1614/////////////////////////////////////////////////////////////////////////////
1615
1616// IOCTL_STORAGE_QUERY_PROPERTY
1617
1618#define FILE_DEVICE_MASS_STORAGE 0x0000002d
1619#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
1620#define FILE_ANY_ACCESS 0
1621
1622#define IOCTL_STORAGE_QUERY_PROPERTY \
1623 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
1624
1625typedef enum _STORAGE_BUS_TYPE {
1626 BusTypeUnknown = 0x00,
1627 BusTypeScsi = 0x01,
1628 BusTypeAtapi = 0x02,
1629 BusTypeAta = 0x03,
1630 BusType1394 = 0x04,
1631 BusTypeSsa = 0x05,
1632 BusTypeFibre = 0x06,
1633 BusTypeUsb = 0x07,
1634 BusTypeRAID = 0x08,
1635 BusTypeiScsi = 0x09,
1636 BusTypeSas = 0x0A,
1637 BusTypeSata = 0x0B,
1638 BusTypeSd = 0x0C,
1639 BusTypeMmc = 0x0D,
1640 BusTypeMax = 0x0E,
1641 BusTypeMaxReserved = 0x7F
1642} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
1643
1644typedef struct _STORAGE_DEVICE_DESCRIPTOR {
1645 ULONG Version;
1646 ULONG Size;
1647 UCHAR DeviceType;
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;
1659
1660typedef enum _STORAGE_QUERY_TYPE {
1661 PropertyStandardQuery = 0,
1662 PropertyExistsQuery,
1663 PropertyMaskQuery,
1664 PropertyQueryMaxDefined
1665} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
1666
1667typedef 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;
1676
1677typedef 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;
1682
1683
1684/////////////////////////////////////////////////////////////////////////////
1685
1953ff6d
GG
1686union STORAGE_DEVICE_DESCRIPTOR_DATA {
1687 STORAGE_DEVICE_DESCRIPTOR desc;
1688 char raw[256];
1689};
a37e7145 1690
1953ff6d
GG
1691// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1692// (This works without admin rights)
1693
1694static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
a37e7145
GG
1695{
1696 STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, 0};
1953ff6d 1697 memset(data, 0, sizeof(*data));
a37e7145
GG
1698
1699 DWORD num_out;
1700 if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
1953ff6d 1701 &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
a37e7145
GG
1702 if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
1703 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
1953ff6d
GG
1704 errno = ENOSYS;
1705 return -1;
a37e7145
GG
1706 }
1707
1708 if (con->reportataioctl > 1 || con->reportscsiioctl > 1) {
1709 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1710 " Vendor: \"%s\"\n"
1711 " Product: \"%s\"\n"
1712 " Revision: \"%s\"\n"
1713 " Removable: %s\n"
1714 " BusType: 0x%02x\n",
1953ff6d
GG
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
a37e7145
GG
1719 );
1720 }
1953ff6d
GG
1721 return 0;
1722}
1723
1724
1725/////////////////////////////////////////////////////////////////////////////
1726
1727// IOCTL_STORAGE_PREDICT_FAILURE
1728
1729#define IOCTL_STORAGE_PREDICT_FAILURE \
1730 CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
1731
1732typedef struct _STORAGE_PREDICT_FAILURE {
1733 ULONG PredictFailure;
1734 UCHAR VendorSpecific[512];
1735} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE;
1736
1737ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
1738
1739
1740/////////////////////////////////////////////////////////////////////////////
1741
1742
1743// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1744// or -1 on error, opionally return VendorSpecific data.
1745// (This works without admin rights)
1746
1747static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
1748{
1749 STORAGE_PREDICT_FAILURE pred;
1750 memset(&pred, 0, sizeof(pred));
1751
1752 DWORD num_out;
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());
1757 errno = ENOSYS;
1758 return -1;
1759 }
1760
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]
1768 );
1769 }
1770 if (data)
1771 memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
1772 return (!pred.PredictFailure ? 0 : 1);
a37e7145
GG
1773}
1774
1775
1776/////////////////////////////////////////////////////////////////////////////
1777
1778// get CONTROLLER_* for open handle
1953ff6d 1779static int get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
a37e7145
GG
1780{
1781 // Try SMART_GET_VERSION first to detect ATA SMART support
1782 // for drivers reporting BusTypeScsi (3ware)
1953ff6d 1783 if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
a37e7145
GG
1784 return CONTROLLER_ATA;
1785
1953ff6d
GG
1786 // Get BusType from device descriptor
1787 STORAGE_DEVICE_DESCRIPTOR_DATA data;
1788 if (storage_query_property_ioctl(hdevice, &data))
1789 return CONTROLLER_UNKNOWN;
1790
1791 switch (data.desc.BusType) {
a37e7145
GG
1792 case BusTypeAta:
1793 case BusTypeSata:
1794 if (ata_version_ex)
1795 memset(ata_version_ex, 0, sizeof(*ata_version_ex));
1796 return CONTROLLER_ATA;
1797 case BusTypeScsi:
1798 case BusTypeiScsi:
1799 case BusTypeSas:
1800 return CONTROLLER_SCSI;
1801 default:
1802 return CONTROLLER_UNKNOWN;
1803 }
1804 /*NOTREACHED*/
1805}
1806
1807// get CONTROLLER_* for device path
1808static int get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
1809{
1953ff6d 1810 bool admin = true;
a37e7145
GG
1811 HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
1812 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1953ff6d
GG
1813 if (h == INVALID_HANDLE_VALUE) {
1814 admin = false;
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;
1819 }
a37e7145 1820 if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
1953ff6d
GG
1821 pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
1822 int type = get_controller_type(h, admin, ata_version_ex);
a37e7145
GG
1823 CloseHandle(h);
1824 return type;
1825}
1826
1827// get CONTROLLER_* for physical drive number
1828static int get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
1829{
1830 char path[30];
1831 snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
1832 return get_controller_type(path, ata_version_ex);
1833}
1834
1835static int get_phy_drive_type(int drive)
1836{
1837 return get_phy_drive_type(drive, 0);
1838}
1839
1840// get CONTROLLER_* for logical drive number
1841static int get_log_drive_type(int drive)
1842{
1843 char path[30];
1844 snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
1845 return get_controller_type(path);
1846}
1847
1953ff6d
GG
1848// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1849static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
1850{
1851 STORAGE_DEVICE_DESCRIPTOR_DATA data;
1852 if (storage_query_property_ioctl(hdevice, &data))
1853 return -1;
1854
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
1863 return 0;
1864}
1865
a37e7145 1866
4d59bff9
GG
1867/////////////////////////////////////////////////////////////////////////////
1868
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)
1872
1873static int get_device_power_state(HANDLE hdevice)
1874{
1875 static HINSTANCE h_kernel_dll = 0;
1876#ifdef __CYGWIN__
1877 static DWORD kernel_dll_pid = 0;
1878#endif
1879 static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
1880
1881 BOOL state = TRUE;
1882
1883 if (!GetDevicePowerState_p
1884#ifdef __CYGWIN__
1885 || kernel_dll_pid != GetCurrentProcessId() // detect fork()
1886#endif
1887 ) {
1888 if (h_kernel_dll == INVALID_HANDLE_VALUE) {
1889 errno = ENOSYS;
1890 return -1;
1891 }
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;
1895 errno = ENOSYS;
1896 return -1;
1897 }
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;
1904 errno = ENOSYS;
1905 return -1;
1906 }
1907#ifdef __CYGWIN__
1908 kernel_dll_pid = GetCurrentProcessId();
1909#endif
1910 }
1911
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.
1919 return -1;
1920 }
1921
1922 if (con->reportataioctl > 1)
1923 pout(" GetDevicePowerState() succeeded, state=%d\n", state);
1924 return state;
1925}
1926
832b75ed
GG
1927
1928/////////////////////////////////////////////////////////////////////////////
1929
4d59bff9 1930// TODO: Put in a struct indexed by fd (or better a C++ object of course ;-)
832b75ed 1931static HANDLE h_ata_ioctl = 0;
1953ff6d 1932static bool ata_admin; // true if opened with admin rights
4d59bff9 1933static const char * ata_def_options;
a37e7145
GG
1934static char * ata_usr_options;
1935const int max_ata_driveno = 25;
4d59bff9 1936static int ata_driveno; // Drive number
a37e7145
GG
1937static int ata_driveno_is_log = -1; // 0=physical drivenumber, 1=logical drive number, -1=unknown
1938static char ata_smartver_state[max_ata_driveno+1]; // SMART_GET_VERSION: 0=unknown, 1=OK, 2=failed
832b75ed
GG
1939
1940// Print SMARTVSD error message, return errno
1941
1942static int smartvsd_error()
1943{
1944 char path[MAX_PATH];
1945 unsigned len;
1946 if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
1947 return ENOENT;
1948 // SMARTVSD.VXD present?
1949 strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
1950 if (!access(path, 0)) {
1951 // Yes, standard IDE driver used?
1952 HANDLE h;
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");
1958 return ENOENT;
1959 }
1960 else {
1961 if (h != INVALID_HANDLE_VALUE) // should not happen
1962 CloseHandle(h);
1963 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
1964 return ENOSYS;
1965 }
1966 }
1967 else {
832b75ed
GG
1968 strcpy(path+len, "\\SMARTVSD.VXD");
1969 if (!access(path, 0)) {
4d59bff9
GG
1970 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
1971 // (http://support.microsoft.com/kb/265854/en-us).
832b75ed
GG
1972 path[len] = 0;
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);
1976 }
1977 else {
4d59bff9
GG
1978 // Some Windows versions do not provide SMARTVSD.VXD
1979 // (http://support.microsoft.com/kb/199886/en-us).
832b75ed
GG
1980 path[len] = 0;
1981 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
1982 }
1983 return ENOSYS;
1984 }
1985}
1986
1987
4d59bff9
GG
1988// Get default ATA device options
1989
1990static const char * ata_get_def_options()
1991{
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
1953ff6d
GG
1998 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
1999 // STORAGE_*, SCSI_MINIPORT_*
4d59bff9
GG
2000}
2001
2002
2003// Open ATA device
2004
a37e7145 2005static int ata_open(int phydrive, int logdrive, const char * options, int port)
832b75ed 2006{
832b75ed
GG
2007 // TODO: This version does not allow to open more than 1 ATA devices
2008 if (h_ata_ioctl) {
2009 errno = ENFILE;
2010 return -1;
2011 }
2012
a37e7145
GG
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");
2018 errno = EINVAL;
832b75ed
GG
2019 return -1;
2020 }
4d59bff9 2021
832b75ed 2022 // path depends on Windows Version
a37e7145
GG
2023 bool win9x = is_win9x();
2024 char devpath[30];
2025 if (win9x && 0 <= phydrive && phydrive <= 7)
4d59bff9 2026 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
a37e7145
GG
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);
2032 }
2033 else {
2034 errno = ENOENT;
2035 return -1;
2036 }
832b75ed
GG
2037
2038 // Open device
1953ff6d
GG
2039 h_ata_ioctl = INVALID_HANDLE_VALUE;
2040 if (win9x || !(*options && !options[strspn(options, "fp")])) {
2041 // Open with admin rights
2042 ata_admin = true;
2043 h_ata_ioctl = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
2044 FILE_SHARE_READ|FILE_SHARE_WRITE,
2045 NULL, OPEN_EXISTING, 0, 0);
2046 }
2047 if (!win9x && h_ata_ioctl == INVALID_HANDLE_VALUE) {
2048 // Open without admin rights
2049 ata_admin = false;
2050 h_ata_ioctl = CreateFileA(devpath, 0,
2051 FILE_SHARE_READ|FILE_SHARE_WRITE,
2052 NULL, OPEN_EXISTING, 0, 0);
2053 }
2054 if (h_ata_ioctl == INVALID_HANDLE_VALUE) {
2055 long err = GetLastError();
832b75ed
GG
2056 pout("Cannot open device %s, Error=%ld\n", devpath, err);
2057 if (err == ERROR_FILE_NOT_FOUND)
a37e7145 2058 errno = (win9x && phydrive <= 3 ? smartvsd_error() : ENOENT);
1953ff6d 2059 else if (err == ERROR_ACCESS_DENIED)
832b75ed 2060 errno = EACCES;
832b75ed
GG
2061 else
2062 errno = EIO;
2063 h_ata_ioctl = 0;
2064 return -1;
2065 }
2066
4d59bff9 2067 if (con->reportataioctl > 1)
1953ff6d 2068 pout("%s: successfully opened%s\n", devpath, (!ata_admin ? " (without admin rights)" :""));
4d59bff9 2069
a37e7145
GG
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);
2077 if (*options)
2078 ata_usr_options = strdup(options);
4d59bff9
GG
2079
2080 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
a37e7145
GG
2081 if (phydrive >= 0) {
2082 ata_driveno = phydrive; ata_driveno_is_log = 0;
2083 }
2084 else {
2085 assert(logdrive >= 0);
2086 ata_driveno = logdrive; ata_driveno_is_log = 1;
2087 }
4d59bff9
GG
2088 if (!win9x && port < 0)
2089 return 0;
2090
2091 // Win9X/ME: Get drive map
2092 // RAID: Get port map
a37e7145
GG
2093 GETVERSIONINPARAMS_EX vers_ex;
2094 int devmap = smart_get_version(h_ata_ioctl, (port >= 0 ? &vers_ex : 0));
2095
4d59bff9 2096 unsigned long portmap = 0;
a37e7145
GG
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);
2103 devmap = -1;
2104 }
2105 else
2106 portmap = vers_ex.dwDeviceMapEx;
2107 }
4d59bff9 2108 if (devmap < 0) {
a37e7145 2109 pout("%s: ATA driver has no SMART support\n", devpath);
832b75ed 2110 if (!is_permissive()) {
4d59bff9 2111 ata_close(0);
832b75ed
GG
2112 errno = ENOSYS;
2113 return -1;
2114 }
4d59bff9
GG
2115 devmap = 0x0f;
2116 }
a37e7145 2117 ata_smartver_state[ata_driveno] = 1;
4d59bff9
GG
2118
2119 if (port >= 0) {
ba59cff1
GG
2120 // 3ware RAID: update devicemap first
2121 if (!update_3ware_devicemap_ioctl(h_ata_ioctl)) {
a37e7145
GG
2122 if ( smart_get_version(h_ata_ioctl, &vers_ex) >= 0
2123 && vers_ex.wIdentifier == SMART_VENDOR_3WARE )
2124 portmap = vers_ex.dwDeviceMapEx;
2125 }
ba59cff1 2126 // Check port existence
4d59bff9
GG
2127 if (!(portmap & (1L << port))) {
2128 pout("%s: Port %d is empty or does not exist\n", devpath, port);
2129 if (!is_permissive()) {
2130 ata_close(0);
2131 errno = ENOENT;
2132 return -1;
2133 }
2134 }
2135 // Encode port into pseudo fd
ba59cff1 2136 return (ATARAID_FDOFFSET | port);
832b75ed 2137 }
832b75ed
GG
2138
2139 // Win9x/ME: Check device presence & type
a37e7145
GG
2140 if (((devmap >> (ata_driveno & 0x3)) & 0x11) != 0x01) {
2141 unsigned char atapi = (devmap >> (ata_driveno & 0x3)) & 0x10;
4d59bff9 2142 pout("%s: Drive %d %s (IDEDeviceMap=0x%02x).\n", devpath,
a37e7145 2143 ata_driveno, (atapi?"is an ATAPI device":"does not exist"), devmap);
4d59bff9 2144 // Win9x drive existence check may not work as expected
832b75ed 2145 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
4d59bff9 2146 // (The related KB Article Q196120 is no longer available)
832b75ed 2147 if (!is_permissive()) {
4d59bff9 2148 ata_close(0);
832b75ed
GG
2149 errno = (atapi ? ENOSYS : ENOENT);
2150 return -1;
2151 }
2152 }
2153 // Use drive number as fd for ioctl
a37e7145 2154 return (ata_driveno & 0x3);
832b75ed
GG
2155}
2156
2157
a37e7145 2158static void ata_close(int /*fd*/)
832b75ed 2159{
832b75ed
GG
2160 CloseHandle(h_ata_ioctl);
2161 h_ata_ioctl = 0;
a37e7145
GG
2162 if (ata_usr_options) {
2163 free(ata_usr_options);
2164 ata_usr_options = 0;
4d59bff9 2165 }
832b75ed
GG
2166}
2167
2168
a37e7145 2169// Scan for ATA drives on Win9x/ME, fill bitmask of drives present, return #drives
832b75ed 2170
a37e7145 2171static int ata_scan_win9x(unsigned long * drives)
832b75ed 2172{
a37e7145
GG
2173 // Open device
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
2181 }
832b75ed 2182
a37e7145
GG
2183 // Get drive map
2184 int devmap = smart_get_version(h);
2185 CloseHandle(h);
2186 if (devmap < 0)
2187 return 0; // Should not happen
832b75ed 2188
a37e7145
GG
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);
2192}
2193
2194
2195// Scan for ATA drives, fill bitmask of drives present, return #drives
2196
2197static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives)
2198{
2199 int cnt = 0;
2200 for (int i = 0; i <= 9; i++) {
2201 GETVERSIONINPARAMS_EX vers_ex;
2202 if (get_phy_drive_type(i, &vers_ex) != CONTROLLER_ATA)
2203 continue;
832b75ed 2204
a37e7145 2205 // Interpret RAID drive map if present
4d59bff9
GG
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)
2209 continue;
2210 assert(rdrives[vers_ex.wControllerId] == 0);
2211 // Count physical drives
2212 int pcnt = 0;
2213 for (int pi = 0; pi < 32; pi++) {
2214 if (vers_ex.dwDeviceMapEx & (1L << pi))
2215 pcnt++;
2216 }
2217 if (!pcnt)
2218 continue; // Should not happen
2219 rdrives[vers_ex.wControllerId] = vers_ex.dwDeviceMapEx;
2220 rdriveno[vers_ex.wControllerId] = i;
2221 cnt += pcnt-1;
2222 }
2223
a37e7145 2224 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
832b75ed
GG
2225 drives[0] |= (1L << i);
2226 cnt++;
2227 }
2228
2229 return cnt;
2230}
2231
2232
2233/////////////////////////////////////////////////////////////////////////////
2234
2235// Interface to ATA devices. See os_linux.c
2236int ata_command_interface(int fd, smart_command_set command, int select, char * data)
2237{
9ebc753d
GG
2238 if (fd == TW_CLI_FDOFFSET) // Parse tw_cli output
2239 return tw_cli_command_interface(command, select, data);
2240
4d59bff9 2241 int port = -1;
ba59cff1 2242 if ((fd & ~0x1f) == ATARAID_FDOFFSET) {
4d59bff9
GG
2243 // RAID Port encoded into pseudo fd
2244 port = fd & 0x1f;
2245 fd = 0;
2246 }
832b75ed
GG
2247
2248 if (!(0 <= fd && fd <= 3)) {
2249 errno = EBADF;
2250 return -1;
2251 }
2252
2253 // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
ba59cff1 2254 IDEREGS regs; memset(&regs, 0, sizeof(regs));
832b75ed
GG
2255 regs.bCommandReg = ATA_SMART_CMD;
2256 regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
ba59cff1 2257 int datasize = 0;
4d59bff9 2258
1953ff6d 2259 // Try by default: SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
a37e7145 2260 // and SCSI_MINIPORT_* if requested by user
1953ff6d 2261 const char * valid_options = (ata_usr_options ? "saicmf" : "saicf");
832b75ed
GG
2262
2263 switch (command) {
832b75ed 2264 case CHECK_POWER_MODE:
4d59bff9 2265 // Not a SMART command, needs IDE register return
832b75ed
GG
2266 regs.bCommandReg = ATA_CHECK_POWER_MODE;
2267 regs.bCylLowReg = regs.bCylHighReg = 0;
ba59cff1 2268 valid_options = "pai3"; // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
832b75ed
GG
2269 // Note: returns SectorCountReg in data[0]
2270 break;
2271 case READ_VALUES:
2272 regs.bFeaturesReg = ATA_SMART_READ_VALUES;
2273 regs.bSectorNumberReg = regs.bSectorCountReg = 1;
4d59bff9 2274 datasize = 512;
832b75ed
GG
2275 break;
2276 case READ_THRESHOLDS:
2277 regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS;
2278 regs.bSectorNumberReg = regs.bSectorCountReg = 1;
4d59bff9 2279 datasize = 512;
832b75ed
GG
2280 break;
2281 case READ_LOG:
2282 regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR;
2283 regs.bSectorNumberReg = select;
2284 regs.bSectorCountReg = 1;
a37e7145
GG
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");
4d59bff9 2288 datasize = 512;
832b75ed 2289 break;
ba59cff1
GG
2290 case WRITE_LOG:
2291 regs.bFeaturesReg = ATA_SMART_WRITE_LOG_SECTOR;
2292 regs.bSectorNumberReg = select;
2293 regs.bSectorCountReg = 1;
a37e7145
GG
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");
ba59cff1
GG
2297 datasize = -512; // DATA_OUT!
2298 break;
832b75ed
GG
2299 case IDENTIFY:
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;
4d59bff9 2305 datasize = 512;
832b75ed
GG
2306 break;
2307 case PIDENTIFY:
2308 regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE;
2309 regs.bCylLowReg = regs.bCylHighReg = 0;
2310 regs.bSectorCountReg = 1;
4d59bff9 2311 datasize = 512;
832b75ed
GG
2312 break;
2313 case ENABLE:
2314 regs.bFeaturesReg = ATA_SMART_ENABLE;
2315 regs.bSectorNumberReg = 1;
2316 break;
2317 case DISABLE:
2318 regs.bFeaturesReg = ATA_SMART_DISABLE;
2319 regs.bSectorNumberReg = 1;
2320 break;
832b75ed 2321 case STATUS_CHECK:
a37e7145 2322 // Requires CL,CH register return
1953ff6d 2323 valid_options = (ata_usr_options ? "saimf" : "saif");
4d59bff9 2324 case STATUS:
832b75ed
GG
2325 regs.bFeaturesReg = ATA_SMART_STATUS;
2326 break;
2327 case AUTO_OFFLINE:
2328 regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE;
2329 regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
2330 break;
2331 case AUTOSAVE:
2332 regs.bFeaturesReg = ATA_SMART_AUTOSAVE;
2333 regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
2334 break;
2335 case IMMEDIATE_OFFLINE:
2336 regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE;
2337 regs.bSectorNumberReg = select;
a37e7145
GG
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");
832b75ed
GG
2341 break;
2342 default:
2343 pout("Unrecognized command %d in win32_ata_command_interface()\n"
2344 "Please contact " PACKAGE_BUGREPORT "\n", command);
2345 errno = ENOSYS;
2346 return -1;
2347 }
2348
1953ff6d
GG
2349 if (!ata_admin) {
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";
2355 else {
2356 errno = ENOSYS;
2357 return -1;
2358 }
2359 }
2360
2361 // Try all valid ioctls in the order specified in ata_*_options
ba59cff1 2362 bool powered_up = false;
a37e7145
GG
2363 assert(ata_def_options);
2364 const char * options = (ata_usr_options ? ata_usr_options : ata_def_options);
ba59cff1 2365 for (int i = 0; ; i++) {
a37e7145 2366 char opt = options[i];
4d59bff9
GG
2367
2368 if (!opt) {
ba59cff1
GG
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;
2373 break;
2374 }
4d59bff9
GG
2375 // No IOCTL found
2376 errno = ENOSYS;
2377 return -1;
832b75ed 2378 }
4d59bff9
GG
2379 if (!strchr(valid_options, opt))
2380 // Invalid for this command
2381 continue;
832b75ed 2382
832b75ed 2383 errno = 0;
a37e7145 2384 assert(datasize == 0 || datasize == 512 || (strchr("am", opt) && datasize == -512));
ba59cff1 2385 int rc;
4d59bff9
GG
2386 switch (opt) {
2387 default: assert(0);
2388 case 's':
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;
2393 break;
832b75ed 2394 }
4d59bff9
GG
2395 if (!ata_smartver_state[ata_driveno]) {
2396 assert(port == -1);
2397 if (smart_get_version(h_ata_ioctl) < 0) {
2398 if (!con->permissive) {
a37e7145
GG
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");
4d59bff9
GG
2401 ata_smartver_state[ata_driveno] = 2;
2402 rc = -1; errno = ENOSYS;
2403 break;
2404 }
2405 con->permissive--;
2406 }
2407 ata_smartver_state[ata_driveno] = 1;
2408 }
2409 rc = smart_ioctl(h_ata_ioctl, fd, &regs, data, datasize, port);
2410 break;
a37e7145
GG
2411 case 'm':
2412 rc = ata_via_scsi_miniport_smart_ioctl(h_ata_ioctl, &regs, data, datasize);
2413 break;
4d59bff9
GG
2414 case 'a':
2415 rc = ata_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
2416 break;
2417 case 'i':
2418 rc = ide_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
2419 break;
2420 case 'c':
2421 rc = ata_via_scsi_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
2422 break;
1953ff6d
GG
2423 case 'f':
2424 switch (command) {
2425 case READ_VALUES:
2426 rc = storage_predict_failure_ioctl(h_ata_ioctl, data);
2427 if (rc > 0)
2428 rc = 0;
2429 break;
2430 case READ_THRESHOLDS:
2431 {
2432 ata_smart_values sv;
2433 rc = storage_predict_failure_ioctl(h_ata_ioctl, (char *)&sv);
2434 if (rc < 0)
2435 break;
2436 rc = 0;
2437 // Fake zero thresholds
2438 ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
2439 memset(tr, 0, 512);
2440 for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++)
2441 tr->chksum -= tr->thres_entries[i].id = sv.vendor_attributes[i].id;
2442 }
2443 break;
2444 case IDENTIFY:
2445 rc = get_identify_from_device_property(h_ata_ioctl, (ata_identify_device *)data);
2446 break;
2447 case ENABLE:
2448 rc = 0;
2449 break;
2450 case STATUS_CHECK:
2451 case STATUS:
2452 rc = storage_predict_failure_ioctl(h_ata_ioctl);
2453 if (rc > 0) {
2454 if (command == STATUS_CHECK) {
2455 regs.bCylHighReg = 0x2c; regs.bCylLowReg = 0xf4;
2456 }
2457 rc = 0;
2458 }
2459 break;
2460 default:
2461 errno = ENOSYS; rc = -1;
2462 }
2463 break;
ba59cff1
GG
2464 case '3':
2465 rc = ata_via_3ware_miniport_ioctl(h_ata_ioctl, &regs, data, datasize, port);
2466 break;
4d59bff9
GG
2467 case 'p':
2468 assert(command == CHECK_POWER_MODE && datasize == 0);
2469 rc = get_device_power_state(h_ata_ioctl);
ba59cff1
GG
2470 if (rc == 0) {
2471 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
2472 // spin up the drive => simulate ATA result STANDBY.
2473 regs.bSectorCountReg = 0x00;
2474 }
2475 else if (rc > 0) {
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.
2479 powered_up = true;
2480 errno = ENOSYS; rc = -1;
4d59bff9
GG
2481 }
2482 break;
832b75ed 2483 }
4d59bff9
GG
2484
2485 if (!rc)
2486 // Working ioctl found
2487 break;
2488
2489 if (errno != ENOSYS)
2490 // Abort on I/O error
2491 return -1;
2492
2493 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
832b75ed 2494 }
832b75ed
GG
2495
2496 switch (command) {
2497 case CHECK_POWER_MODE:
2498 // Return power mode from SectorCountReg in data[0]
2499 data[0] = regs.bSectorCountReg;
2500 return 0;
2501
2502 case STATUS_CHECK:
2503 // Cyl low and Cyl high unchanged means "Good SMART status"
2504 if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW)
2505 return 0;
2506
2507 // These values mean "Bad SMART status"
2508 if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4)
2509 return 1;
2510
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(&regs, 1);
2515 errno = EIO;
2516 return -1;
2517
2518 default:
2519 return 0;
2520 }
2521 /*NOTREACHED*/
2522}
2523
2524
2525#ifndef HAVE_ATA_IDENTIFY_IS_CACHED
2526#error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
2527#endif
2528
2529// Return true if OS caches the ATA identify sector
2530int ata_identify_is_cached(int fd)
2531{
4d59bff9 2532 // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false
a37e7145 2533 return (!(fd & 0xff00) && !is_win9x());
832b75ed
GG
2534}
2535
2536
2537// Print not implemeted warning once
2538static void pr_not_impl(const char * what, int * warned)
2539{
2540 if (*warned)
2541 return;
2542 pout(
2543 "#######################################################################\n"
2544 "%s\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"
2549 "\n", what
2550 );
2551 *warned = 1;
2552}
2553
2554// Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c
a37e7145 2555int escalade_command_interface(int /*fd*/, int disknum, int /*escalade_type*/, smart_command_set /*command*/, int /*select*/, char * /*data*/)
832b75ed
GG
2556{
2557 static int warned = 0;
4d59bff9
GG
2558 if (!warned) {
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);
2562 warned = 1;
2563 }
832b75ed
GG
2564 errno = ENOSYS;
2565 return -1;
2566}
2567
2568// Interface to ATA devices behind Marvell chip-set based controllers. See os_linux.c
a37e7145 2569int marvell_command_interface(int /*fd*/, smart_command_set /*command*/, int /*select*/, char * /*data*/)
832b75ed
GG
2570{
2571 static int warned = 0;
832b75ed
GG
2572 pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
2573 errno = ENOSYS;
2574 return -1;
2575}
2576
4d59bff9 2577// Interface to ATA devices behind HighPoint Raid controllers. See os_linux.c
a37e7145 2578int highpoint_command_interface(int /*fd*/, smart_command_set /*command*/, int /*select*/, char * /*data*/)
4d59bff9
GG
2579{
2580 static int warned = 0;
4d59bff9
GG
2581 pr_not_impl("HighPoint raid controller command routine highpoint_command_interface()", &warned);
2582 errno = ENOSYS;
2583 return -1;
2584}
2585
832b75ed
GG
2586
2587/////////////////////////////////////////////////////////////////////////////
ba59cff1 2588// ASPI Interface (for SCSI devices)
832b75ed
GG
2589/////////////////////////////////////////////////////////////////////////////
2590
2591#pragma pack(1)
2592
2593#define ASPI_SENSE_SIZE 18
2594
2595// ASPI SCSI Request block header
2596
2597typedef struct {
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
2603} ASPI_SRB_HEAD;
2604
2605// SRB for host adapter inquiry
2606
2607typedef struct {
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
2614} ASPI_SRB_INQUIRY;
2615
2616// SRB for get device type
2617
2618typedef struct {
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
2624} ASPI_SRB_DEVTYPE;
2625
2626// SRB for SCSI I/O
2627
2628typedef struct {
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];
2642} ASPI_SRB_IO;
2643
2644// Macro to retrieve start of sense information
2645#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
2646
2647// SRB union
2648
2649typedef union {
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
2654} ASPI_SRB;
2655
2656#pragma pack()
2657
2658// ASPI commands
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
2663
2664// Request flags
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
2669
2670// ASPI status
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
2680
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
2688
2689// Target status
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
2694
2695
2696static HINSTANCE h_aspi_dll; // DLL handle
2697static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
2698static unsigned num_aspi_adapters;
2699
2700#ifdef __CYGWIN__
2701// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
2702static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
2703#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
2704#else
2705#define aspi_entry_valid() (!!aspi_entry)
2706#endif
2707
2708
2709static int aspi_call(ASPI_SRB * srb)
2710{
2711 int i;
2712 aspi_entry(srb);
2713 i = 0;
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);
2717 aspi_entry = 0;
4d59bff9 2718 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
832b75ed
GG
2719 errno = EIO;
2720 return -1;
2721 }
2722 if (con->reportscsiioctl > 1)
2723 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
2724 Sleep(100);
2725 }
2726 return 0;
2727}
2728
2729
2730// Get ASPI entrypoint from wnaspi32.dll
2731
2732static FARPROC aspi_get_address(const char * name, int verbose)
2733{
2734 FARPROC addr;
2735 assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
2736
2737 if (!(addr = GetProcAddress(h_aspi_dll, name))) {
2738 if (verbose)
2739 pout("Missing %s() in WNASPI32.DLL\n", name);
2740 aspi_entry = 0;
2741 FreeLibrary(h_aspi_dll);
4d59bff9 2742 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
832b75ed
GG
2743 errno = ENOSYS;
2744 return 0;
2745 }
2746 return addr;
2747}
2748
2749
2750static int aspi_open_dll(int verbose)
2751{
2752 UINT (*aspi_info)(void);
2753 UINT info, rc;
2754
2755 assert(!aspi_entry_valid());
2756
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);
2768
2769 if (h_aspi_dll == INVALID_HANDLE_VALUE) {
2770 // do not retry
2771 errno = ENOENT;
2772 return -1;
2773 }
2774
2775 // Load ASPI DLL
2776 if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
2777 if (verbose)
2778 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
4d59bff9 2779 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
832b75ed
GG
2780 errno = ENOENT;
2781 return -1;
2782 }
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);
2789 }
2790
2791 // Get ASPI entrypoints
2792 if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
2793 return -1;
2794 if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
2795 return -1;
2796
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;
2804 }
2805 else if (rc == ASPI_STATUS_NO_ERROR) {
2806 num_aspi_adapters = info & 0xff;
2807 }
2808 else {
2809 if (verbose)
2810 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
2811 aspi_entry = 0;
2812 FreeLibrary(h_aspi_dll);
4d59bff9 2813 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
832b75ed
GG
2814 errno = ENOENT;
2815 return -1;
2816 }
2817
2818 if (con->reportscsiioctl)
2819 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
2820
2821#ifdef __CYGWIN__
2822 // save PID to detect fork() in aspi_entry_valid()
2823 aspi_dll_pid = GetCurrentProcessId();
2824#endif
2825 assert(aspi_entry_valid());
2826 return 0;
2827}
2828
2829
2830static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
2831{
2832 HANDLE event;
2833 // Create event
2834 if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
2835 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
2836 }
2837 srb->i.event_handle = event;
2838 srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
2839 // Start ASPI request
2840 aspi_entry(srb);
2841 if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
2842 // Wait for event
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);
2848 }
2849 else {
2850 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
2851 (unsigned long)event, rc, rc, GetLastError());
2852 }
2853 // TODO: ASPI_ABORT_IO command
2854 aspi_entry = 0;
4d59bff9 2855 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
832b75ed
GG
2856 return -EIO;
2857 }
2858 }
2859 CloseHandle(event);
2860 return 0;
2861}
2862
2863
2864static int aspi_open(unsigned adapter, unsigned id)
2865{
2866 ASPI_SRB srb;
2867 if (!(adapter <= 9 && id < 16)) {
2868 errno = ENOENT;
2869 return -1;
2870 }
2871
2872 if (!aspi_entry_valid()) {
2873 if (aspi_open_dll(1/*verbose*/))
2874 return -1;
2875 }
2876
2877 // Adapter OK?
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()) {
2882 errno = ENOENT;
2883 return -1;
2884 }
2885 }
2886
2887 // Device present ?
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)) {
2892 errno = EIO;
2893 return -1;
2894 }
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);
2899 return -1;
2900 }
2901 }
2902 else if (con->reportscsiioctl)
2903 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
2904
ba59cff1 2905 return (ASPI_FDOFFSET | ((adapter & 0xf)<<4) | (id & 0xf));
832b75ed
GG
2906}
2907
2908
a37e7145 2909static void aspi_close(int /*fd*/)
832b75ed
GG
2910{
2911 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
832b75ed
GG
2912}
2913
2914
2915// Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
2916// return #drives
2917
2918static int aspi_scan(unsigned long * drives)
2919{
2920 int cnt = 0;
2921 unsigned ad;
2922
2923 if (!aspi_entry_valid()) {
2924 if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
2925 return 0;
2926 }
2927
2928 for (ad = 0; ad < num_aspi_adapters; ad++) {
2929 ASPI_SRB srb; unsigned id;
2930
2931 if (ad > 9) {
2932 if (con->reportscsiioctl)
2933 pout(" ASPI Adapter %u: Ignored\n", ad);
2934 continue;
2935 }
2936
2937 // Get adapter name
2938 memset(&srb, 0, sizeof(srb));
2939 srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
2940 srb.h.adapter = ad;
2941 if (aspi_call(&srb))
2942 return 0;
2943
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);
2947 continue;
2948 }
2949
2950 if (con->reportscsiioctl) {
2951 int i;
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);
2956 }
2957
4d59bff9
GG
2958 bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5);
2959
832b75ed
GG
2960 for (id = 0; id <= 7; id++) {
2961 // Get device type
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))
2966 return 0;
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);
2970 continue;
2971 }
4d59bff9
GG
2972
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);
832b75ed
GG
2976 drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id));
2977 cnt++;
2978 }
4d59bff9
GG
2979 else if (con->reportscsiioctl)
2980 pout(" ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);
832b75ed
GG
2981 }
2982 }
2983 return cnt;
2984}
2985
2986
2987/////////////////////////////////////////////////////////////////////////////
2988
ba59cff1
GG
2989// Interface to ASPI SCSI devices. See scsicmds.h and os_linux.c
2990static int do_aspi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
832b75ed
GG
2991{
2992 ASPI_SRB srb;
2993
2994 if (!aspi_entry_valid())
2995 return -EBADF;
ba59cff1 2996 if (!((fd & ~0xff) == ASPI_FDOFFSET))
832b75ed
GG
2997 return -EBADF;
2998
4d59bff9 2999 if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
ba59cff1 3000 pout("do_aspi_cmnd_io: bad CDB length\n");
832b75ed
GG
3001 return -EINVAL;
3002 }
3003
3004 if (report > 0) {
3005 // From os_linux.c
3006 int k, j;
3007 const unsigned char * ucp = iop->cmnd;
3008 const char * np;
3009 char buff[256];
3010 const int sz = (int)sizeof(buff);
3011
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]);
3016 if ((report > 1) &&
3017 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
3018 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3019
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);
3024 }
3025 else
3026 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
3027 pout(buff);
3028 }
3029
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);
3034 //srb.i.lun = 0;
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);
3038
3039 switch (iop->dxfer_dir) {
3040 case DXFER_NONE:
3041 srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
3042 break;
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;
3047 break;
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;
3052 break;
3053 default:
ba59cff1 3054 pout("do_aspi_cmnd_io: bad dxfer_dir\n");
832b75ed
GG
3055 return -EINVAL;
3056 }
3057
3058 iop->resp_sense_len = 0;
3059 iop->scsi_status = 0;
3060 iop->resid = 0;
3061
3062 if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
3063 // Timeout
3064 return -EIO;
3065 }
3066
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) {
3071 // Sense valid
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;
3078 if (report > 1) {
3079 pout(" >>> Sense buffer, len=%d:\n", (int)len);
3080 dStrHex(iop->sensep, len , 1);
3081 }
3082 }
3083 if (report) {
3084 pout(" sense_key=%x asc=%x ascq=%x\n",
3085 sense[2] & 0xf, sense[12], sense[13]);
3086 }
3087 return 0;
3088 }
3089 else {
3090 if (report)
3091 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
3092 return -EIO;
3093 }
3094 }
3095
3096 if (report > 0)
3097 pout(" OK\n");
3098
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);
3104 }
3105
3106 return 0;
3107}
ba59cff1
GG
3108
3109
3110/////////////////////////////////////////////////////////////////////////////
3111// SPT Interface (for SCSI devices and ATA devices behind SATLs)
3112// Only supported in NT and later
3113/////////////////////////////////////////////////////////////////////////////
3114
3115#define SPT_MAXDEV 64
3116
3117struct spt_dev_info {
3118 HANDLE h_spt_ioctl;
ba59cff1
GG
3119 int sub_addr; // addressing disks within a RAID, for example
3120};
3121
3122// Private table of open devices: guaranteed zero on startup since
3123// part of static data.
3124static struct spt_dev_info * spt_dev_arr[SPT_MAXDEV];
3125
3126
a37e7145 3127static int spt_open(int pd_num, int ld_num, int tape_num, int sub_addr)
ba59cff1
GG
3128{
3129 int k;
3130 struct spt_dev_info * sdip;
3131 char b[128];
3132 HANDLE h;
3133
3134 for (k = 0; k < SPT_MAXDEV; k++)
3135 if (! spt_dev_arr[k])
3136 break;
3137
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",
3143 SPT_MAXDEV);
3144 errno = EMFILE;
3145 return -1;
3146 }
3147 sdip = (struct spt_dev_info *)malloc(sizeof(struct spt_dev_info));
3148 if (NULL == sdip) {
3149 errno = ENOMEM;
3150 return -1;
3151 }
3152 spt_dev_arr[k] = sdip;
ba59cff1
GG
3153 sdip->sub_addr = sub_addr;
3154
3155 b[sizeof(b) - 1] = '\0';
3156 if (pd_num >= 0)
3157 snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
a37e7145
GG
3158 else if (ld_num >= 0)
3159 snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
ba59cff1
GG
3160 else if (tape_num >= 0)
3161 snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
3162 else {
3163 if (con->reportscsiioctl)
a37e7145 3164 pout("spt_open: bad parameters\n");
ba59cff1
GG
3165 errno = EINVAL;
3166 goto err_out;
3167 }
3168
3169 // Open device
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());
3175 errno = ENODEV;
3176 goto err_out;
3177 }
3178 sdip->h_spt_ioctl = h;
3179 return k + SPT_FDOFFSET;
a37e7145 3180
ba59cff1
GG
3181err_out:
3182 spt_dev_arr[k] = NULL;
3183 free(sdip);
3184 return -1;
3185}
3186
3187
3188static void spt_close(int fd)
3189{
3190 struct spt_dev_info * sdip;
3191 int index = fd - SPT_FDOFFSET;
3192
3193 if ((index < 0) || (index >= SPT_MAXDEV)) {
3194 if (con->reportscsiioctl)
3195 pout("spt_close: bad fd range\n");
3196 return;
3197 }
3198 sdip = spt_dev_arr[index];
3199 if (NULL == sdip) {
3200 if (con->reportscsiioctl)
3201 pout("spt_close: fd already closed\n");
3202 return;
3203 }
3204 free(sdip);
3205 spt_dev_arr[index] = NULL;
3206}
3207
3208
a37e7145
GG
3209static int spt_scan(unsigned long * drives)
3210{
3211 int cnt = 0;
3212 for (int i = 0; i <= 9; i++) {
3213 if (get_phy_drive_type(i) != CONTROLLER_SCSI)
3214 continue;
3215 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
3216 drives[0] |= (1L << i);
3217 cnt++;
3218 }
3219 return cnt;
3220}
3221
3222
ba59cff1
GG
3223#define IOCTL_SCSI_PASS_THROUGH_DIRECT \
3224 CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
3225
3226typedef struct _SCSI_PASS_THROUGH_DIRECT {
3227 USHORT Length;
3228 UCHAR ScsiStatus;
3229 UCHAR PathId;
3230 UCHAR TargetId;
3231 UCHAR Lun;
3232 UCHAR CdbLength;
3233 UCHAR SenseInfoLength;
3234 UCHAR DataIn;
3235 ULONG DataTransferLength;
3236 ULONG TimeOutValue;
3237 PVOID DataBuffer;
3238 ULONG SenseInfoOffset;
3239 UCHAR Cdb[16];
3240} SCSI_PASS_THROUGH_DIRECT;
3241
3242typedef struct {
3243 SCSI_PASS_THROUGH_DIRECT spt;
3244 ULONG Filler;
3245 UCHAR ucSenseBuf[64];
3246} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
3247
3248
3249// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
3250static int do_spt_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
3251{
3252 struct spt_dev_info * sdip;
3253 int index = fd - SPT_FDOFFSET;
3254 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
3255 DWORD num_out;
3256
3257 if ((index < 0) || (index >= SPT_MAXDEV)) {
3258 if (report)
3259 pout("do_spt_cmnd_io: bad fd range\n");
3260 return -EBADF;
3261 }
3262 sdip = spt_dev_arr[index];
3263 if (NULL == sdip) {
3264 if (report)
3265 pout("do_spt_cmnd_io: fd already closed\n");
3266 return -EBADF;
3267 }
3268
3269 if (report > 0) {
3270 int k, j;
3271 const unsigned char * ucp = iop->cmnd;
3272 const char * np;
3273 char buff[256];
3274 const int sz = (int)sizeof(buff);
3275
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]);
3280 if ((report > 1) &&
3281 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
3282 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3283
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);
3288 }
3289 else
3290 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
3291 pout(buff);
3292 }
3293 if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
3294 if (report)
3295 pout("do_spt_cmnd_io: cmnd_len too large\n");
3296 return -EINVAL;
3297 }
3298
3299 memset(&sb, 0, sizeof(sb));
3300 sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
a37e7145 3301 sb.spt.CdbLength = iop->cmnd_len;
ba59cff1
GG
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) {
3308 case DXFER_NONE:
3309 sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
3310 break;
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;
3315 break;
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;
3320 break;
3321 default:
3322 pout("do_spt_cmnd_io: bad dxfer_dir\n");
3323 return -EINVAL;
3324 }
3325
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();
3329
3330 if (report)
3331 pout(" IOCTL_SCSI_PASS_THROUGH_DIRECT failed, Error=%ld\n", err);
3332 return -(err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
3333 }
3334
3335 iop->scsi_status = sb.spt.ScsiStatus;
3336 if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
3337 int slen = sb.ucSenseBuf[7] + 8;
3338
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;
3345 if (report) {
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]);
3350 else
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]);
3354 }
3355 } else
3356 iop->resp_sense_len = 0;
3357
3358 if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
3359 iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
3360 else
3361 iop->resid = 0;
3362
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);
3368 }
3369 return 0;
3370}
3371
3372
3373// Decides which SCSI implementation based on pseudo fd.
3374// Declaration and explanation in scsicmds.h
3375int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
3376{
3377 if ((fd & ~0xff) == ASPI_FDOFFSET)
3378 return do_aspi_cmnd_io(fd, iop, report);
3379 else
3380 return do_spt_cmnd_io(fd, iop, report);
3381}