]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_win32.c
document changes
[mirror_smartmontools-debian.git] / os_win32.c
1 /*
2 * os_win32.c
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
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"
23 extern smartmonctrl * con; // con->permissive,reportataioctl
24 #include "scsicmds.h"
25 #include "utility.h"
26 extern 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
39 #define ARGUSED(x) ((void)(x))
40
41 // Needed by '-V' option (CVS versioning) of smartd/smartctl
42 const char *os_XXXX_c_cvsid="$Id: os_win32.c,v 1.33 2006/04/07 15:02:19 chrfranke Exp $"
43 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
44
45
46 #ifndef HAVE_GET_OS_VERSION_STR
47 #error define of HAVE_GET_OS_VERSION_STR missing in config.h
48 #endif
49
50 // Return build host and OS version as static string
51 const char * get_os_version_str()
52 {
53 static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2-sp2.1")+13];
54 char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
55 const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
56
57 OSVERSIONINFOEXA vi;
58 const char * w;
59
60 // remove "-pc" to avoid long lines
61 assert(!strncmp(SMARTMONTOOLS_BUILD_HOST+5, "pc-", 3));
62 strcpy(vstr, "i686-"); strcpy(vstr+5, SMARTMONTOOLS_BUILD_HOST+5+3);
63 assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
64
65 memset(&vi, 0, sizeof(vi));
66 vi.dwOSVersionInfoSize = sizeof(vi);
67 if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
68 memset(&vi, 0, sizeof(vi));
69 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
70 if (!GetVersionExA((OSVERSIONINFOA *)&vi))
71 return vstr;
72 }
73
74 if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
75 return vstr;
76
77 switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
78 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
79 w = (vi.szCSDVersion[1] == 'B' ||
80 vi.szCSDVersion[1] == 'C' ? "95-osr2" : "95"); break;
81 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
82 w = (vi.szCSDVersion[1] == 'A' ? "98se" : "98"); break;
83 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me"; break;
84 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
85 case VER_PLATFORM_WIN32_NT <<16|0x0400| 0: w = "nt4"; break;
86 case VER_PLATFORM_WIN32_NT <<16|0x0500| 0: w = "2000"; break;
87 case VER_PLATFORM_WIN32_NT <<16|0x0500| 1:
88 w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
89 : "xp-mc"); break;
90 case VER_PLATFORM_WIN32_NT <<16|0x0500| 2:
91 w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
92 : "2003r2"); break;
93 case VER_PLATFORM_WIN32_NT <<16|0x0600| 0: w = "vista"; break;
94 default: w = 0; break;
95 }
96
97 if (!w)
98 snprintf(vptr, vlen, "-%s%lu.%lu",
99 (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
100 vi.dwMajorVersion, vi.dwMinorVersion);
101 else if (vi.wServicePackMinor)
102 snprintf(vptr, vlen, "-%s-sp%u.%u", w, vi.wServicePackMajor, vi.wServicePackMinor);
103 else if (vi.wServicePackMajor)
104 snprintf(vptr, vlen, "-%s-sp%u", w, vi.wServicePackMajor);
105 else
106 snprintf(vptr, vlen, "-%s", w);
107 return vstr;
108 }
109
110
111 static int ata_open(int drive);
112 static void ata_close(int fd);
113 static int ata_scan(unsigned long * drives);
114
115 static int aspi_open(unsigned adapter, unsigned id);
116 static void aspi_close(int fd);
117 static int aspi_scan(unsigned long * drives);
118
119
120 static int is_permissive()
121 {
122 if (con->permissive <= 0) {
123 pout("To continue, add one or more '-T permissive' options.\n");
124 return 0;
125 }
126 con->permissive--;
127 return 1;
128 }
129
130 static const char * skipdev(const char * s)
131 {
132 return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
133 }
134
135
136 // tries to guess device type given the name (a path). See utility.h
137 // for return values.
138 int guess_device_type (const char * dev_name)
139 {
140 dev_name = skipdev(dev_name);
141 if (!strncmp(dev_name, "hd", 2))
142 return CONTROLLER_ATA;
143 if (!strncmp(dev_name, "scsi", 4))
144 return CONTROLLER_SCSI;
145 return CONTROLLER_UNKNOWN;
146 }
147
148
149 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
150 // smartd. Returns number N of devices, or -1 if out of
151 // memory. Allocates N+1 arrays: one of N pointers (devlist), the
152 // others each contain null-terminated character strings.
153 int make_device_names (char*** devlist, const char* type)
154 {
155 unsigned long drives[3];
156 int i, j, n, nmax, sz;
157 const char * path;
158
159 drives[0] = drives[1] = drives[2] = 0;
160 if (!strcmp(type, "ATA")) {
161 // bit i set => drive i present
162 n = ata_scan(drives);
163 path = "/dev/hda";
164 nmax = 10;
165 }
166 else if (!strcmp(type, "SCSI")) {
167 // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
168 n = aspi_scan(drives);
169 path = "/dev/scsi00";
170 nmax = 10*8;
171 }
172 else
173 return -1;
174
175 if (n <= 0)
176 return 0;
177
178 // Alloc devlist
179 sz = n * sizeof(char **);
180 *devlist = (char **)malloc(sz); bytes += sz;
181
182 // Add devices
183 for (i = j = 0; i < n; i++) {
184 char * s;
185 sz = strlen(path)+1;
186 s = (char *)malloc(sz); bytes += sz;
187 strcpy(s, path);
188 while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f))))
189 j++;
190 assert(j < nmax);
191 if (nmax <= 10) {
192 assert(j <= 9);
193 s[sz-2] += j; // /dev/hd[a-j]
194 }
195 else {
196 assert((j >> 3) <= 9);
197 s[sz-3] += (j >> 3); // /dev/scsi[0-9].....
198 s[sz-2] += (j & 0x7); // .....[0-7]
199 }
200 (*devlist)[i] = s;
201 j++;
202 }
203 return n;
204 }
205
206
207 // Like open(). Return positive integer handle, only used by
208 // functions below. type="ATA" or "SCSI". If you need to store extra
209 // information about your devices, create a private internal array
210 // within this file (see os_freebsd.c for an example).
211 int deviceopen(const char * pathname, char *type)
212 {
213 int len;
214 pathname = skipdev(pathname);
215 len = strlen(pathname);
216
217 if (!strcmp(type, "ATA")) {
218 // hd[a-z] => ATA 0-9
219 if (!(len == 3 && pathname[0] == 'h' && pathname[1] == 'd'
220 && 'a' <= pathname[2] && pathname[2] <= 'j')) {
221 errno = ENOENT;
222 return -1;
223 }
224 return ata_open(pathname[2] - 'a');
225 }
226
227 if (!strcmp(type, "SCSI")) {
228 // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0
229 unsigned adapter = ~0, id = ~0; int n = -1;
230 if (!(sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len)) {
231 errno = ENOENT;
232 return -1;
233 }
234 return aspi_open(adapter, id);
235 }
236 errno = ENOENT;
237 return -1;
238 }
239
240
241 // Like close(). Acts only on handles returned by above function.
242 // (Never called in smartctl!)
243 int deviceclose(int fd)
244 {
245 if (fd < 0x100) {
246 ata_close(fd);
247 }
248 else {
249 aspi_close(fd);
250 }
251 return 0;
252 }
253
254
255 // print examples for smartctl
256 void print_smartctl_examples(){
257 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
258 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
259 #ifdef HAVE_GETOPT_LONG
260 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
261 " (Enables SMART on first disk)\n\n"
262 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
263 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
264 " (Prints Self-Test & Attribute errors)\n"
265 #else
266 " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n"
267 " smartctl -t long /dev/hda (Executes extended disk self-test)\n"
268 " smartctl -A -l selftest -q errorsonly /dev/hda\n"
269 " (Prints Self-Test & Attribute errors)\n"
270 #endif
271 " smartctl -a /dev/scsi21\n"
272 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
273 );
274 }
275
276
277 /////////////////////////////////////////////////////////////////////////////
278 // ATA Interface
279 /////////////////////////////////////////////////////////////////////////////
280
281 // SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
282
283 // Deklarations from:
284 // http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3
285
286 #define FILE_READ_ACCESS 0x0001
287 #define FILE_WRITE_ACCESS 0x0002
288 #define METHOD_BUFFERED 0
289 #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
290
291 #define FILE_DEVICE_DISK 7
292 #define IOCTL_DISK_BASE FILE_DEVICE_DISK
293
294 #define SMART_GET_VERSION \
295 CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
296
297 #define SMART_RCV_DRIVE_DATA \
298 CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
299
300 #define SMART_SEND_DRIVE_COMMAND \
301 CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
302
303 #define SMART_CYL_LOW 0x4F
304 #define SMART_CYL_HI 0xC2
305
306 #pragma pack(1)
307
308 typedef struct _GETVERSIONOUTPARAMS {
309 UCHAR bVersion;
310 UCHAR bRevision;
311 UCHAR bReserved;
312 UCHAR bIDEDeviceMap;
313 ULONG fCapabilities;
314 ULONG dwReserved[4];
315 } GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
316
317 typedef struct _IDEREGS {
318 UCHAR bFeaturesReg;
319 UCHAR bSectorCountReg;
320 UCHAR bSectorNumberReg;
321 UCHAR bCylLowReg;
322 UCHAR bCylHighReg;
323 UCHAR bDriveHeadReg;
324 UCHAR bCommandReg;
325 UCHAR bReserved;
326 } IDEREGS, *PIDEREGS, *LPIDEREGS;
327
328 typedef struct _SENDCMDINPARAMS {
329 ULONG cBufferSize;
330 IDEREGS irDriveRegs;
331 UCHAR bDriveNumber;
332 UCHAR bReserved[3];
333 ULONG dwReserved[4];
334 UCHAR bBuffer[1];
335 } SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
336
337 /* DRIVERSTATUS.bDriverError constants (just for info, not used)
338 #define SMART_NO_ERROR 0
339 #define SMART_IDE_ERROR 1
340 #define SMART_INVALID_FLAG 2
341 #define SMART_INVALID_COMMAND 3
342 #define SMART_INVALID_BUFFER 4
343 #define SMART_INVALID_DRIVE 5
344 #define SMART_INVALID_IOCTL 6
345 #define SMART_ERROR_NO_MEM 7
346 #define SMART_INVALID_REGISTER 8
347 #define SMART_NOT_SUPPORTED 9
348 #define SMART_NO_IDE_DEVICE 10
349 */
350
351 typedef struct _DRIVERSTATUS {
352 UCHAR bDriverError;
353 UCHAR bIDEError;
354 UCHAR bReserved[2];
355 ULONG dwReserved[2];
356 } DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
357
358 typedef struct _SENDCMDOUTPARAMS {
359 ULONG cBufferSize;
360 DRIVERSTATUS DriverStatus;
361 UCHAR bBuffer[1];
362 } SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
363
364 #pragma pack()
365
366
367 /////////////////////////////////////////////////////////////////////////////
368
369 static void print_ide_regs(const IDEREGS * r, int out)
370 {
371 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
372 (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
373 r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
374 }
375
376 static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
377 {
378 pout(" Input : "); print_ide_regs(ri, 0);
379 if (ro) {
380 pout(" Output: "); print_ide_regs(ro, 1);
381 }
382 }
383
384 /////////////////////////////////////////////////////////////////////////////
385
386 // call SMART_* ioctl
387
388 static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize)
389 {
390 SENDCMDINPARAMS inpar;
391 unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
392 const SENDCMDOUTPARAMS * outpar;
393 DWORD code, num_out;
394 unsigned int size_out;
395 const char * name;
396
397 assert(SMART_SEND_DRIVE_COMMAND == 0x07c084);
398 assert(SMART_RCV_DRIVE_DATA == 0x07c088);
399 assert(sizeof(SENDCMDINPARAMS)-1 == 32);
400 assert(sizeof(SENDCMDOUTPARAMS)-1 == 16);
401
402 memset(&inpar, 0, sizeof(inpar));
403 inpar.irDriveRegs = *regs;
404 // drive is set to 0-3 on Win9x only
405 inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
406 inpar.bDriveNumber = drive;
407
408 assert(datasize == 0 || datasize == 512);
409 if (datasize) {
410 code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
411 inpar.cBufferSize = size_out = 512;
412 }
413 else {
414 code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
415 if (regs->bFeaturesReg == ATA_SMART_STATUS)
416 size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
417 // Note: cBufferSize must be 0 on Win9x
418 else
419 size_out = 0;
420 }
421
422 memset(&outbuf, 0, sizeof(outbuf));
423
424 if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
425 outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
426 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
427 long err = GetLastError();
428 if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) {
429 pout(" %s failed, Error=%ld\n", name, err);
430 print_ide_regs_io(regs, NULL);
431 }
432 errno = ( err == ERROR_INVALID_FUNCTION /*9x*/
433 || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO);
434 return -1;
435 }
436 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
437
438 outpar = (const SENDCMDOUTPARAMS *)outbuf;
439
440 if (outpar->DriverStatus.bDriverError) {
441 if (con->reportataioctl) {
442 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
443 outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
444 print_ide_regs_io(regs, NULL);
445 }
446 errno = EIO;
447 return -1;
448 }
449
450 if (con->reportataioctl > 1) {
451 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
452 num_out, outpar->cBufferSize);
453 print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
454 (const IDEREGS *)(outpar->bBuffer) : NULL));
455 }
456
457 if (datasize)
458 memcpy(data, outpar->bBuffer, 512);
459 else if (regs->bFeaturesReg == ATA_SMART_STATUS)
460 *regs = *(const IDEREGS *)(outpar->bBuffer);
461
462 return 0;
463 }
464
465
466 /////////////////////////////////////////////////////////////////////////////
467
468 // IDE PASS THROUGH for W2K/XP (does not work on W9x/NT4)
469 // Only used for SMART commands not supported by SMART_* IOCTLs
470 //
471 // Based on WinATA.cpp, 2002 c't/Matthias Withopf
472 // ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
473
474 #define FILE_DEVICE_CONTROLLER 4
475 #define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
476
477 #define IOCTL_IDE_PASS_THROUGH \
478 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
479
480 #pragma pack(1)
481
482 typedef struct {
483 IDEREGS IdeReg;
484 ULONG DataBufferSize;
485 UCHAR DataBuffer[1];
486 } ATA_PASS_THROUGH;
487
488 #pragma pack()
489
490
491 /////////////////////////////////////////////////////////////////////////////
492
493 static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
494 {
495 unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
496 ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
497 DWORD num_out;
498 const unsigned char magic = 0xcf;
499 assert(sizeof(ATA_PASS_THROUGH)-1 == 12);
500 assert(IOCTL_IDE_PASS_THROUGH == 0x04d028);
501
502 if (!buf) {
503 errno = ENOMEM;
504 return -1;
505 }
506
507 buf->IdeReg = *regs;
508 buf->DataBufferSize = datasize;
509 if (datasize)
510 buf->DataBuffer[0] = magic;
511
512 if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
513 buf, size, buf, size, &num_out, NULL)) {
514 long err = GetLastError();
515 if (con->reportataioctl)
516 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
517 VirtualFree(buf, 0, MEM_RELEASE);
518 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
519 return -1;
520 }
521
522 // Check ATA status
523 if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
524 if (con->reportataioctl) {
525 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
526 print_ide_regs_io(regs, &buf->IdeReg);
527 }
528 VirtualFree(buf, 0, MEM_RELEASE);
529 errno = EIO;
530 return -1;
531 }
532
533 // Check and copy data
534 if (datasize) {
535 if ( num_out != size
536 || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
537 if (con->reportataioctl) {
538 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
539 num_out, buf->DataBufferSize);
540 print_ide_regs_io(regs, &buf->IdeReg);
541 }
542 VirtualFree(buf, 0, MEM_RELEASE);
543 errno = EIO;
544 return -1;
545 }
546 memcpy(data, buf->DataBuffer, datasize);
547 }
548
549 if (con->reportataioctl > 1) {
550 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
551 num_out, buf->DataBufferSize);
552 print_ide_regs_io(regs, &buf->IdeReg);
553 }
554 *regs = buf->IdeReg;
555
556 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
557 VirtualFree(buf, 0, MEM_RELEASE);
558 return 0;
559 }
560
561
562
563 /////////////////////////////////////////////////////////////////////////////
564
565 // ATA PASS THROUGH via SCSI PASS THROUGH for NT4 only
566 // Only used for SMART commands not supported by SMART_* IOCTLs
567
568 // Declarations from:
569 // http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddscsi.h?rev=1.2
570
571 #define IOCTL_SCSI_PASS_THROUGH \
572 CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
573
574 #define SCSI_IOCTL_DATA_OUT 0
575 #define SCSI_IOCTL_DATA_IN 1
576 #define SCSI_IOCTL_DATA_UNSPECIFIED 2
577 // undocumented SCSI opcode to for ATA passthrough
578 #define SCSIOP_ATA_PASSTHROUGH 0xCC
579
580 typedef struct _SCSI_PASS_THROUGH {
581 USHORT Length;
582 UCHAR ScsiStatus;
583 UCHAR PathId;
584 UCHAR TargetId;
585 UCHAR Lun;
586 UCHAR CdbLength;
587 UCHAR SenseInfoLength;
588 UCHAR DataIn;
589 ULONG DataTransferLength;
590 ULONG TimeOutValue;
591 ULONG/*_PTR*/ DataBufferOffset;
592 ULONG SenseInfoOffset;
593 UCHAR Cdb[16];
594 } SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
595
596
597 /////////////////////////////////////////////////////////////////////////////
598
599 static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
600 {
601 typedef struct {
602 SCSI_PASS_THROUGH spt;
603 ULONG Filler;
604 UCHAR ucSenseBuf[32];
605 UCHAR ucDataBuf[512];
606 } SCSI_PASS_THROUGH_WITH_BUFFERS;
607
608 SCSI_PASS_THROUGH_WITH_BUFFERS sb;
609 IDEREGS * cdbregs;
610 unsigned int size;
611 DWORD num_out;
612 const unsigned char magic = 0xcf;
613
614 assert(sizeof(SCSI_PASS_THROUGH) == 44);
615 assert(IOCTL_SCSI_PASS_THROUGH == 0x04d004);
616
617 memset(&sb, 0, sizeof(sb));
618 sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
619 //sb.spt.PathId = 0;
620 sb.spt.TargetId = 1;
621 //sb.spt.Lun = 0;
622 sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
623 sb.spt.TimeOutValue = 10;
624 sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
625 size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
626 sb.spt.DataBufferOffset = size;
627
628 if (datasize) {
629 if (datasize > sizeof(sb.ucDataBuf)) {
630 errno = EINVAL;
631 return -1;
632 }
633 sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
634 sb.spt.DataTransferLength = datasize;
635 size += datasize;
636 sb.ucDataBuf[0] = magic;
637 }
638 else {
639 sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
640 //sb.spt.DataTransferLength = 0;
641 }
642
643 // Use pseudo SCSI command followed by registers
644 sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
645 cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
646 *cdbregs = *regs;
647
648 if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
649 &sb, size, &sb, size, &num_out, NULL)) {
650 long err = GetLastError();
651 if (con->reportataioctl)
652 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
653 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
654 return -1;
655 }
656
657 // Cannot check ATA status, because command does not return IDEREGS
658
659 // Check and copy data
660 if (datasize) {
661 if ( num_out != size
662 || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
663 if (con->reportataioctl) {
664 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
665 print_ide_regs_io(regs, NULL);
666 }
667 errno = EIO;
668 return -1;
669 }
670 memcpy(data, sb.ucDataBuf, datasize);
671 }
672
673 if (con->reportataioctl > 1) {
674 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
675 print_ide_regs_io(regs, NULL);
676 }
677 return 0;
678 }
679
680
681 /////////////////////////////////////////////////////////////////////////////
682
683 static HANDLE h_ata_ioctl = 0;
684
685
686 // Print SMARTVSD error message, return errno
687
688 static int smartvsd_error()
689 {
690 char path[MAX_PATH];
691 unsigned len;
692 if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
693 return ENOENT;
694 // SMARTVSD.VXD present?
695 strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
696 if (!access(path, 0)) {
697 // Yes, standard IDE driver used?
698 HANDLE h;
699 if ( (h = CreateFileA("\\\\.\\ESDI_506",
700 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
701 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
702 && GetLastError() == ERROR_FILE_NOT_FOUND ) {
703 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
704 return ENOENT;
705 }
706 else {
707 if (h != INVALID_HANDLE_VALUE) // should not happen
708 CloseHandle(h);
709 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
710 return ENOSYS;
711 }
712 }
713 else {
714 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
715 // http://support.microsoft.com/default.aspx?scid=kb;en-us;265854
716 strcpy(path+len, "\\SMARTVSD.VXD");
717 if (!access(path, 0)) {
718 path[len] = 0;
719 pout("SMART driver is not properly installed,\n"
720 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
721 " and reboot Windows.\n", path, path);
722 }
723 else {
724 path[len] = 0;
725 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
726 }
727 return ENOSYS;
728 }
729 }
730
731
732 static int ata_open(int drive)
733 {
734 int win9x;
735 char devpath[30];
736 GETVERSIONOUTPARAMS vers;
737 DWORD num_out;
738
739 assert(SMART_GET_VERSION == 0x074080);
740 assert(sizeof(GETVERSIONOUTPARAMS) == 24);
741
742 // TODO: This version does not allow to open more than 1 ATA devices
743 if (h_ata_ioctl) {
744 errno = ENFILE;
745 return -1;
746 }
747
748 win9x = ((GetVersion() & 0x80000000) != 0);
749
750 if (!(0 <= drive && drive <= (win9x ? 3 : 9))) {
751 errno = ENOENT;
752 return -1;
753 }
754 // path depends on Windows Version
755 if (win9x)
756 strcpy(devpath, "\\\\.\\SMARTVSD");
757 else
758 snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive);
759
760 // Open device
761 if ((h_ata_ioctl = CreateFileA(devpath,
762 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
763 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
764 long err = GetLastError();
765 pout("Cannot open device %s, Error=%ld\n", devpath, err);
766 if (err == ERROR_FILE_NOT_FOUND)
767 errno = (win9x ? smartvsd_error() : ENOENT);
768 else if (err == ERROR_ACCESS_DENIED) {
769 if (!win9x)
770 pout("Administrator rights are necessary to access physical drives.\n");
771 errno = EACCES;
772 }
773 else
774 errno = EIO;
775 h_ata_ioctl = 0;
776 return -1;
777 }
778
779 // Get drive map
780 memset(&vers, 0, sizeof(vers));
781 if (!DeviceIoControl(h_ata_ioctl, SMART_GET_VERSION,
782 NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
783 pout("%s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
784 if (!win9x)
785 pout("If this is a SCSI disk, try \"scsi<adapter><id>\".\n");
786 if (!is_permissive()) {
787 CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
788 errno = ENOSYS;
789 return -1;
790 }
791 }
792
793 if (con->reportataioctl > 1)
794 pout("%s: SMART_GET_VERSION (%ld bytes):\n"
795 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
796 devpath, num_out, vers.bVersion, vers.bRevision,
797 vers.fCapabilities, vers.bIDEDeviceMap);
798
799 // TODO: Check vers.fCapabilities here?
800
801 if (!win9x)
802 // NT4/2K/XP: Drive exists, Drive number not necessary for ioctl
803 return 0;
804
805 // Win9x/ME: Check device presence & type
806 if (((vers.bIDEDeviceMap >> drive) & 0x11) != 0x01) {
807 unsigned char atapi = (vers.bIDEDeviceMap >> drive) & 0x10;
808 pout(( atapi
809 ? "Drive %d is an ATAPI device (IDEDeviceMap=0x%02x).\n"
810 : "Drive %d does not exist (IDEDeviceMap=0x%02x).\n"),
811 drive, vers.bIDEDeviceMap);
812 // Win9x Drive existence check may not work as expected
813 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
814 // http://support.microsoft.com/support/kb/articles/Q196/1/20.ASP
815 if (!is_permissive()) {
816 CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
817 errno = (atapi ? ENOSYS : ENOENT);
818 return -1;
819 }
820 }
821 // Use drive number as fd for ioctl
822 return drive;
823 }
824
825
826 static void ata_close(int fd)
827 {
828 ARGUSED(fd);
829 CloseHandle(h_ata_ioctl);
830 h_ata_ioctl = 0;
831 }
832
833
834 // Scan for ATA drives, fill bitmask of drives present, return #drives
835
836 static int ata_scan(unsigned long * drives)
837 {
838 int win9x = ((GetVersion() & 0x80000000) != 0);
839 int cnt = 0, i;
840
841 for (i = 0; i <= 9; i++) {
842 char devpath[30];
843 GETVERSIONOUTPARAMS vers;
844 DWORD num_out;
845 HANDLE h;
846 if (win9x)
847 strcpy(devpath, "\\\\.\\SMARTVSD");
848 else
849 snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i);
850
851 // Open device
852 if ((h = CreateFileA(devpath,
853 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
854 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
855 if (con->reportataioctl > 1)
856 pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
857 if (win9x)
858 break; // SMARTVSD.VXD missing or no ATA devices
859 continue; // Disk not found or access denied (break;?)
860 }
861
862 // Get drive map
863 memset(&vers, 0, sizeof(vers));
864 if (!DeviceIoControl(h, SMART_GET_VERSION,
865 NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
866 if (con->reportataioctl)
867 pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
868 CloseHandle(h);
869 if (win9x)
870 break; // Should not happen
871 continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk)
872 }
873 CloseHandle(h);
874
875 if (con->reportataioctl)
876 pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
877 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
878 devpath, num_out, vers.bVersion, vers.bRevision,
879 vers.fCapabilities, vers.bIDEDeviceMap);
880
881 if (win9x) {
882 // Check ATA device presence, remove ATAPI devices
883 drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf);
884 cnt = (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
885 break;
886 }
887
888 // ATA drive exists and driver supports SMART ioctl
889 drives[0] |= (1L << i);
890 cnt++;
891 }
892
893 return cnt;
894 }
895
896
897 /////////////////////////////////////////////////////////////////////////////
898
899 // Interface to ATA devices. See os_linux.c
900 int ata_command_interface(int fd, smart_command_set command, int select, char * data)
901 {
902 IDEREGS regs;
903 int copydata, try_ioctl;
904
905 if (!(0 <= fd && fd <= 3)) {
906 errno = EBADF;
907 return -1;
908 }
909
910 // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
911 memset(&regs, 0, sizeof(regs));
912 regs.bCommandReg = ATA_SMART_CMD;
913 regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
914 copydata = 0;
915 try_ioctl = 0x01; // 0x01=SMART_*, 0x02=IDE_PASS_THROUGH [, 0x04=ATA_PASS_THROUGH]
916
917 switch (command) {
918 case WRITE_LOG:
919 // TODO. Not supported by SMART IOCTL (no data out ioctl available),
920 // also not supported by IOCTL_IDE_PASS_THROUGH (data out not working)
921 errno = ENOSYS;
922 return -1;
923 case CHECK_POWER_MODE:
924 regs.bCommandReg = ATA_CHECK_POWER_MODE;
925 regs.bCylLowReg = regs.bCylHighReg = 0;
926 try_ioctl = 0x02; // IOCTL_IDE_PASS_THROUGH
927 // Note: returns SectorCountReg in data[0]
928 break;
929 case READ_VALUES:
930 regs.bFeaturesReg = ATA_SMART_READ_VALUES;
931 regs.bSectorNumberReg = regs.bSectorCountReg = 1;
932 copydata = 1;
933 break;
934 case READ_THRESHOLDS:
935 regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS;
936 regs.bSectorNumberReg = regs.bSectorCountReg = 1;
937 copydata = 1;
938 break;
939 case READ_LOG:
940 regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR;
941 regs.bSectorNumberReg = select;
942 regs.bSectorCountReg = 1;
943 // Read log only supported on Win9x, retry with pass through command
944 try_ioctl = 0x03; // SMART_RCV_DRIVE_DATA, then IOCTL_IDE_PASS_THROUGH
945 copydata = 1;
946 break;
947 case IDENTIFY:
948 // Note: WinNT4/2000/XP return identify data cached during boot
949 // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH)
950 regs.bCommandReg = ATA_IDENTIFY_DEVICE;
951 regs.bCylLowReg = regs.bCylHighReg = 0;
952 regs.bSectorCountReg = 1;
953 copydata = 1;
954 break;
955 case PIDENTIFY:
956 regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE;
957 regs.bCylLowReg = regs.bCylHighReg = 0;
958 regs.bSectorCountReg = 1;
959 copydata = 1;
960 break;
961 case ENABLE:
962 regs.bFeaturesReg = ATA_SMART_ENABLE;
963 regs.bSectorNumberReg = 1;
964 break;
965 case DISABLE:
966 regs.bFeaturesReg = ATA_SMART_DISABLE;
967 regs.bSectorNumberReg = 1;
968 break;
969 case STATUS:
970 case STATUS_CHECK:
971 regs.bFeaturesReg = ATA_SMART_STATUS;
972 break;
973 case AUTO_OFFLINE:
974 regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE;
975 regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
976 break;
977 case AUTOSAVE:
978 regs.bFeaturesReg = ATA_SMART_AUTOSAVE;
979 regs.bSectorCountReg = select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
980 break;
981 case IMMEDIATE_OFFLINE:
982 regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE;
983 regs.bSectorNumberReg = select;
984 if (select == ABORT_SELF_TEST) // Abort only supported on Win9x, try
985 try_ioctl = 0x03; // SMART_SEND_DRIVE_COMMAND, then IOCTL_IDE_PASS_THROUGH
986 break;
987 default:
988 pout("Unrecognized command %d in win32_ata_command_interface()\n"
989 "Please contact " PACKAGE_BUGREPORT "\n", command);
990 errno = ENOSYS;
991 return -1;
992 }
993
994 if (try_ioctl & 0x01) {
995 if (smart_ioctl(h_ata_ioctl, fd, &regs, data, (copydata?512:0))) {
996 if (!(try_ioctl & 0x02) || errno != ENOSYS)
997 return -1;
998 // CAUTION: smart_ioctl() MUST NOT change "regs" Parameter in this case
999 }
1000 else
1001 try_ioctl = 0;
1002 }
1003
1004 if (try_ioctl & 0x02) {
1005 errno = 0;
1006 if ((GetVersion() & 0x8000ffff) == 0x00000004) {
1007 // Special case WinNT4
1008 if (command == CHECK_POWER_MODE) { // SCSI_PASS_THROUGH does not return regs!
1009 errno = ENOSYS;
1010 return -1;
1011 }
1012 if (ata_via_scsi_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
1013 return -1;
1014 }
1015 else {
1016 if (ide_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
1017 return -1;
1018 }
1019 try_ioctl = 0;
1020 }
1021 assert(!try_ioctl);
1022
1023 switch (command) {
1024 case CHECK_POWER_MODE:
1025 // Return power mode from SectorCountReg in data[0]
1026 data[0] = regs.bSectorCountReg;
1027 return 0;
1028
1029 case STATUS_CHECK:
1030 // Cyl low and Cyl high unchanged means "Good SMART status"
1031 if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW)
1032 return 0;
1033
1034 // These values mean "Bad SMART status"
1035 if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4)
1036 return 1;
1037
1038 // We haven't gotten output that makes sense; print out some debugging info
1039 syserror("Error SMART Status command failed");
1040 pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE);
1041 print_ide_regs(&regs, 1);
1042 errno = EIO;
1043 return -1;
1044
1045 default:
1046 return 0;
1047 }
1048 /*NOTREACHED*/
1049 }
1050
1051
1052 #ifndef HAVE_ATA_IDENTIFY_IS_CACHED
1053 #error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
1054 #endif
1055
1056 // Return true if OS caches the ATA identify sector
1057 int ata_identify_is_cached(int fd)
1058 {
1059 ARGUSED(fd);
1060 // WinNT4/2000/XP => true, Win9x/ME => false
1061 return ((GetVersion() & 0x80000000) == 0);
1062 }
1063
1064
1065 // Print not implemeted warning once
1066 static void pr_not_impl(const char * what, int * warned)
1067 {
1068 if (*warned)
1069 return;
1070 pout(
1071 "#######################################################################\n"
1072 "%s\n"
1073 "NOT IMPLEMENTED under Win32.\n"
1074 "Please contact " PACKAGE_BUGREPORT " if\n"
1075 "you want to help in porting smartmontools to Win32.\n"
1076 "#######################################################################\n"
1077 "\n", what
1078 );
1079 *warned = 1;
1080 }
1081
1082 // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c
1083 int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
1084 {
1085 static int warned = 0;
1086 ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data);
1087 pr_not_impl("3ware Escalade Controller command routine escalade_command_interface()", &warned);
1088 errno = ENOSYS;
1089 return -1;
1090 }
1091
1092 // Interface to ATA devices behind Marvell chip-set based controllers. See os_linux.c
1093 int marvell_command_interface(int fd, smart_command_set command, int select, char * data)
1094 {
1095 static int warned = 0;
1096 ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
1097 pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
1098 errno = ENOSYS;
1099 return -1;
1100 }
1101
1102
1103 /////////////////////////////////////////////////////////////////////////////
1104 // ASPI Interface
1105 /////////////////////////////////////////////////////////////////////////////
1106
1107 #pragma pack(1)
1108
1109 #define ASPI_SENSE_SIZE 18
1110
1111 // ASPI SCSI Request block header
1112
1113 typedef struct {
1114 unsigned char cmd; // 00: Command code
1115 unsigned char status; // 01: ASPI status
1116 unsigned char adapter; // 02: Host adapter number
1117 unsigned char flags; // 03: Request flags
1118 unsigned char reserved[4]; // 04: 0
1119 } ASPI_SRB_HEAD;
1120
1121 // SRB for host adapter inquiry
1122
1123 typedef struct {
1124 ASPI_SRB_HEAD h; // 00: Header
1125 unsigned char adapters; // 08: Number of adapters
1126 unsigned char target_id; // 09: Target ID ?
1127 char manager_id[16]; // 10: SCSI manager ID
1128 char adapter_id[16]; // 26: Host adapter ID
1129 unsigned char parameters[16]; // 42: Host adapter unique parmameters
1130 } ASPI_SRB_INQUIRY;
1131
1132 // SRB for get device type
1133
1134 typedef struct {
1135 ASPI_SRB_HEAD h; // 00: Header
1136 unsigned char target_id; // 08: Target ID
1137 unsigned char lun; // 09: LUN
1138 unsigned char devtype; // 10: Device type
1139 unsigned char reserved; // 11: Reserved
1140 } ASPI_SRB_DEVTYPE;
1141
1142 // SRB for SCSI I/O
1143
1144 typedef struct {
1145 ASPI_SRB_HEAD h; // 00: Header
1146 unsigned char target_id; // 08: Target ID
1147 unsigned char lun; // 09: LUN
1148 unsigned char reserved[2]; // 10: Reserved
1149 unsigned long data_size; // 12: Data alloc. lenght
1150 void * data_addr; // 16: Data buffer pointer
1151 unsigned char sense_size; // 20: Sense alloc. length
1152 unsigned char cdb_size; // 21: CDB length
1153 unsigned char host_status; // 22: Host status
1154 unsigned char target_status; // 23: Target status
1155 void * event_handle; // 24: Event handle
1156 unsigned char workspace[20]; // 28: ASPI workspace
1157 unsigned char cdb[16+ASPI_SENSE_SIZE];
1158 } ASPI_SRB_IO;
1159
1160 // Macro to retrieve start of sense information
1161 #define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
1162
1163 // SRB union
1164
1165 typedef union {
1166 ASPI_SRB_HEAD h; // Common header
1167 ASPI_SRB_INQUIRY q; // Inquiry
1168 ASPI_SRB_DEVTYPE t; // Device type
1169 ASPI_SRB_IO i; // I/O
1170 } ASPI_SRB;
1171
1172 #pragma pack()
1173
1174 // ASPI commands
1175 #define ASPI_CMD_ADAPTER_INQUIRE 0x00
1176 #define ASPI_CMD_GET_DEVICE_TYPE 0x01
1177 #define ASPI_CMD_EXECUTE_IO 0x02
1178 #define ASPI_CMD_ABORT_IO 0x03
1179
1180 // Request flags
1181 #define ASPI_REQFLAG_DIR_TO_HOST 0x08
1182 #define ASPI_REQFLAG_DIR_TO_TARGET 0x10
1183 #define ASPI_REQFLAG_DIR_NO_XFER 0x18
1184 #define ASPI_REQFLAG_EVENT_NOTIFY 0x40
1185
1186 // ASPI status
1187 #define ASPI_STATUS_IN_PROGRESS 0x00
1188 #define ASPI_STATUS_NO_ERROR 0x01
1189 #define ASPI_STATUS_ABORTED 0x02
1190 #define ASPI_STATUS_ABORT_ERR 0x03
1191 #define ASPI_STATUS_ERROR 0x04
1192 #define ASPI_STATUS_INVALID_COMMAND 0x80
1193 #define ASPI_STATUS_INVALID_ADAPTER 0x81
1194 #define ASPI_STATUS_INVALID_TARGET 0x82
1195 #define ASPI_STATUS_NO_ADAPTERS 0xE8
1196
1197 // Adapter (host) status
1198 #define ASPI_HSTATUS_NO_ERROR 0x00
1199 #define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
1200 #define ASPI_HSTATUS_DATA_OVERRUN 0x12
1201 #define ASPI_HSTATUS_BUS_FREE 0x13
1202 #define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
1203 #define ASPI_HSTATUS_BAD_SGLIST 0x1A
1204
1205 // Target status
1206 #define ASPI_TSTATUS_NO_ERROR 0x00
1207 #define ASPI_TSTATUS_CHECK_CONDITION 0x02
1208 #define ASPI_TSTATUS_BUSY 0x08
1209 #define ASPI_TSTATUS_RESERV_CONFLICT 0x18
1210
1211
1212 static HINSTANCE h_aspi_dll; // DLL handle
1213 static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
1214 static unsigned num_aspi_adapters;
1215
1216 #ifdef __CYGWIN__
1217 // h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
1218 static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
1219 #define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
1220 #else
1221 #define aspi_entry_valid() (!!aspi_entry)
1222 #endif
1223
1224
1225 static int aspi_call(ASPI_SRB * srb)
1226 {
1227 int i;
1228 aspi_entry(srb);
1229 i = 0;
1230 while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
1231 if (++i > 100/*10sek*/) {
1232 pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
1233 aspi_entry = 0;
1234 h_aspi_dll = INVALID_HANDLE_VALUE;
1235 errno = EIO;
1236 return -1;
1237 }
1238 if (con->reportscsiioctl > 1)
1239 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
1240 Sleep(100);
1241 }
1242 return 0;
1243 }
1244
1245
1246 // Get ASPI entrypoint from wnaspi32.dll
1247
1248 static FARPROC aspi_get_address(const char * name, int verbose)
1249 {
1250 FARPROC addr;
1251 assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
1252
1253 if (!(addr = GetProcAddress(h_aspi_dll, name))) {
1254 if (verbose)
1255 pout("Missing %s() in WNASPI32.DLL\n", name);
1256 aspi_entry = 0;
1257 FreeLibrary(h_aspi_dll);
1258 h_aspi_dll = INVALID_HANDLE_VALUE;
1259 errno = ENOSYS;
1260 return 0;
1261 }
1262 return addr;
1263 }
1264
1265
1266 static int aspi_open_dll(int verbose)
1267 {
1268 UINT (*aspi_info)(void);
1269 UINT info, rc;
1270
1271 assert(!aspi_entry_valid());
1272
1273 // Check structure layout
1274 assert(sizeof(ASPI_SRB_HEAD) == 8);
1275 assert(sizeof(ASPI_SRB_INQUIRY) == 58);
1276 assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
1277 assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
1278 assert(offsetof(ASPI_SRB,h.cmd) == 0);
1279 assert(offsetof(ASPI_SRB,h.flags) == 3);
1280 assert(offsetof(ASPI_SRB_IO,lun) == 9);
1281 assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
1282 assert(offsetof(ASPI_SRB_IO,workspace) == 28);
1283 assert(offsetof(ASPI_SRB_IO,cdb) == 48);
1284
1285 if (h_aspi_dll == INVALID_HANDLE_VALUE) {
1286 // do not retry
1287 errno = ENOENT;
1288 return -1;
1289 }
1290
1291 // Load ASPI DLL
1292 if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
1293 if (verbose)
1294 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
1295 h_aspi_dll = INVALID_HANDLE_VALUE;
1296 errno = ENOENT;
1297 return -1;
1298 }
1299 if (con->reportscsiioctl > 1) {
1300 // Print full path of WNASPI32.DLL
1301 char path[MAX_PATH];
1302 if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
1303 strcpy(path, "*unknown*");
1304 pout("Using ASPI interface \"%s\"\n", path);
1305 }
1306
1307 // Get ASPI entrypoints
1308 if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
1309 return -1;
1310 if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
1311 return -1;
1312
1313 // Init ASPI manager and get number of adapters
1314 info = (aspi_info)();
1315 if (con->reportscsiioctl > 1)
1316 pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
1317 rc = (info >> 8) & 0xff;
1318 if (rc == ASPI_STATUS_NO_ADAPTERS) {
1319 num_aspi_adapters = 0;
1320 }
1321 else if (rc == ASPI_STATUS_NO_ERROR) {
1322 num_aspi_adapters = info & 0xff;
1323 }
1324 else {
1325 if (verbose)
1326 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
1327 aspi_entry = 0;
1328 FreeLibrary(h_aspi_dll);
1329 h_aspi_dll = INVALID_HANDLE_VALUE;
1330 errno = ENOENT;
1331 return -1;
1332 }
1333
1334 if (con->reportscsiioctl)
1335 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
1336
1337 #ifdef __CYGWIN__
1338 // save PID to detect fork() in aspi_entry_valid()
1339 aspi_dll_pid = GetCurrentProcessId();
1340 #endif
1341 assert(aspi_entry_valid());
1342 return 0;
1343 }
1344
1345
1346 static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
1347 {
1348 HANDLE event;
1349 // Create event
1350 if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
1351 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
1352 }
1353 srb->i.event_handle = event;
1354 srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
1355 // Start ASPI request
1356 aspi_entry(srb);
1357 if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
1358 // Wait for event
1359 DWORD rc = WaitForSingleObject(event, timeout*1000L);
1360 if (rc != WAIT_OBJECT_0) {
1361 if (rc == WAIT_TIMEOUT) {
1362 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
1363 srb->h.adapter, srb->i.target_id, timeout);
1364 }
1365 else {
1366 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
1367 (unsigned long)event, rc, rc, GetLastError());
1368 }
1369 // TODO: ASPI_ABORT_IO command
1370 aspi_entry = 0;
1371 h_aspi_dll = INVALID_HANDLE_VALUE;
1372 return -EIO;
1373 }
1374 }
1375 CloseHandle(event);
1376 return 0;
1377 }
1378
1379
1380 static int aspi_open(unsigned adapter, unsigned id)
1381 {
1382 ASPI_SRB srb;
1383 if (!(adapter <= 9 && id < 16)) {
1384 errno = ENOENT;
1385 return -1;
1386 }
1387
1388 if (!aspi_entry_valid()) {
1389 if (aspi_open_dll(1/*verbose*/))
1390 return -1;
1391 }
1392
1393 // Adapter OK?
1394 if (adapter >= num_aspi_adapters) {
1395 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
1396 adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
1397 if (!is_permissive()) {
1398 errno = ENOENT;
1399 return -1;
1400 }
1401 }
1402
1403 // Device present ?
1404 memset(&srb, 0, sizeof(srb));
1405 srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
1406 srb.h.adapter = adapter; srb.i.target_id = id;
1407 if (aspi_call(&srb)) {
1408 errno = EIO;
1409 return -1;
1410 }
1411 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
1412 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
1413 if (!is_permissive()) {
1414 errno = (srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
1415 return -1;
1416 }
1417 }
1418 else if (con->reportscsiioctl)
1419 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
1420
1421 return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf));
1422 }
1423
1424
1425 static void aspi_close(int fd)
1426 {
1427 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
1428 ARGUSED(fd);
1429 }
1430
1431
1432 // Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
1433 // return #drives
1434
1435 static int aspi_scan(unsigned long * drives)
1436 {
1437 int cnt = 0;
1438 unsigned ad;
1439
1440 if (!aspi_entry_valid()) {
1441 if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
1442 return 0;
1443 }
1444
1445 for (ad = 0; ad < num_aspi_adapters; ad++) {
1446 ASPI_SRB srb; unsigned id;
1447
1448 if (ad > 9) {
1449 if (con->reportscsiioctl)
1450 pout(" ASPI Adapter %u: Ignored\n", ad);
1451 continue;
1452 }
1453
1454 // Get adapter name
1455 memset(&srb, 0, sizeof(srb));
1456 srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
1457 srb.h.adapter = ad;
1458 if (aspi_call(&srb))
1459 return 0;
1460
1461 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
1462 if (con->reportscsiioctl)
1463 pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
1464 continue;
1465 }
1466
1467 if (con->reportscsiioctl) {
1468 int i;
1469 for (i = 1; i < 16 && srb.q.adapter_id[i]; i++)
1470 if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
1471 srb.q.adapter_id[i] = '?';
1472 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
1473 }
1474
1475 for (id = 0; id <= 7; id++) {
1476 // Get device type
1477 memset(&srb, 0, sizeof(srb));
1478 srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
1479 srb.h.adapter = ad; srb.i.target_id = id;
1480 if (aspi_call(&srb))
1481 return 0;
1482 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
1483 if (con->reportscsiioctl > 1)
1484 pout(" ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
1485 continue;
1486 }
1487 if (con->reportscsiioctl)
1488 pout(" ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
1489 if (srb.t.devtype == 0x00/*HDD*/) {
1490 drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id));
1491 cnt++;
1492 }
1493 }
1494 }
1495 return cnt;
1496 }
1497
1498
1499 /////////////////////////////////////////////////////////////////////////////
1500
1501 // Interface to SCSI devices. See os_linux.c
1502 int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
1503 {
1504 ASPI_SRB srb;
1505
1506 if (!aspi_entry_valid())
1507 return -EBADF;
1508 if (!((fd & ~0xff) == 0x100))
1509 return -EBADF;
1510
1511 if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12)) {
1512 pout("do_scsi_cmnd_io: bad CDB length\n");
1513 return -EINVAL;
1514 }
1515
1516 if (report > 0) {
1517 // From os_linux.c
1518 int k, j;
1519 const unsigned char * ucp = iop->cmnd;
1520 const char * np;
1521 char buff[256];
1522 const int sz = (int)sizeof(buff);
1523
1524 np = scsi_get_opcode_name(ucp[0]);
1525 j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
1526 for (k = 0; k < (int)iop->cmnd_len; ++k)
1527 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
1528 if ((report > 1) &&
1529 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
1530 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
1531
1532 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing "
1533 "data, len=%d%s:\n", (int)iop->dxfer_len,
1534 (trunc ? " [only first 256 bytes shown]" : ""));
1535 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
1536 }
1537 else
1538 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
1539 pout(buff);
1540 }
1541
1542 memset(&srb, 0, sizeof(srb));
1543 srb.h.cmd = ASPI_CMD_EXECUTE_IO;
1544 srb.h.adapter = ((fd >> 4) & 0xf);
1545 srb.i.target_id = (fd & 0xf);
1546 //srb.i.lun = 0;
1547 srb.i.sense_size = ASPI_SENSE_SIZE;
1548 srb.i.cdb_size = iop->cmnd_len;
1549 memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
1550
1551 switch (iop->dxfer_dir) {
1552 case DXFER_NONE:
1553 srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
1554 break;
1555 case DXFER_FROM_DEVICE:
1556 srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
1557 srb.i.data_size = iop->dxfer_len;
1558 srb.i.data_addr = iop->dxferp;
1559 break;
1560 case DXFER_TO_DEVICE:
1561 srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
1562 srb.i.data_size = iop->dxfer_len;
1563 srb.i.data_addr = iop->dxferp;
1564 break;
1565 default:
1566 pout("do_scsi_cmnd_io: bad dxfer_dir\n");
1567 return -EINVAL;
1568 }
1569
1570 iop->resp_sense_len = 0;
1571 iop->scsi_status = 0;
1572 iop->resid = 0;
1573
1574 if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
1575 // Timeout
1576 return -EIO;
1577 }
1578
1579 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
1580 if ( srb.h.status == ASPI_STATUS_ERROR
1581 && srb.i.host_status == ASPI_HSTATUS_NO_ERROR
1582 && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
1583 // Sense valid
1584 const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
1585 int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
1586 iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
1587 if (len > 0 && iop->sensep) {
1588 memcpy(iop->sensep, sense, len);
1589 iop->resp_sense_len = len;
1590 if (report > 1) {
1591 pout(" >>> Sense buffer, len=%d:\n", (int)len);
1592 dStrHex(iop->sensep, len , 1);
1593 }
1594 }
1595 if (report) {
1596 pout(" sense_key=%x asc=%x ascq=%x\n",
1597 sense[2] & 0xf, sense[12], sense[13]);
1598 }
1599 return 0;
1600 }
1601 else {
1602 if (report)
1603 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
1604 return -EIO;
1605 }
1606 }
1607
1608 if (report > 0)
1609 pout(" OK\n");
1610
1611 if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
1612 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
1613 pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
1614 (trunc ? " [only first 256 bytes shown]" : ""));
1615 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
1616 }
1617
1618 return 0;
1619 }