]>
Commit | Line | Data |
---|---|---|
832b75ed GG |
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(®s, 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, ®s, 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, ®s, data, (copydata?512:0))) | |
1013 | return -1; | |
1014 | } | |
1015 | else { | |
1016 | if (ide_pass_through_ioctl(h_ata_ioctl, ®s, 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(®s, 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 | } |