]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_win32.cpp
Imported Upstream version 5.39.1+svn3060
[mirror_smartmontools-debian.git] / os_win32.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * os_win32.cpp
832b75ed
GG
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
a23d5117 6 * Copyright (C) 2004-10 Christian Franke <smartmontools-support@lists.sourceforge.net>
832b75ed
GG
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * You should have received a copy of the GNU General Public License
2127e193 14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
832b75ed
GG
15 *
16 */
17
18#include "config.h"
19#include "int64.h"
20#include "atacmds.h"
21#include "extern.h"
22extern smartmonctrl * con; // con->permissive,reportataioctl
23#include "scsicmds.h"
24#include "utility.h"
2127e193
GI
25
26#include "dev_interface.h"
27#include "dev_ata_cmd_set.h"
832b75ed
GG
28
29#include <errno.h>
2127e193 30
832b75ed
GG
31#ifdef _DEBUG
32#include <assert.h>
33#else
2127e193 34#undef assert
a23d5117 35#define assert(x) /* */
832b75ed 36#endif
2127e193 37
832b75ed
GG
38#define WIN32_LEAN_AND_MEAN
39#include <windows.h>
40#include <stddef.h> // offsetof()
41#include <io.h> // access()
42
a23d5117
GI
43#ifdef __CYGWIN__
44#include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
45#endif
46
4d59bff9
GG
47// Macro to check constants at compile time using a dummy typedef
48#define ASSERT_CONST(c, n) \
49 typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
50#define ASSERT_SIZEOF(t, n) \
51 typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
52
a23d5117
GI
53const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3049 2010-01-27 19:47:09Z chrfranke $";
54
55// Disable Win9x/ME specific code if no longer supported by compiler.
56#ifndef WIN9X_SUPPORT
57 #if defined(CYGWIN_VERSION_DLL_MAJOR) && (CYGWIN_VERSION_DLL_MAJOR >= 1007)
58 // Win9x/ME support was dropped in Cygwin 1.7
59 #elif defined(_MSC_VER) && (_MSC_VER >= 1500)
60 // Win9x/ME support was dropped in MSVC9 (cl.exe 15.0)
61 #else
62 #define WIN9X_SUPPORT 1
63 #endif
64#endif
832b75ed 65
2127e193
GI
66/////////////////////////////////////////////////////////////////////////////
67
68namespace os_win32 { // no need to publish anything, name provided for Doxygen
69
70#ifdef _MSC_VER
71#pragma warning(disable:4250)
72#endif
73
a23d5117
GI
74// Running on Win9x/ME ?
75#if WIN9X_SUPPORT
76// Set true in win9x_smart_interface ctor.
77static bool win9x = false;
78#else
79// Never true (const allows compiler to remove dead code).
80const bool win9x = false;
81#endif
82
83
2127e193
GI
84class win_smart_device
85: virtual public /*implements*/ smart_device
86{
87public:
88 win_smart_device()
89 : smart_device(never_called),
90 m_fh(INVALID_HANDLE_VALUE)
91 { }
92
93 virtual ~win_smart_device() throw();
94
95 virtual bool is_open() const;
96
97 virtual bool close();
98
99protected:
100 /// Set handle for open() in derived classes.
101 void set_fh(HANDLE fh)
102 { m_fh = fh; }
103
104 /// Return handle for derived classes.
105 HANDLE get_fh() const
106 { return m_fh; }
107
108private:
109 HANDLE m_fh; ///< File handle
110};
111
112
113/////////////////////////////////////////////////////////////////////////////
114
115class win_ata_device
116: public /*implements*/ ata_device,
117 public /*extends*/ win_smart_device
118{
119public:
120 win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
121
122 virtual ~win_ata_device() throw();
123
124 virtual bool open();
125
126 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
127
128 virtual bool ata_identify_is_cached() const;
129
130private:
131 bool open(int phydrive, int logdrive, const char * options, int port);
132
133 std::string m_options;
134 bool m_usr_options; // options set by user?
135 bool m_admin; // open with admin access?
a23d5117 136 bool m_id_is_cached; // ata_identify_is_cached() return value.
2127e193
GI
137 int m_drive, m_port;
138 int m_smartver_state;
139};
140
141
142/////////////////////////////////////////////////////////////////////////////
143
144class win_scsi_device
145: public /*implements*/ scsi_device,
146 virtual public /*extends*/ win_smart_device
147{
148public:
149 win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
150
151 virtual bool open();
152
153 virtual bool scsi_pass_through(scsi_cmnd_io * iop);
154
155private:
156 bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
157};
158
159
160/////////////////////////////////////////////////////////////////////////////
161
a23d5117
GI
162#if WIN9X_SUPPORT
163
2127e193
GI
164class win_aspi_device
165: public /*implements*/ scsi_device
166{
167public:
168 win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type);
169
170 virtual bool is_open() const;
171
172 virtual bool open();
173
174 virtual bool close();
175
176 virtual bool scsi_pass_through(scsi_cmnd_io * iop);
177
178private:
179 int m_adapter;
180 unsigned char m_id;
181};
182
a23d5117 183#endif // WIN9X_SUPPORT
2127e193
GI
184
185//////////////////////////////////////////////////////////////////////
186
187class win_tw_cli_device
188: public /*implements*/ ata_device_with_command_set
189{
190public:
191 win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
192
193 virtual bool is_open() const;
194
195 virtual bool open();
196
197 virtual bool close();
198
199protected:
200 virtual int ata_command_interface(smart_command_set command, int select, char * data);
201
202private:
203 bool m_ident_valid, m_smart_valid;
204 ata_identify_device m_ident_buf;
205 ata_smart_values m_smart_buf;
206};
207
208
209//////////////////////////////////////////////////////////////////////
210// Platform specific interfaces
211
212// Common to all windows flavors
213class win_smart_interface
214: public /*implements part of*/ smart_interface
215{
216public:
54965743 217 virtual std::string get_os_version_str();
2127e193 218
54965743 219 virtual std::string get_app_examples(const char * appname);
2127e193
GI
220
221 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
222 const char * pattern = 0);
223
224protected:
225 virtual ata_device * get_ata_device(const char * name, const char * type);
226
227//virtual scsi_device * get_scsi_device(const char * name, const char * type);
228
229 virtual smart_device * autodetect_smart_device(const char * name);
230
231 virtual bool ata_scan(smart_device_list & devlist) = 0;
232
233 virtual bool scsi_scan(smart_device_list & devlist) = 0;
234};
235
a23d5117
GI
236#if WIN9X_SUPPORT
237
2127e193
GI
238// Win9x/ME reduced functionality
239class win9x_smart_interface
240: public /*extends*/ win_smart_interface
241{
a23d5117
GI
242public:
243 win9x_smart_interface()
244 { win9x = true; }
245
2127e193
GI
246protected:
247 virtual scsi_device * get_scsi_device(const char * name, const char * type);
248
249 virtual bool ata_scan(smart_device_list & devlist);
250
251 virtual bool scsi_scan(smart_device_list & devlist);
252};
253
a23d5117
GI
254#endif // WIN9X_SUPPORT
255
2127e193
GI
256// WinNT,2000,XP,...
257class winnt_smart_interface
258: public /*extends*/ win_smart_interface
259{
260protected:
261 virtual scsi_device * get_scsi_device(const char * name, const char * type);
262
263 virtual smart_device * autodetect_smart_device(const char * name);
264
265 virtual bool ata_scan(smart_device_list & devlist);
266
267 virtual bool scsi_scan(smart_device_list & devlist);
268};
269
270
271//////////////////////////////////////////////////////////////////////
272
a37e7145
GG
273// Running on 64-bit Windows as 32-bit app ?
274static bool is_wow64()
275{
2127e193
GI
276 HMODULE hk = GetModuleHandleA("kernel32");
277 if (!hk)
278 return false;
279 BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
280 (BOOL (WINAPI *)(HANDLE, PBOOL))GetProcAddress(hk, "IsWow64Process");
281 if (!IsWow64Process_p)
282 return false;
283 BOOL w64 = FALSE;
284 if (!IsWow64Process_p(GetCurrentProcess(), &w64))
285 return false;
286 return !!w64;
a37e7145
GG
287}
288
54965743
GI
289// Return info string about build host and OS version
290std::string win_smart_interface::get_os_version_str()
2127e193 291{
54965743 292 char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
2127e193
GI
293 = SMARTMONTOOLS_BUILD_HOST;
294 if (vstr[1] < '6')
295 vstr[1] = '6';
296 char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
297 const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
298 assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
299
300 OSVERSIONINFOEXA vi; memset(&vi, 0, sizeof(vi));
301 vi.dwOSVersionInfoSize = sizeof(vi);
302 if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
303 memset(&vi, 0, sizeof(vi));
304 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
305 if (!GetVersionExA((OSVERSIONINFOA *)&vi))
306 return vstr;
307 }
308
309 if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
310 return vstr;
311
312 const char * w;
313 switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
314 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
315 w = (vi.szCSDVersion[1] == 'B' ||
316 vi.szCSDVersion[1] == 'C' ? "95-osr2" : "95"); break;
317 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
318 w = (vi.szCSDVersion[1] == 'A' ? "98se" : "98"); break;
319 case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me"; break;
320 //case VER_PLATFORM_WIN32_NT <<16|0x0300|51: w = "nt3.51"; break;
321 case VER_PLATFORM_WIN32_NT <<16|0x0400| 0: w = "nt4"; break;
322 case VER_PLATFORM_WIN32_NT <<16|0x0500| 0: w = "2000"; break;
323 case VER_PLATFORM_WIN32_NT <<16|0x0500| 1:
324 w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ? "xp"
325 : "xp-mc"); break;
326 case VER_PLATFORM_WIN32_NT <<16|0x0500| 2:
327 w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ? "2003"
328 : "2003r2"); break;
329 case VER_PLATFORM_WIN32_NT <<16|0x0600| 0:
330 w = (vi.wProductType == VER_NT_WORKSTATION ? "vista"
331 : "2008" ); break;
332 case VER_PLATFORM_WIN32_NT <<16|0x0600| 1:
333 w = (vi.wProductType == VER_NT_WORKSTATION ? "win7"
334 : "2008r2"); break;
335 default: w = 0; break;
336 }
337
338 const char * w64 = (is_wow64() ? "(64)" : "");
339 if (!w)
340 snprintf(vptr, vlen, "-%s%lu.%lu%s",
341 (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
342 vi.dwMajorVersion, vi.dwMinorVersion, w64);
343 else if (vi.wServicePackMinor)
344 snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
345 else if (vi.wServicePackMajor)
346 snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
347 else
348 snprintf(vptr, vlen, "-%s%s", w, w64);
349 return vstr;
350}
ba59cff1 351
2127e193
GI
352// Return value for device detection functions
353enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
832b75ed 354
2127e193
GI
355static win_dev_type get_phy_drive_type(int drive);
356static win_dev_type get_log_drive_type(int drive);
357static bool get_usb_id(int drive, unsigned short & vendor_id,
358 unsigned short & product_id);
ba59cff1 359
2127e193 360static const char * ata_get_def_options(void);
ba59cff1 361
832b75ed
GG
362
363static int is_permissive()
364{
2127e193
GI
365 if (!con->permissive) {
366 pout("To continue, add one or more '-T permissive' options.\n");
367 return 0;
368 }
369 con->permissive--;
370 return 1;
832b75ed
GG
371}
372
a37e7145
GG
373// return number for drive letter, -1 on error
374// "[A-Za-z]:([/\\][.]?)?" => 0-25
375// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
376static int drive_letter(const char * s)
377{
2127e193
GI
378 return ( (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
379 && s[1] == ':'
380 && (!s[2] || ( strchr("/\\\"", s[2])
381 && (!s[3] || (s[3] == '.' && !s[4]))) ) ?
382 (s[0] & 0x1f) - 1 : -1);
a37e7145
GG
383}
384
385// Skip trailing "/dev/", do not allow "/dev/X:"
832b75ed
GG
386static const char * skipdev(const char * s)
387{
2127e193
GI
388 return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
389}
390
391ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
392{
393 const char * testname = skipdev(name);
394 if (!strncmp(testname, "tw_cli", 6))
395 return new win_tw_cli_device(this, name, type);
396 return new win_ata_device(this, name, type);
397}
398
a23d5117
GI
399#ifdef WIN9X_SUPPORT
400
2127e193
GI
401scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type)
402{
403 return new win_aspi_device(this, name, type);
404}
405
a23d5117
GI
406#endif
407
2127e193
GI
408scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type)
409{
410 const char * testname = skipdev(name);
411 if (!strncmp(testname, "scsi", 4))
a23d5117 412#if WIN9X_SUPPORT
2127e193 413 return new win_aspi_device(this, name, type);
a23d5117
GI
414#else
415 return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0);
416#endif
2127e193
GI
417 return new win_scsi_device(this, name, type);
418}
419
420static win_dev_type get_dev_type(const char * name, int & phydrive)
421{
422 phydrive = -1;
423 name = skipdev(name);
424 if (!strncmp(name, "st", 2))
425 return DEV_SCSI;
426 if (!strncmp(name, "nst", 3))
427 return DEV_SCSI;
428 if (!strncmp(name, "tape", 4))
429 return DEV_SCSI;
430
431 int logdrive = drive_letter(name);
432 if (logdrive >= 0) {
433 win_dev_type type = get_log_drive_type(logdrive);
434 return (type != DEV_UNKNOWN ? type : DEV_SCSI);
435 }
436
437 char drive[1+1] = "";
438 if (sscanf(name, "sd%1[a-z]", drive) == 1) {
439 phydrive = drive[0] - 'a';
440 return get_phy_drive_type(phydrive);
441 }
442
443 phydrive = -1;
444 if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
445 return get_phy_drive_type(phydrive);
446 return DEV_UNKNOWN;
447}
448
449smart_device * win_smart_interface::autodetect_smart_device(const char * name)
450{
451 const char * testname = skipdev(name);
452 if (!strncmp(testname, "hd", 2))
453 return new win_ata_device(this, name, "");
a23d5117 454#if WIN9X_SUPPORT
2127e193
GI
455 if (!strncmp(testname, "scsi", 4))
456 return new win_aspi_device(this, name, "");
a23d5117 457#endif
2127e193
GI
458 if (!strncmp(testname, "tw_cli", 6))
459 return new win_tw_cli_device(this, name, "");
460 return 0;
461}
462
463smart_device * winnt_smart_interface::autodetect_smart_device(const char * name)
464{
465 smart_device * dev = win_smart_interface::autodetect_smart_device(name);
466 if (dev)
467 return dev;
468
469 int phydrive = -1;
470 win_dev_type type = get_dev_type(name, phydrive);
471
472 if (type == DEV_ATA)
473 return new win_ata_device(this, name, "");
474 if (type == DEV_SCSI)
475 return new win_scsi_device(this, name, "");
476
477 if (type == DEV_USB) {
478 // Get USB bridge ID
479 unsigned short vendor_id = 0, product_id = 0;
480 if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
481 set_err(EINVAL, "Unable to read USB device ID");
482 return 0;
483 }
484 // Get type name for this ID
485 const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
486 if (!usbtype)
487 return 0;
488 // Return SAT/USB device for this type
489 return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
490 }
491
492 return 0;
493}
494
495
496// makes a list of ATA or SCSI devices for the DEVICESCAN directive
497bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
498 const char * type, const char * pattern /* = 0*/)
499{
500 if (pattern) {
501 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
502 return false;
503 }
504
505 if (!type || !strcmp(type, "ata")) {
506 if (!ata_scan(devlist))
507 return false;
508 }
509
510 if (!type || !strcmp(type, "scsi")) {
511 if (!scsi_scan(devlist))
512 return false;
513 }
514 return true;
515}
516
517
518// get examples for smartctl
54965743 519std::string win_smart_interface::get_app_examples(const char * appname)
2127e193
GI
520{
521 if (strcmp(appname, "smartctl"))
54965743
GI
522 return "";
523 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
832b75ed 524 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
832b75ed
GG
525 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
526 " (Enables SMART on first disk)\n\n"
527 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n"
528 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
529 " (Prints Self-Test & Attribute errors)\n"
832b75ed
GG
530 " smartctl -a /dev/scsi21\n"
531 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
ba59cff1
GG
532 " smartctl -a /dev/sda\n"
533 " (Prints all information for SCSI disk on PhysicalDrive 0)\n"
534 " smartctl -a /dev/pd3\n"
535 " (Prints all information for SCSI disk on PhysicalDrive 3)\n"
536 " smartctl -a /dev/tape1\n"
537 " (Prints all information for SCSI tape on Tape 1)\n"
4d59bff9
GG
538 " smartctl -A /dev/hdb,3\n"
539 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
9ebc753d
GG
540 " smartctl -A /dev/tw_cli/c0/p1\n"
541 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
4d59bff9
GG
542 "\n"
543 " ATA SMART access methods and ordering may be specified by modifiers\n"
a37e7145 544 " following the device name: /dev/hdX:[saicm], where\n"
4d59bff9 545 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
a37e7145 546 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
1953ff6d 547 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
54965743 548 + strprintf(
1953ff6d 549 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
54965743 550 );
832b75ed
GG
551}
552
553
554/////////////////////////////////////////////////////////////////////////////
555// ATA Interface
556/////////////////////////////////////////////////////////////////////////////
557
558// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
559
832b75ed
GG
560#define FILE_READ_ACCESS 0x0001
561#define FILE_WRITE_ACCESS 0x0002
562#define METHOD_BUFFERED 0
563#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
564
2127e193 565#define FILE_DEVICE_DISK 7
832b75ed
GG
566#define IOCTL_DISK_BASE FILE_DEVICE_DISK
567
568#define SMART_GET_VERSION \
569 CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
570
4d59bff9
GG
571#define SMART_SEND_DRIVE_COMMAND \
572 CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
573
832b75ed
GG
574#define SMART_RCV_DRIVE_DATA \
575 CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
576
4d59bff9
GG
577ASSERT_CONST(SMART_GET_VERSION , 0x074080);
578ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
579ASSERT_CONST(SMART_RCV_DRIVE_DATA , 0x07c088);
832b75ed
GG
580
581#define SMART_CYL_LOW 0x4F
582#define SMART_CYL_HI 0xC2
583
4d59bff9 584
832b75ed
GG
585#pragma pack(1)
586
587typedef struct _GETVERSIONOUTPARAMS {
2127e193
GI
588 UCHAR bVersion;
589 UCHAR bRevision;
590 UCHAR bReserved;
591 UCHAR bIDEDeviceMap;
592 ULONG fCapabilities;
593 ULONG dwReserved[4];
832b75ed
GG
594} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
595
4d59bff9
GG
596ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24);
597
598
599#define SMART_VENDOR_3WARE 0x13C1 // identifies 3ware specific parameters
600
601typedef struct _GETVERSIONINPARAMS_EX {
2127e193
GI
602 BYTE bVersion;
603 BYTE bRevision;
604 BYTE bReserved;
605 BYTE bIDEDeviceMap;
606 DWORD fCapabilities;
607 DWORD dwDeviceMapEx; // 3ware specific: RAID drive bit map
608 WORD wIdentifier; // Vendor specific identifier
609 WORD wControllerId; // 3ware specific: Controller ID (0,1,...)
610 ULONG dwReserved[2];
4d59bff9
GG
611} GETVERSIONINPARAMS_EX, *PGETVERSIONINPARAMS_EX, *LPGETVERSIONINPARAMS_EX;
612
613ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONOUTPARAMS));
614
615
832b75ed 616typedef struct _IDEREGS {
2127e193
GI
617 UCHAR bFeaturesReg;
618 UCHAR bSectorCountReg;
619 UCHAR bSectorNumberReg;
620 UCHAR bCylLowReg;
621 UCHAR bCylHighReg;
622 UCHAR bDriveHeadReg;
623 UCHAR bCommandReg;
624 UCHAR bReserved;
832b75ed
GG
625} IDEREGS, *PIDEREGS, *LPIDEREGS;
626
627typedef struct _SENDCMDINPARAMS {
2127e193
GI
628 ULONG cBufferSize;
629 IDEREGS irDriveRegs;
630 UCHAR bDriveNumber;
631 UCHAR bReserved[3];
632 ULONG dwReserved[4];
633 UCHAR bBuffer[1];
832b75ed
GG
634} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
635
4d59bff9
GG
636ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
637
638typedef struct _SENDCMDINPARAMS_EX {
2127e193
GI
639 DWORD cBufferSize;
640 IDEREGS irDriveRegs;
641 BYTE bDriveNumber;
642 BYTE bPortNumber; // 3ware specific: port number
643 WORD wIdentifier; // Vendor specific identifier
644 DWORD dwReserved[4];
645 BYTE bBuffer[1];
4d59bff9
GG
646} SENDCMDINPARAMS_EX, *PSENDCMDINPARAMS_EX, *LPSENDCMDINPARAMS_EX;
647
648ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
649
650
832b75ed
GG
651/* DRIVERSTATUS.bDriverError constants (just for info, not used)
652#define SMART_NO_ERROR 0
653#define SMART_IDE_ERROR 1
654#define SMART_INVALID_FLAG 2
655#define SMART_INVALID_COMMAND 3
656#define SMART_INVALID_BUFFER 4
657#define SMART_INVALID_DRIVE 5
658#define SMART_INVALID_IOCTL 6
659#define SMART_ERROR_NO_MEM 7
660#define SMART_INVALID_REGISTER 8
661#define SMART_NOT_SUPPORTED 9
662#define SMART_NO_IDE_DEVICE 10
663*/
664
665typedef struct _DRIVERSTATUS {
2127e193
GI
666 UCHAR bDriverError;
667 UCHAR bIDEError;
668 UCHAR bReserved[2];
669 ULONG dwReserved[2];
832b75ed
GG
670} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
671
672typedef struct _SENDCMDOUTPARAMS {
2127e193
GI
673 ULONG cBufferSize;
674 DRIVERSTATUS DriverStatus;
675 UCHAR bBuffer[1];
832b75ed
GG
676} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
677
4d59bff9
GG
678ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
679
832b75ed
GG
680#pragma pack()
681
682
683/////////////////////////////////////////////////////////////////////////////
684
685static void print_ide_regs(const IDEREGS * r, int out)
686{
2127e193
GI
687 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
688 (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
689 r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
832b75ed
GG
690}
691
692static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
693{
2127e193
GI
694 pout(" Input : "); print_ide_regs(ri, 0);
695 if (ro) {
696 pout(" Output: "); print_ide_regs(ro, 1);
697 }
832b75ed
GG
698}
699
700/////////////////////////////////////////////////////////////////////////////
701
4d59bff9
GG
702// call SMART_GET_VERSION, return device map or -1 on error
703
a37e7145 704static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
4d59bff9 705{
2127e193
GI
706 GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
707 const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
708 DWORD num_out;
709
710 if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
711 NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
712 if (con->reportataioctl)
713 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
714 errno = ENOSYS;
715 return -1;
716 }
717 assert(num_out == sizeof(GETVERSIONOUTPARAMS));
718
719 if (con->reportataioctl > 1) {
720 pout(" SMART_GET_VERSION suceeded, bytes returned: %lu\n"
721 " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
722 num_out, vers.bVersion, vers.bRevision,
723 vers.fCapabilities, vers.bIDEDeviceMap);
724 if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
725 pout(" Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
726 vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
727 }
728
729 if (ata_version_ex)
730 *ata_version_ex = vers_ex;
731
732 // TODO: Check vers.fCapabilities here?
733 return vers.bIDEDeviceMap;
4d59bff9
GG
734}
735
736
832b75ed
GG
737// call SMART_* ioctl
738
4d59bff9 739static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
832b75ed 740{
2127e193
GI
741 SENDCMDINPARAMS inpar;
742 SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
743
744 unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
745 const SENDCMDOUTPARAMS * outpar;
746 DWORD code, num_out;
747 unsigned int size_out;
748 const char * name;
749
750 memset(&inpar, 0, sizeof(inpar));
751 inpar.irDriveRegs = *regs;
752 // drive is set to 0-3 on Win9x only
753 inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
754 inpar.bDriveNumber = drive;
755
756 if (port >= 0) {
757 // Set RAID port
758 inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
759 inpar_ex.bPortNumber = port;
760 }
761
762 if (datasize == 512) {
763 code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
764 inpar.cBufferSize = size_out = 512;
765 }
766 else if (datasize == 0) {
767 code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
768 if (regs->bFeaturesReg == ATA_SMART_STATUS)
769 size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
770 // Note: cBufferSize must be 0 on Win9x
771 else
772 size_out = 0;
773 }
774 else {
775 errno = EINVAL;
776 return -1;
777 }
778
779 memset(&outbuf, 0, sizeof(outbuf));
780
781 if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
782 outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
783 // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
784 long err = GetLastError();
785 if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) {
786 pout(" %s failed, Error=%ld\n", name, err);
787 print_ide_regs_io(regs, NULL);
788 }
789 errno = ( err == ERROR_INVALID_FUNCTION/*9x*/
790 || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
791 || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
792 return -1;
793 }
794 // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
795
796 outpar = (const SENDCMDOUTPARAMS *)outbuf;
797
798 if (outpar->DriverStatus.bDriverError) {
799 if (con->reportataioctl) {
800 pout(" %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
801 outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
802 print_ide_regs_io(regs, NULL);
803 }
804 errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
805 return -1;
806 }
807
808 if (con->reportataioctl > 1) {
809 pout(" %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
810 num_out, outpar->cBufferSize);
811 print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
812 (const IDEREGS *)(outpar->bBuffer) : NULL));
813 }
814
815 if (datasize)
816 memcpy(data, outpar->bBuffer, 512);
817 else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
a23d5117 818 if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
2127e193
GI
819 *regs = *(const IDEREGS *)(outpar->bBuffer);
820 else { // Workaround for driver not returning regs
821 if (con->reportataioctl)
822 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
823 *regs = inpar.irDriveRegs;
824 }
825 }
826
827 return 0;
832b75ed
GG
828}
829
830
831/////////////////////////////////////////////////////////////////////////////
832
4d59bff9 833// IDE PASS THROUGH (2000, XP, undocumented)
832b75ed
GG
834//
835// Based on WinATA.cpp, 2002 c't/Matthias Withopf
836// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
837
838#define FILE_DEVICE_CONTROLLER 4
839#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER
840
841#define IOCTL_IDE_PASS_THROUGH \
842 CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
843
4d59bff9
GG
844ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
845
832b75ed
GG
846#pragma pack(1)
847
848typedef struct {
2127e193
GI
849 IDEREGS IdeReg;
850 ULONG DataBufferSize;
851 UCHAR DataBuffer[1];
832b75ed
GG
852} ATA_PASS_THROUGH;
853
4d59bff9
GG
854ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
855
832b75ed
GG
856#pragma pack()
857
858
859/////////////////////////////////////////////////////////////////////////////
860
861static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
2127e193
GI
862{
863 if (datasize > 512) {
864 errno = EINVAL;
865 return -1;
866 }
867 unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
868 ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
869 DWORD num_out;
870 const unsigned char magic = 0xcf;
871
872 if (!buf) {
873 errno = ENOMEM;
874 return -1;
875 }
876
877 buf->IdeReg = *regs;
878 buf->DataBufferSize = datasize;
879 if (datasize)
880 buf->DataBuffer[0] = magic;
881
882 if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
883 buf, size, buf, size, &num_out, NULL)) {
884 long err = GetLastError();
885 if (con->reportataioctl) {
886 pout(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
887 print_ide_regs_io(regs, NULL);
888 }
889 VirtualFree(buf, 0, MEM_RELEASE);
890 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
891 return -1;
892 }
893
894 // Check ATA status
895 if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
896 if (con->reportataioctl) {
897 pout(" IOCTL_IDE_PASS_THROUGH command failed:\n");
898 print_ide_regs_io(regs, &buf->IdeReg);
899 }
900 VirtualFree(buf, 0, MEM_RELEASE);
901 errno = EIO;
902 return -1;
903 }
904
905 // Check and copy data
906 if (datasize) {
907 if ( num_out != size
908 || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
909 if (con->reportataioctl) {
910 pout(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
911 num_out, buf->DataBufferSize);
912 print_ide_regs_io(regs, &buf->IdeReg);
913 }
914 VirtualFree(buf, 0, MEM_RELEASE);
915 errno = EIO;
916 return -1;
917 }
918 memcpy(data, buf->DataBuffer, datasize);
919 }
920
921 if (con->reportataioctl > 1) {
922 pout(" IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
923 num_out, buf->DataBufferSize);
924 print_ide_regs_io(regs, &buf->IdeReg);
925 }
926 *regs = buf->IdeReg;
927
928 // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
929 VirtualFree(buf, 0, MEM_RELEASE);
930 return 0;
832b75ed
GG
931}
932
933
4d59bff9
GG
934/////////////////////////////////////////////////////////////////////////////
935
936// ATA PASS THROUGH (Win2003, XP SP2)
937
938#define IOCTL_ATA_PASS_THROUGH \
2127e193 939 CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
4d59bff9
GG
940
941ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
942
943typedef struct _ATA_PASS_THROUGH_EX {
2127e193
GI
944 USHORT Length;
945 USHORT AtaFlags;
946 UCHAR PathId;
947 UCHAR TargetId;
948 UCHAR Lun;
949 UCHAR ReservedAsUchar;
950 ULONG DataTransferLength;
951 ULONG TimeOutValue;
952 ULONG ReservedAsUlong;
953 ULONG/*_PTR*/ DataBufferOffset;
954 UCHAR PreviousTaskFile[8];
955 UCHAR CurrentTaskFile[8];
4d59bff9
GG
956} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX;
957
958ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40);
959
960#define ATA_FLAGS_DRDY_REQUIRED 0x01
961#define ATA_FLAGS_DATA_IN 0x02
962#define ATA_FLAGS_DATA_OUT 0x04
963#define ATA_FLAGS_48BIT_COMMAND 0x08
2127e193
GI
964#define ATA_FLAGS_USE_DMA 0x10
965#define ATA_FLAGS_NO_MULTIPLE 0x20 // Vista
4d59bff9
GG
966
967
968/////////////////////////////////////////////////////////////////////////////
969
2127e193
GI
970// Warning:
971// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
972// transfer per command. Therefore, multi-sector transfers are only supported
973// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
974// or READ/WRITE LOG EXT work only with single sector transfers.
975// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
976// See:
977// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
978
979static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
980{
981 const int max_sectors = 32; // TODO: Allocate dynamic buffer
982
983 typedef struct {
984 ATA_PASS_THROUGH_EX apt;
985 ULONG Filler;
986 UCHAR ucDataBuf[max_sectors * 512];
987 } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
988
989 const unsigned char magic = 0xcf;
990
991 ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
992 ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
993 //ab.apt.PathId = 0;
994 //ab.apt.TargetId = 0;
995 //ab.apt.Lun = 0;
996 ab.apt.TimeOutValue = 10;
997 unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
998 ab.apt.DataBufferOffset = size;
999
1000 if (datasize > 0) {
1001 if (datasize > (int)sizeof(ab.ucDataBuf)) {
1002 errno = EINVAL;
1003 return -1;
1004 }
1005 ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
1006 ab.apt.DataTransferLength = datasize;
1007 size += datasize;
1008 ab.ucDataBuf[0] = magic;
1009 }
1010 else if (datasize < 0) {
1011 if (-datasize > (int)sizeof(ab.ucDataBuf)) {
1012 errno = EINVAL;
1013 return -1;
1014 }
1015 ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
1016 ab.apt.DataTransferLength = -datasize;
1017 size += -datasize;
1018 memcpy(ab.ucDataBuf, data, -datasize);
1019 }
1020 else {
1021 assert(ab.apt.AtaFlags == 0);
1022 assert(ab.apt.DataTransferLength == 0);
1023 }
1024
1025 assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
1026 IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
1027 IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
1028 *ctfregs = *regs;
1029
1030 if (prev_regs) {
1031 *ptfregs = *prev_regs;
1032 ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
1033 }
1034
1035 DWORD num_out;
1036 if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
1037 &ab, size, &ab, size, &num_out, NULL)) {
1038 long err = GetLastError();
1039 if (con->reportataioctl) {
1040 pout(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
1041 print_ide_regs_io(regs, NULL);
1042 }
1043 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
1044 return -1;
1045 }
1046
1047 // Check ATA status
1048 if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
1049 if (con->reportataioctl) {
1050 pout(" IOCTL_ATA_PASS_THROUGH command failed:\n");
1051 print_ide_regs_io(regs, ctfregs);
1052 }
1053 errno = EIO;
1054 return -1;
1055 }
1056
1057 // Check and copy data
1058 if (datasize > 0) {
1059 if ( num_out != size
1060 || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
1061 if (con->reportataioctl) {
1062 pout(" IOCTL_ATA_PASS_THROUGH output data missing (%lu)\n", num_out);
1063 print_ide_regs_io(regs, ctfregs);
1064 }
1065 errno = EIO;
1066 return -1;
1067 }
1068 memcpy(data, ab.ucDataBuf, datasize);
1069 }
1070
1071 if (con->reportataioctl > 1) {
1072 pout(" IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
1073 print_ide_regs_io(regs, ctfregs);
1074 }
1075 *regs = *ctfregs;
1076 if (prev_regs)
1077 *prev_regs = *ptfregs;
1078
1079 return 0;
4d59bff9
GG
1080}
1081
832b75ed
GG
1082
1083/////////////////////////////////////////////////////////////////////////////
1084
4d59bff9 1085// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
832b75ed 1086
832b75ed 1087#define IOCTL_SCSI_PASS_THROUGH \
2127e193 1088 CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
832b75ed 1089
4d59bff9
GG
1090ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
1091
832b75ed
GG
1092#define SCSI_IOCTL_DATA_OUT 0
1093#define SCSI_IOCTL_DATA_IN 1
1094#define SCSI_IOCTL_DATA_UNSPECIFIED 2
1095// undocumented SCSI opcode to for ATA passthrough
1096#define SCSIOP_ATA_PASSTHROUGH 0xCC
1097
1098typedef struct _SCSI_PASS_THROUGH {
2127e193
GI
1099 USHORT Length;
1100 UCHAR ScsiStatus;
1101 UCHAR PathId;
1102 UCHAR TargetId;
1103 UCHAR Lun;
1104 UCHAR CdbLength;
1105 UCHAR SenseInfoLength;
1106 UCHAR DataIn;
1107 ULONG DataTransferLength;
1108 ULONG TimeOutValue;
1109 ULONG/*_PTR*/ DataBufferOffset;
1110 ULONG SenseInfoOffset;
1111 UCHAR Cdb[16];
832b75ed
GG
1112} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
1113
4d59bff9
GG
1114ASSERT_SIZEOF(SCSI_PASS_THROUGH, 44);
1115
832b75ed
GG
1116
1117/////////////////////////////////////////////////////////////////////////////
1118
1119static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
1120{
2127e193
GI
1121 typedef struct {
1122 SCSI_PASS_THROUGH spt;
1123 ULONG Filler;
1124 UCHAR ucSenseBuf[32];
1125 UCHAR ucDataBuf[512];
1126 } SCSI_PASS_THROUGH_WITH_BUFFERS;
1127
1128 SCSI_PASS_THROUGH_WITH_BUFFERS sb;
1129 IDEREGS * cdbregs;
1130 unsigned int size;
1131 DWORD num_out;
1132 const unsigned char magic = 0xcf;
1133
1134 memset(&sb, 0, sizeof(sb));
1135 sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
1136 //sb.spt.PathId = 0;
1137 sb.spt.TargetId = 1;
1138 //sb.spt.Lun = 0;
1139 sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
1140 sb.spt.TimeOutValue = 10;
1141 sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
1142 size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
1143 sb.spt.DataBufferOffset = size;
1144
1145 if (datasize) {
1146 if (datasize > sizeof(sb.ucDataBuf)) {
1147 errno = EINVAL;
1148 return -1;
1149 }
1150 sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
1151 sb.spt.DataTransferLength = datasize;
1152 size += datasize;
1153 sb.ucDataBuf[0] = magic;
1154 }
1155 else {
1156 sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
1157 //sb.spt.DataTransferLength = 0;
1158 }
1159
1160 // Use pseudo SCSI command followed by registers
1161 sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
1162 cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
1163 *cdbregs = *regs;
1164
1165 if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
1166 &sb, size, &sb, size, &num_out, NULL)) {
1167 long err = GetLastError();
1168 if (con->reportataioctl)
1169 pout(" ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
1170 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
1171 return -1;
1172 }
1173
1174 // Cannot check ATA status, because command does not return IDEREGS
1175
1176 // Check and copy data
1177 if (datasize) {
1178 if ( num_out != size
1179 || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
1180 if (con->reportataioctl) {
1181 pout(" ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
1182 print_ide_regs_io(regs, NULL);
1183 }
1184 errno = EIO;
1185 return -1;
1186 }
1187 memcpy(data, sb.ucDataBuf, datasize);
1188 }
1189
1190 if (con->reportataioctl > 1) {
1191 pout(" ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
1192 print_ide_regs_io(regs, NULL);
1193 }
1194 return 0;
832b75ed
GG
1195}
1196
ba59cff1
GG
1197
1198/////////////////////////////////////////////////////////////////////////////
1199
a37e7145
GG
1200// SMART IOCTL via SCSI MINIPORT ioctl
1201
1202// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
1203// miniport driver (via SCSI port driver scsiport.sys).
1204// It can be used to skip the missing or broken handling of some SMART
1205// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
ba59cff1
GG
1206
1207#define IOCTL_SCSI_MINIPORT \
2127e193 1208 CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
ba59cff1
GG
1209
1210ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
1211
1212typedef struct _SRB_IO_CONTROL {
2127e193
GI
1213 ULONG HeaderLength;
1214 UCHAR Signature[8];
1215 ULONG Timeout;
1216 ULONG ControlCode;
1217 ULONG ReturnCode;
1218 ULONG Length;
ba59cff1
GG
1219} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
1220
1221ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
1222
a37e7145
GG
1223#define FILE_DEVICE_SCSI 0x001b
1224
1225#define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI << 16) + 0x0500)
1226#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
1227#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI << 16) + 0x0502)
1228#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI << 16) + 0x0503)
1229#define IOCTL_SCSI_MINIPORT_ENABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0504)
1230#define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI << 16) + 0x0505)
1231#define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI << 16) + 0x0506)
1232#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI << 16) + 0x0507)
1233#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI << 16) + 0x0508)
1234#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI << 16) + 0x0509)
1235#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
1236#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050b)
1237#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG ((FILE_DEVICE_SCSI << 16) + 0x050c)
1238
1239/////////////////////////////////////////////////////////////////////////////
1240
1241static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
1242{
2127e193
GI
1243 // Select code
1244 DWORD code = 0; const char * name = 0;
1245 if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
1246 code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
1247 }
1248 else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
1249 case ATA_SMART_READ_VALUES:
1250 code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
1251 case ATA_SMART_READ_THRESHOLDS:
1252 code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
1253 case ATA_SMART_ENABLE:
1254 code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
1255 case ATA_SMART_DISABLE:
1256 code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
1257 case ATA_SMART_STATUS:
1258 code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
1259 case ATA_SMART_AUTOSAVE:
1260 code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
1261 //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
1262 // code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
1263 case ATA_SMART_IMMEDIATE_OFFLINE:
1264 code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
1265 case ATA_SMART_AUTO_OFFLINE:
1266 code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
1267 case ATA_SMART_READ_LOG_SECTOR:
1268 code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
1269 case ATA_SMART_WRITE_LOG_SECTOR:
1270 code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
1271 }
1272 if (!code) {
1273 errno = ENOSYS;
1274 return -1;
1275 }
1276
1277 // Set SRB
1278 struct {
1279 SRB_IO_CONTROL srbc;
1280 union {
1281 SENDCMDINPARAMS in;
1282 SENDCMDOUTPARAMS out;
1283 } params;
1284 char space[512-1];
1285 } sb;
1286 ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
1287 memset(&sb, 0, sizeof(sb));
1288
1289 unsigned size;
1290 if (datasize > 0) {
1291 if (datasize > (int)sizeof(sb.space)+1) {
1292 errno = EINVAL;
1293 return -1;
1294 }
1295 size = datasize;
1296 }
1297 else if (datasize < 0) {
1298 if (-datasize > (int)sizeof(sb.space)+1) {
1299 errno = EINVAL;
1300 return -1;
1301 }
1302 size = -datasize;
1303 memcpy(sb.params.in.bBuffer, data, size);
1304 }
1305 else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
1306 size = sizeof(IDEREGS);
1307 else
1308 size = 0;
1309 sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
1310 memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
1311 sb.srbc.Timeout = 60; // seconds
1312 sb.srbc.ControlCode = code;
1313 //sb.srbc.ReturnCode = 0;
1314 sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
1315 sb.params.in.irDriveRegs = *regs;
1316 sb.params.in.cBufferSize = size;
1317
1318 // Call miniport ioctl
1319 size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
1320 DWORD num_out;
1321 if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
1322 &sb, size, &sb, size, &num_out, NULL)) {
1323 long err = GetLastError();
1324 if (con->reportataioctl) {
1325 pout(" IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
1326 print_ide_regs_io(regs, NULL);
1327 }
1328 errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
1329 return -1;
1330 }
1331
1332 // Check result
1333 if (sb.srbc.ReturnCode) {
1334 if (con->reportataioctl) {
1335 pout(" IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
1336 print_ide_regs_io(regs, NULL);
1337 }
1338 errno = EIO;
1339 return -1;
1340 }
1341
1342 if (sb.params.out.DriverStatus.bDriverError) {
1343 if (con->reportataioctl) {
1344 pout(" IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
1345 sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
1346 print_ide_regs_io(regs, NULL);
1347 }
1348 errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
1349 return -1;
1350 }
1351
1352 if (con->reportataioctl > 1) {
1353 pout(" IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
1354 num_out, sb.params.out.cBufferSize);
1355 print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
1356 (const IDEREGS *)(sb.params.out.bBuffer) : 0));
1357 }
1358
1359 if (datasize > 0)
1360 memcpy(data, sb.params.out.bBuffer, datasize);
1361 else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
1362 *regs = *(const IDEREGS *)(sb.params.out.bBuffer);
1363
1364 return 0;
a37e7145
GG
1365}
1366
1367
ba59cff1
GG
1368/////////////////////////////////////////////////////////////////////////////
1369
a37e7145
GG
1370// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1371
ba59cff1
GG
1372static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
1373{
2127e193
GI
1374 struct {
1375 SRB_IO_CONTROL srbc;
1376 IDEREGS regs;
1377 UCHAR buffer[512];
1378 } sb;
1379 ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
1380
1381 if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
1382 errno = EINVAL;
1383 return -1;
1384 }
1385 memset(&sb, 0, sizeof(sb));
1386 strcpy((char *)sb.srbc.Signature, "<3ware>");
1387 sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
1388 sb.srbc.Timeout = 60; // seconds
1389 sb.srbc.ControlCode = 0xA0000000;
1390 sb.srbc.ReturnCode = 0;
1391 sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
1392 sb.regs = *regs;
1393 sb.regs.bReserved = port;
1394
1395 DWORD num_out;
1396 if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
1397 &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
1398 long err = GetLastError();
1399 if (con->reportataioctl) {
1400 pout(" ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
1401 print_ide_regs_io(regs, NULL);
1402 }
1403 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
1404 return -1;
1405 }
1406
1407 if (sb.srbc.ReturnCode) {
1408 if (con->reportataioctl) {
1409 pout(" ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
1410 print_ide_regs_io(regs, NULL);
1411 }
1412 errno = EIO;
1413 return -1;
1414 }
1415
1416 // Copy data
1417 if (datasize > 0)
1418 memcpy(data, sb.buffer, datasize);
1419
1420 if (con->reportataioctl > 1) {
1421 pout(" ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
1422 print_ide_regs_io(regs, &sb.regs);
1423 }
1424 *regs = sb.regs;
1425
1426 return 0;
ba59cff1
GG
1427}
1428
1429
1430/////////////////////////////////////////////////////////////////////////////
1431
1432// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
1433// 3DM/CLI "Rescan Controller" function does not to always update it.
1434
1435static int update_3ware_devicemap_ioctl(HANDLE hdevice)
1436{
2127e193
GI
1437 SRB_IO_CONTROL srbc;
1438 memset(&srbc, 0, sizeof(srbc));
1439 strcpy((char *)srbc.Signature, "<3ware>");
1440 srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
1441 srbc.Timeout = 60; // seconds
1442 srbc.ControlCode = 0xCC010014;
1443 srbc.ReturnCode = 0;
1444 srbc.Length = 0;
1445
1446 DWORD num_out;
1447 if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
1448 &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
1449 long err = GetLastError();
1450 if (con->reportataioctl)
1451 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
1452 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
1453 return -1;
1454 }
1455 if (srbc.ReturnCode) {
1456 if (con->reportataioctl)
1457 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
1458 errno = EIO;
1459 return -1;
1460 }
1461 if (con->reportataioctl > 1)
1462 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1463 return 0;
ba59cff1
GG
1464}
1465
1466
9ebc753d
GG
1467
1468/////////////////////////////////////////////////////////////////////////////
1469
1470// Routines for pseudo device /dev/tw_cli/*
1471// Parses output of 3ware "tw_cli /cx/py show all" or 3DM SMART data window
1472
1473
1474// Get clipboard data
1475
1476static int get_clipboard(char * data, int datasize)
1477{
2127e193
GI
1478 if (!OpenClipboard(NULL))
1479 return -1;
1480 HANDLE h = GetClipboardData(CF_TEXT);
1481 if (!h) {
1482 CloseClipboard();
1483 return 0;
1484 }
1485 const void * p = GlobalLock(h);
1486 int n = GlobalSize(h);
1487 if (n > datasize)
1488 n = datasize;
1489 memcpy(data, p, n);
1490 GlobalFree(h);
1491 CloseClipboard();
1492 return n;
9ebc753d
GG
1493}
1494
1495
1496// Run a command, write stdout to dataout
1497// TODO: Combine with daemon_win32.cpp:daemon_spawn()
1498
1499static int run_cmd(const char * cmd, char * dataout, int outsize)
1500{
2127e193
GI
1501 // Create stdout pipe
1502 SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
1503 HANDLE pipe_out_w, h;
1504 if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
1505 return -1;
1506 HANDLE self = GetCurrentProcess();
1507 HANDLE pipe_out_r;
1508 if (!DuplicateHandle(self, h, self, &pipe_out_r,
1509 GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
1510 CloseHandle(pipe_out_w);
1511 return -1;
1512 }
1513 HANDLE pipe_err_w;
1514 if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
1515 0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
1516 CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
1517 return -1;
1518 }
1519
1520 // Create process
1521 STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
1522 si.hStdInput = INVALID_HANDLE_VALUE;
1523 si.hStdOutput = pipe_out_w; si.hStdError = pipe_err_w;
1524 si.dwFlags = STARTF_USESTDHANDLES;
1525 PROCESS_INFORMATION pi;
1526 if (!CreateProcess(
1527 NULL, const_cast<char *>(cmd),
1528 NULL, NULL, TRUE/*inherit*/,
1529 CREATE_NO_WINDOW/*do not create a new console window*/,
1530 NULL, NULL, &si, &pi)) {
1531 CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
1532 return -1;
1533 }
1534 CloseHandle(pi.hThread);
1535 CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
1536
1537 // Copy stdout to output buffer
1538 int i = 0;
1539 while (i < outsize) {
1540 DWORD num_read;
1541 if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
1542 break;
1543 i += num_read;
1544 }
1545 CloseHandle(pipe_out_r);
1546 // Wait for process
1547 WaitForSingleObject(pi.hProcess, INFINITE);
1548 CloseHandle(pi.hProcess);
1549 return i;
9ebc753d
GG
1550}
1551
1552
1553static const char * findstr(const char * str, const char * sub)
1554{
2127e193
GI
1555 const char * s = strstr(str, sub);
1556 return (s ? s+strlen(sub) : "");
9ebc753d
GG
1557}
1558
1559
1560static void copy_swapped(unsigned char * dest, const char * src, int destsize)
1561{
2127e193
GI
1562 int srclen = strcspn(src, "\r\n");
1563 int i;
1564 for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
1565 dest[i] = src[i+1]; dest[i+1] = src[i];
1566 }
1567 if (i < destsize-1 && i < srclen)
1568 dest[i+1] = src[i];
1569}
1570
1571
1572// TODO: This is OS independent
1573
1574win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
1575: smart_device(intf, dev_name, "tw_cli", req_type),
1576 m_ident_valid(false), m_smart_valid(false)
1577{
1578 memset(&m_ident_buf, 0, sizeof(m_ident_buf));
1579 memset(&m_smart_buf, 0, sizeof(m_smart_buf));
1580}
1581
1582
1583bool win_tw_cli_device::is_open() const
1584{
1585 return (m_ident_valid || m_smart_valid);
1586}
1587
1588
1589bool win_tw_cli_device::open()
1590{
1591 m_ident_valid = m_smart_valid = false;
1592 const char * name = skipdev(get_dev_name());
1593 // Read tw_cli or 3DM browser output into buffer
1594 char buffer[4096];
1595 int size = -1, n1 = -1, n2 = -1;
1596 if (!strcmp(name, "tw_cli/clip")) { // read clipboard
1597 size = get_clipboard(buffer, sizeof(buffer));
1598 }
1599 else if (!strcmp(name, "tw_cli/stdin")) { // read stdin
1600 size = fread(buffer, 1, sizeof(buffer), stdin);
1601 }
1602 else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
1603 // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
1604 char cmd[100];
1605 snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
1606 if (con->reportataioctl > 1)
1607 pout("%s: Run: \"%s\"\n", name, cmd);
1608 size = run_cmd(cmd, buffer, sizeof(buffer));
1609 }
1610 else {
1611 return set_err(EINVAL);
1612 }
1613
1614 if (con->reportataioctl > 1)
1615 pout("%s: Read %d bytes\n", name, size);
1616 if (size <= 0)
1617 return set_err(ENOENT);
1618 if (size >= (int)sizeof(buffer))
1619 return set_err(EIO);
1620
1621 buffer[size] = 0;
1622 if (con->reportataioctl > 1)
1623 pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
1624
1625 // Fake identify sector
1626 ASSERT_SIZEOF(ata_identify_device, 512);
1627 ata_identify_device * id = &m_ident_buf;
1628 memset(id, 0, sizeof(*id));
1629 copy_swapped(id->model , findstr(buffer, " Model = " ), sizeof(id->model));
1630 copy_swapped(id->fw_rev , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
1631 copy_swapped(id->serial_no, findstr(buffer, " Serial = " ), sizeof(id->serial_no));
1632 unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
1633 sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
1634 if (nblocks) {
1635 id->words047_079[49-47] = 0x0200; // size valid
1636 id->words047_079[60-47] = (unsigned short)(nblocks ); // secs_16
1637 id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
1638 }
2127e193
GI
1639 id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
1640 id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
1641
1642 // Parse smart data hex dump
1643 const char * s = findstr(buffer, "Drive Smart Data:");
f4ebf3d1
GI
1644 if (!*s)
1645 s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
2127e193
GI
1646 if (!*s) {
1647 s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
1648 if (*s) {
1649 const char * s1 = findstr(s, "<td class"); // html version
1650 if (*s1)
1651 s = s1;
1652 s += strcspn(s, "\r\n");
1653 }
1654 else
1655 s = buffer; // try raw hex dump without header
1656 }
1657 unsigned char * sd = (unsigned char *)&m_smart_buf;
1658 int i = 0;
1659 for (;;) {
1660 unsigned x = ~0; int n = -1;
1661 if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
1662 break;
1663 sd[i] = (unsigned char)x;
1664 if (!(++i < 512 && n > 0))
1665 break;
1666 s += n;
1667 if (*s == '<') // "<br>"
1668 s += strcspn(s, "\r\n");
1669 }
1670 if (i < 512) {
1671 if (!id->model[1]) {
1672 // No useful data found
1673 char * err = strstr(buffer, "Error:");
1674 if (!err)
1675 err = strstr(buffer, "error :");
1676 if (err && (err = strchr(err, ':'))) {
1677 // Show tw_cli error message
1678 err++;
1679 err[strcspn(err, "\r\n")] = 0;
1680 return set_err(EIO, err);
1681 }
1682 return set_err(EIO);
1683 }
1684 sd = 0;
1685 }
1686
1687 m_ident_valid = true;
1688 m_smart_valid = !!sd;
1689 return true;
1690}
1691
1692
1693bool win_tw_cli_device::close()
1694{
1695 m_ident_valid = m_smart_valid = false;
1696 return true;
1697}
1698
1699
1700int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
1701{
1702 switch (command) {
1703 case IDENTIFY:
1704 if (!m_ident_valid)
1705 break;
1706 memcpy(data, &m_ident_buf, 512);
1707 return 0;
1708 case READ_VALUES:
1709 if (!m_smart_valid)
1710 break;
1711 memcpy(data, &m_smart_buf, 512);
1712 return 0;
2127e193
GI
1713 case ENABLE:
1714 case STATUS:
1715 case STATUS_CHECK: // Fake "good" SMART status
1716 return 0;
1717 default:
1718 break;
1719 }
1720 // Arrive here for all unsupported commands
1721 set_err(ENOSYS);
1722 return -1;
9ebc753d
GG
1723}
1724
1725
a37e7145
GG
1726/////////////////////////////////////////////////////////////////////////////
1727
1728// IOCTL_STORAGE_QUERY_PROPERTY
1729
1730#define FILE_DEVICE_MASS_STORAGE 0x0000002d
1731#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
1732#define FILE_ANY_ACCESS 0
1733
1734#define IOCTL_STORAGE_QUERY_PROPERTY \
2127e193 1735 CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
a37e7145
GG
1736
1737typedef enum _STORAGE_BUS_TYPE {
2127e193
GI
1738 BusTypeUnknown = 0x00,
1739 BusTypeScsi = 0x01,
1740 BusTypeAtapi = 0x02,
1741 BusTypeAta = 0x03,
1742 BusType1394 = 0x04,
1743 BusTypeSsa = 0x05,
1744 BusTypeFibre = 0x06,
1745 BusTypeUsb = 0x07,
1746 BusTypeRAID = 0x08,
1747 BusTypeiScsi = 0x09,
1748 BusTypeSas = 0x0A,
1749 BusTypeSata = 0x0B,
1750 BusTypeSd = 0x0C,
1751 BusTypeMmc = 0x0D,
1752 BusTypeMax = 0x0E,
1753 BusTypeMaxReserved = 0x7F
a37e7145
GG
1754} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
1755
1756typedef struct _STORAGE_DEVICE_DESCRIPTOR {
2127e193
GI
1757 ULONG Version;
1758 ULONG Size;
1759 UCHAR DeviceType;
1760 UCHAR DeviceTypeModifier;
1761 BOOLEAN RemovableMedia;
1762 BOOLEAN CommandQueueing;
1763 ULONG VendorIdOffset;
1764 ULONG ProductIdOffset;
1765 ULONG ProductRevisionOffset;
1766 ULONG SerialNumberOffset;
1767 STORAGE_BUS_TYPE BusType;
1768 ULONG RawPropertiesLength;
1769 UCHAR RawDeviceProperties[1];
a37e7145
GG
1770} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
1771
1772typedef enum _STORAGE_QUERY_TYPE {
2127e193
GI
1773 PropertyStandardQuery = 0,
1774 PropertyExistsQuery,
1775 PropertyMaskQuery,
1776 PropertyQueryMaxDefined
a37e7145
GG
1777} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
1778
1779typedef enum _STORAGE_PROPERTY_ID {
2127e193
GI
1780 StorageDeviceProperty = 0,
1781 StorageAdapterProperty,
1782 StorageDeviceIdProperty,
1783 StorageDeviceUniqueIdProperty,
1784 StorageDeviceWriteCacheProperty,
1785 StorageMiniportProperty,
1786 StorageAccessAlignmentProperty
a37e7145
GG
1787} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
1788
1789typedef struct _STORAGE_PROPERTY_QUERY {
2127e193
GI
1790 STORAGE_PROPERTY_ID PropertyId;
1791 STORAGE_QUERY_TYPE QueryType;
1792 UCHAR AdditionalParameters[1];
a37e7145
GG
1793} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
1794
1795
1796/////////////////////////////////////////////////////////////////////////////
1797
1953ff6d 1798union STORAGE_DEVICE_DESCRIPTOR_DATA {
2127e193
GI
1799 STORAGE_DEVICE_DESCRIPTOR desc;
1800 char raw[256];
1953ff6d 1801};
a37e7145 1802
1953ff6d
GG
1803// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
1804// (This works without admin rights)
1805
1806static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
a37e7145 1807{
2127e193
GI
1808 STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
1809 memset(data, 0, sizeof(*data));
1810
1811 DWORD num_out;
1812 if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
1813 &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
1814 if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
1815 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
1816 errno = ENOSYS;
1817 return -1;
1818 }
1819
1820 if (con->reportataioctl > 1 || con->reportscsiioctl > 1) {
1821 pout(" IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
1822 " Vendor: \"%s\"\n"
1823 " Product: \"%s\"\n"
1824 " Revision: \"%s\"\n"
1825 " Removable: %s\n"
1826 " BusType: 0x%02x\n",
1827 (data->desc.VendorIdOffset ? data->raw+data->desc.VendorIdOffset : ""),
1828 (data->desc.ProductIdOffset ? data->raw+data->desc.ProductIdOffset : ""),
1829 (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : ""),
1830 (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
1831 );
1832 }
1833 return 0;
1953ff6d
GG
1834}
1835
1836
1837/////////////////////////////////////////////////////////////////////////////
1838
1839// IOCTL_STORAGE_PREDICT_FAILURE
1840
1841#define IOCTL_STORAGE_PREDICT_FAILURE \
1842 CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
1843
1844typedef struct _STORAGE_PREDICT_FAILURE {
2127e193
GI
1845 ULONG PredictFailure;
1846 UCHAR VendorSpecific[512];
1953ff6d
GG
1847} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE;
1848
1849ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
1850
1851
1852/////////////////////////////////////////////////////////////////////////////
1853
1854
1855// Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
1856// or -1 on error, opionally return VendorSpecific data.
1857// (This works without admin rights)
1858
1859static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
1860{
2127e193
GI
1861 STORAGE_PREDICT_FAILURE pred;
1862 memset(&pred, 0, sizeof(pred));
1863
1864 DWORD num_out;
1865 if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
1866 0, 0, &pred, sizeof(pred), &num_out, NULL)) {
1867 if (con->reportataioctl > 1)
1868 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
1869 errno = ENOSYS;
1870 return -1;
1871 }
1872
1873 if (con->reportataioctl > 1) {
1874 pout(" IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
1875 " PredictFailure: 0x%08lx\n"
1876 " VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
1877 pred.PredictFailure,
1878 pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
1879 pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
1880 );
1881 }
1882 if (data)
1883 memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
1884 return (!pred.PredictFailure ? 0 : 1);
a37e7145
GG
1885}
1886
1887
1888/////////////////////////////////////////////////////////////////////////////
1889
2127e193
GI
1890// get DEV_* for open handle
1891static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
a37e7145 1892{
2127e193
GI
1893 // Try SMART_GET_VERSION first to detect ATA SMART support
1894 // for drivers reporting BusTypeScsi (3ware)
1895 if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
1896 return DEV_ATA;
1897
1898 // Get BusType from device descriptor
1899 STORAGE_DEVICE_DESCRIPTOR_DATA data;
1900 if (storage_query_property_ioctl(hdevice, &data))
1901 return DEV_UNKNOWN;
1902
1903 switch (data.desc.BusType) {
1904 case BusTypeAta:
1905 case BusTypeSata:
1906 if (ata_version_ex)
1907 memset(ata_version_ex, 0, sizeof(*ata_version_ex));
1908 return DEV_ATA;
1909 case BusTypeScsi:
1910 case BusTypeiScsi:
1911 case BusTypeSas:
1912 return DEV_SCSI;
1913 case BusTypeUsb:
1914 return DEV_USB;
1915 default:
1916 return DEV_UNKNOWN;
1917 }
1918 /*NOTREACHED*/
a37e7145
GG
1919}
1920
2127e193
GI
1921// get DEV_* for device path
1922static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
a37e7145 1923{
2127e193
GI
1924 bool admin = true;
1925 HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
1926 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1927 if (h == INVALID_HANDLE_VALUE) {
1928 admin = false;
1929 h = CreateFileA(path, 0,
1930 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1931 if (h == INVALID_HANDLE_VALUE)
1932 return DEV_UNKNOWN;
1933 }
1934 if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
1935 pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
1936 win_dev_type type = get_controller_type(h, admin, ata_version_ex);
1937 CloseHandle(h);
1938 return type;
a37e7145
GG
1939}
1940
2127e193
GI
1941// get DEV_* for physical drive number
1942static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
a37e7145 1943{
2127e193
GI
1944 char path[30];
1945 snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
1946 return get_controller_type(path, ata_version_ex);
a37e7145
GG
1947}
1948
2127e193 1949static win_dev_type get_phy_drive_type(int drive)
a37e7145 1950{
2127e193 1951 return get_phy_drive_type(drive, 0);
a37e7145
GG
1952}
1953
2127e193
GI
1954// get DEV_* for logical drive number
1955static win_dev_type get_log_drive_type(int drive)
a37e7145 1956{
2127e193
GI
1957 char path[30];
1958 snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
1959 return get_controller_type(path);
a37e7145
GG
1960}
1961
1953ff6d
GG
1962// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
1963static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
1964{
2127e193
GI
1965 STORAGE_DEVICE_DESCRIPTOR_DATA data;
1966 if (storage_query_property_ioctl(hdevice, &data))
1967 return -1;
1968
1969 memset(id, 0, sizeof(*id));
1970 if (data.desc.ProductIdOffset)
1971 copy_swapped(id->model, data.raw+data.desc.ProductIdOffset, sizeof(id->model));
1972 if (data.desc.ProductRevisionOffset)
1973 copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
2127e193
GI
1974 id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
1975 id->cfs_enable_1 = 0x0001; id->csf_default = 0x4000; // SMART enabled, words 85,87 valid
1976 return 0;
1977}
1978
1979
1980/////////////////////////////////////////////////////////////////////////////
1981// USB ID detection using WMI
1953ff6d 1982
2127e193
GI
1983// Run a command, split stdout into lines.
1984// Return number of lines read, -1 on error.
1985static int run_cmd(std::vector<std::string> & lines, const char * cmd, ...)
1986{
1987 lines.clear();
1988
1989 va_list ap; va_start(ap, cmd);
1990 std::string cmdline = vstrprintf(cmd, ap);
1991 va_end(ap);
1992
1993 if (con->reportscsiioctl > 1)
1994 pout("Run: \"%s\"\n", cmdline.c_str());
1995
1996 char buffer[16*1024];
1997 int size = run_cmd(cmdline.c_str(), buffer, sizeof(buffer));
1998
1999 if (con->reportscsiioctl > 1)
2000 pout("Read %d bytes\n", size);
2001 if (!(0 < size && size < (int)sizeof(buffer)-1))
2002 return -1;
2003
2004 buffer[size] = 0;
2005
2006 for (int i = 0; buffer[i]; ) {
2007 int len = strcspn(buffer+i, "\r\n");
2008 lines.push_back(std::string(buffer+i, len));
2009 i += len;
2010 i += strspn(buffer+i, "\r\n");
2011 }
2012 if (con->reportscsiioctl > 1) {
2013 for (unsigned i = 0; i < lines.size(); i++)
2014 printf("'%s'\n", lines[i].c_str());
2015 }
2016 return lines.size();
2017}
2018
2019// Quote string for WMI
2020static std::string wmi_quote(const char * s, int len)
2021{
2022 std::string r;
2023 for (int i = 0; i < len; i++) {
2024 char c = s[i];
2025 if (c == '\\')
2026 r += '\\';
2027 r += c;
2028 }
2029 return r;
2030}
2031
2032// Get USB ID for a physical drive number
2033static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
2034{
2035 // Get device name
2036 std::vector<std::string> result;
2037 if (run_cmd(result,
2038 "wmic PATH Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\" GET Model",
2039 drive) != 2)
2040 return false;
2041
2042 std::string name = result[1];
2043
2044 // Get USB_CONTROLLER -> DEVICE associations
2045 std::vector<std::string> assoc;
2046 int n = run_cmd(assoc, "wmic PATH Win32_USBControllerDevice GET Antecedent,Dependent");
2047 if (n < 2)
2048 return false;
2049
2050 regular_expression regex("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"(USBSTOR\\\\[^\"]*)\" *$",
2051 REG_EXTENDED);
2052 if (regex.empty()) // TODO: throw in constructor?
2053 return false;
2054
2055 int usbstoridx = -1;
2056 std::string usbcontr;
2057 for (int i = 2; i < n; i++) {
2058 // Find next 'USB_CONTROLLER USBSTORAGE_DEVICE' pair
2059 regmatch_t match[3];
2060 const char * s = assoc[i].c_str();
2061 if (!regex.execute(s, 3, match))
2062 continue;
2063
2064 // USBSTOR device found, compare Name
2065 if (run_cmd(result,
2066 "wmic PATH Win32_PnPEntity WHERE DeviceID=\"%s\" GET Name",
2067 wmi_quote(s + match[2].rm_so, match[2].rm_eo - match[2].rm_so).c_str()
2068 ) != 2)
2069 continue;
2070 if (result[1] != name)
2071 continue;
2072
2073 // Name must be uniqe
2074 if (usbstoridx >= 0)
2075 return false;
2076
2077 usbstoridx = i;
2078 usbcontr.assign(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
2079 }
2080
2081 // Found ?
2082 if (usbstoridx <= 0)
2083 return false;
2084
2085 // The entry preceding USBSTOR should be the USB bridge device
2086 regex.compile("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"USB\\\\VID_(....&PID_....)[^\"]*\" *$",
2087 REG_EXTENDED);
2088 if (regex.empty())
2089 return false;
2090 regmatch_t match[3];
2091 const char * s = assoc[usbstoridx-1].c_str();
2092 if (!regex.execute(s, 3, match))
2093 return false;
2094
2095 // Both devices must be associated to same controller
2096 if (usbcontr != std::string(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so))
2097 return false;
2098
2099 // Parse USB ID
2100 int nc = -1;
2101 if (!(sscanf(s + match[2].rm_so, "%4hx&PID_%4hx%n",
2102 &vendor_id, &product_id, &nc) == 2 && nc == 4+5+4))
2103 return false;
2104
2105 if (con->reportscsiioctl > 1)
2106 pout("USB ID = 0x%04x:0x%04x\n", vendor_id, product_id);
2107 return true;
1953ff6d
GG
2108}
2109
a37e7145 2110
4d59bff9
GG
2111/////////////////////////////////////////////////////////////////////////////
2112
2113// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
2114// returns: 1=active, 0=standby, -1=error
2115// (This would also work for SCSI drives)
2116
2117static int get_device_power_state(HANDLE hdevice)
2118{
2127e193 2119 static HINSTANCE h_kernel_dll = 0;
4d59bff9 2120#ifdef __CYGWIN__
2127e193 2121 static DWORD kernel_dll_pid = 0;
4d59bff9 2122#endif
2127e193 2123 static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
4d59bff9 2124
2127e193 2125 BOOL state = TRUE;
4d59bff9 2126
2127e193 2127 if (!GetDevicePowerState_p
4d59bff9 2128#ifdef __CYGWIN__
2127e193 2129 || kernel_dll_pid != GetCurrentProcessId() // detect fork()
4d59bff9 2130#endif
2127e193
GI
2131 ) {
2132 if (h_kernel_dll == INVALID_HANDLE_VALUE) {
2133 errno = ENOSYS;
2134 return -1;
2135 }
2136 if (!(h_kernel_dll = LoadLibraryA("KERNEL32.DLL"))) {
2137 pout("Cannot load KERNEL32.DLL, Error=%ld\n", GetLastError());
2138 h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
2139 errno = ENOSYS;
2140 return -1;
2141 }
2142 if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *))
2143 GetProcAddress(h_kernel_dll, "GetDevicePowerState"))) {
2144 if (con->reportataioctl)
2145 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
2146 FreeLibrary(h_kernel_dll);
2147 h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
2148 errno = ENOSYS;
2149 return -1;
2150 }
4d59bff9 2151#ifdef __CYGWIN__
2127e193 2152 kernel_dll_pid = GetCurrentProcessId();
4d59bff9 2153#endif
2127e193
GI
2154 }
2155
2156 if (!GetDevicePowerState_p(hdevice, &state)) {
2157 long err = GetLastError();
2158 if (con->reportataioctl)
2159 pout(" GetDevicePowerState() failed, Error=%ld\n", err);
2160 errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
2161 // TODO: This may not work as expected on transient errors,
2162 // because smartd interprets -1 as SLEEP mode regardless of errno.
2163 return -1;
2164 }
2165
2166 if (con->reportataioctl > 1)
2167 pout(" GetDevicePowerState() succeeded, state=%d\n", state);
2168 return state;
4d59bff9
GG
2169}
2170
832b75ed
GG
2171
2172/////////////////////////////////////////////////////////////////////////////
2173
a23d5117 2174#if WIN9X_SUPPORT
832b75ed
GG
2175// Print SMARTVSD error message, return errno
2176
2177static int smartvsd_error()
2178{
2127e193
GI
2179 char path[MAX_PATH];
2180 unsigned len;
2181 if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
2182 return ENOENT;
2183 // SMARTVSD.VXD present?
2184 strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
2185 if (!access(path, 0)) {
2186 // Yes, standard IDE driver used?
2187 HANDLE h;
2188 if ( (h = CreateFileA("\\\\.\\ESDI_506",
2189 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
2190 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
2191 && GetLastError() == ERROR_FILE_NOT_FOUND ) {
2192 pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
2193 return ENOENT;
2194 }
2195 else {
2196 if (h != INVALID_HANDLE_VALUE) // should not happen
2197 CloseHandle(h);
2198 pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
2199 return ENOSYS;
2200 }
2201 }
2202 else {
2203 strcpy(path+len, "\\SMARTVSD.VXD");
2204 if (!access(path, 0)) {
2205 // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
2206 // (http://support.microsoft.com/kb/265854/en-us).
2207 path[len] = 0;
2208 pout("SMART driver is not properly installed,\n"
2209 " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
2210 " and reboot Windows.\n", path, path);
2211 }
2212 else {
2213 // Some Windows versions do not provide SMARTVSD.VXD
2214 // (http://support.microsoft.com/kb/199886/en-us).
2215 path[len] = 0;
2216 pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
2217 }
2218 return ENOSYS;
2219 }
832b75ed
GG
2220}
2221
a23d5117 2222#endif // WIN9X_SUPPORT
832b75ed 2223
4d59bff9
GG
2224// Get default ATA device options
2225
2226static const char * ata_get_def_options()
2227{
2127e193
GI
2228 DWORD ver = GetVersion();
2229 if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME
2230 return "s"; // SMART_* only
2231 else if ((ver & 0xff) == 4) // WinNT4
2232 return "sc"; // SMART_*, SCSI_PASS_THROUGH
2233 else // WinXP, 2003, Vista
2234 return "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
2235 // STORAGE_*, SCSI_MINIPORT_*
4d59bff9
GG
2236}
2237
2238
2127e193 2239// Common routines for devices with HANDLEs
4d59bff9 2240
2127e193
GI
2241win_smart_device::~win_smart_device() throw()
2242{
2243 if (m_fh != INVALID_HANDLE_VALUE)
2244 ::CloseHandle(m_fh);
832b75ed
GG
2245}
2246
2127e193
GI
2247bool win_smart_device::is_open() const
2248{
2249 return (m_fh != INVALID_HANDLE_VALUE);
2250}
832b75ed 2251
2127e193
GI
2252bool win_smart_device::close()
2253{
2254 if (m_fh == INVALID_HANDLE_VALUE)
2255 return true;
2256 BOOL rc = ::CloseHandle(m_fh);
2257 m_fh = INVALID_HANDLE_VALUE;
2258 return !!rc;
2259}
832b75ed 2260
2127e193 2261// ATA
832b75ed 2262
2127e193
GI
2263win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
2264: smart_device(intf, dev_name, "ata", req_type),
2265 m_usr_options(false),
2266 m_admin(false),
a23d5117 2267 m_id_is_cached(false),
2127e193
GI
2268 m_drive(0),
2269 m_port(-1),
2270 m_smartver_state(0)
2271{
2272}
2273
2274win_ata_device::~win_ata_device() throw()
832b75ed 2275{
832b75ed
GG
2276}
2277
2278
2127e193
GI
2279// Open ATA device
2280
2281bool win_ata_device::open()
832b75ed 2282{
2127e193
GI
2283 const char * name = skipdev(get_dev_name()); int len = strlen(name);
2284 // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
2285 char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
2286 if ( sscanf(name, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1
2287 && ((n1 == len && !options[0]) || n2 == len) ) {
2288 return open(drive[0] - 'a', -1, options, -1);
2289 }
2290 // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
2291 drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
2292 unsigned port = ~0;
2293 if ( sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
2294 && port < 32 && ((n1 == len && !options[0]) || n2 == len) ) {
2295 return open(drive[0] - 'a', -1, options, port);
2296 }
2297 // pd<m>,N => Physical drive <m>, RAID port N
2298 int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
2299 if ( sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
2300 && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
2301 return open(phydrive, -1, "", (int)port);
2302 }
2303 // [a-zA-Z]: => Physical drive behind logical drive 0-25
2304 int logdrive = drive_letter(name);
2305 if (logdrive >= 0) {
2306 return open(-1, logdrive, "", -1);
2307 }
2308
2309 return set_err(EINVAL);
832b75ed
GG
2310}
2311
2127e193
GI
2312
2313bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
832b75ed 2314{
2127e193 2315 // path depends on Windows Version
2127e193
GI
2316 char devpath[30];
2317 if (win9x && 0 <= phydrive && phydrive <= 7)
2318 // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
2319 strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
2320 else if (!win9x && 0 <= phydrive && phydrive <= 255)
2321 snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive);
2322 else if (!win9x && 0 <= logdrive && logdrive <= 'Z'-'A')
2323 snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
2324 else
2325 return set_err(ENOENT);
2326
2327 // Open device
2328 HANDLE h = INVALID_HANDLE_VALUE;
2329 if (win9x || !(*options && !options[strspn(options, "fp")])) {
2330 // Open with admin rights
2331 m_admin = true;
2332 h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
2333 FILE_SHARE_READ|FILE_SHARE_WRITE,
2334 NULL, OPEN_EXISTING, 0, 0);
2335 }
2336 if (!win9x && h == INVALID_HANDLE_VALUE) {
2337 // Open without admin rights
2338 m_admin = false;
2339 h = CreateFileA(devpath, 0,
2340 FILE_SHARE_READ|FILE_SHARE_WRITE,
2341 NULL, OPEN_EXISTING, 0, 0);
2342 }
2343 if (h == INVALID_HANDLE_VALUE) {
2344 long err = GetLastError();
a23d5117
GI
2345#if WIN9X_SUPPORT
2346 if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND)
2347 smartvsd_error();
2348#endif
2127e193 2349 if (err == ERROR_FILE_NOT_FOUND)
a23d5117 2350 set_err(ENOENT, "%s: not found", devpath);
2127e193
GI
2351 else if (err == ERROR_ACCESS_DENIED)
2352 set_err(EACCES, "%s: access denied", devpath);
2353 else
2354 set_err(EIO, "%s: Error=%ld", devpath, err);
2355 return false;
2356 }
2357 set_fh(h);
2358
a23d5117
GI
2359 // Warn once if admin rights are missing
2360 if (!m_admin) {
2361 static bool noadmin_warning = false;
2362 if (!noadmin_warning) {
2363 pout("Warning: Limited functionality due to missing admin rights\n");
2364 noadmin_warning = true;
2365 }
2366 }
2367
2127e193
GI
2368 if (con->reportataioctl > 1)
2369 pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
2370
2371 m_usr_options = false;
2372 if (*options) {
2373 // Save user options
2374 m_options = options; m_usr_options = true;
2375 }
2376 else if (port >= 0)
2377 // RAID: SMART_* and SCSI_MINIPORT
2378 m_options = "s3";
2379 else {
2380 // Set default options according to Windows version
2381 static const char * def_options = ata_get_def_options();
2382 m_options = def_options;
2383 }
2384
2385 // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
2386 m_drive = 0; m_port = port;
2387 if (!win9x && port < 0)
2388 return true;
2389
2390 // Win9X/ME: Get drive map
2391 // RAID: Get port map
2392 GETVERSIONINPARAMS_EX vers_ex;
2393 int devmap = smart_get_version(h, (port >= 0 ? &vers_ex : 0));
2394
2395 unsigned long portmap = 0;
2396 if (port >= 0 && devmap >= 0) {
2397 // 3ware RAID: check vendor id
2398 if (vers_ex.wIdentifier != SMART_VENDOR_3WARE) {
2399 pout("SMART_GET_VERSION returns unknown Identifier = %04x\n"
2400 "This is no 3ware 9000 controller or driver has no SMART support.\n",
2401 vers_ex.wIdentifier);
2402 devmap = -1;
2403 }
2404 else
2405 portmap = vers_ex.dwDeviceMapEx;
2406 }
2407 if (devmap < 0) {
2408 pout("%s: ATA driver has no SMART support\n", devpath);
2409 if (!is_permissive()) {
2410 close();
2411 return set_err(ENOSYS);
2412 }
2413 devmap = 0x0f;
2414 }
2415 m_smartver_state = 1;
2416
2417 if (port >= 0) {
2418 // 3ware RAID: update devicemap first
2419
2420 if (!update_3ware_devicemap_ioctl(h)) {
2421 if ( smart_get_version(h, &vers_ex) >= 0
2422 && vers_ex.wIdentifier == SMART_VENDOR_3WARE )
2423 portmap = vers_ex.dwDeviceMapEx;
2424 }
2425 // Check port existence
2426 if (!(portmap & (1L << port))) {
2427 if (!is_permissive()) {
2428 close();
2429 return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
2430 }
2431 }
2432 return true;
2433 }
2434
2435 // Win9x/ME: Check device presence & type
2436 if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) {
2437 unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10;
2438 // Win9x drive existence check may not work as expected
2439 // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
2440 // (The related KB Article Q196120 is no longer available)
2441 if (!is_permissive()) {
2442 close();
2443 return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
2444 devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap);
2445 }
2446 }
2447 // Drive number must be passed to ioctl
2448 m_drive = (phydrive & 0x3);
2449 return true;
832b75ed
GG
2450}
2451
2127e193 2452
a23d5117
GI
2453#if WIN9X_SUPPORT
2454
2127e193
GI
2455// Scan for ATA drives on Win9x/ME
2456
2457bool win9x_smart_interface::ata_scan(smart_device_list & devlist)
832b75ed 2458{
2127e193
GI
2459 // Open device
2460 const char devpath[] = "\\\\.\\SMARTVSD";
2461 HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
2462 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
2463 if (h == INVALID_HANDLE_VALUE) {
2464 if (con->reportataioctl > 1)
2465 pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
2466 return true; // SMARTVSD.VXD missing or no ATA devices
2467 }
2468
2469 // Get drive map
2470 int devmap = smart_get_version(h);
2471 CloseHandle(h);
2472 if (devmap < 0)
2473 return true; // Should not happen
2474
2475 // Check ATA device presence, remove ATAPI devices
2476 devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf);
2477 char name[20];
2478 for (int i = 0; i < 4; i++) {
2479 if (!(devmap & (1 << i)))
2480 continue;
2481 sprintf(name, "/dev/hd%c", 'a'+i);
bed94269 2482 devlist.push_back( new win_ata_device(this, name, "ata") );
2127e193
GI
2483 }
2484 return true;
832b75ed
GG
2485}
2486
a23d5117
GI
2487#endif // WIN9X_SUPPORT
2488
2127e193
GI
2489
2490// Scan for ATA drives
2491
2492bool winnt_smart_interface::ata_scan(smart_device_list & devlist)
4d59bff9 2493{
2127e193
GI
2494 const int max_raid = 2;
2495 bool raid_seen[max_raid] = {false, false};
2496
2497 char name[20];
2498 for (int i = 0; i <= 9; i++) {
2499 GETVERSIONINPARAMS_EX vers_ex;
2500 if (get_phy_drive_type(i, &vers_ex) != DEV_ATA)
2501 continue;
2502
2503 // Interpret RAID drive map if present
2504 if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
2505 // Skip if more than 2 controllers or logical drive from this controller already seen
2506 if (vers_ex.wControllerId >= max_raid || raid_seen[vers_ex.wControllerId])
2507 continue;
2508 raid_seen[vers_ex.wControllerId] = true;
2509 // Add physical drives
2510 for (int pi = 0; pi < 32; pi++) {
2511 if (vers_ex.dwDeviceMapEx & (1L << pi)) {
2512 sprintf(name, "/dev/sd%c,%u", 'a'+i, pi);
bed94269 2513 devlist.push_back( new win_ata_device(this, name, "ata") );
2127e193
GI
2514 }
2515 }
2516 continue;
2517 }
2518
2519 // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
2520 sprintf(name, "/dev/sd%c", 'a'+i);
bed94269 2521 devlist.push_back( new win_ata_device(this, name, "ata") );
2127e193
GI
2522 }
2523
2524 return true;
2525}
2526
2527
2528/////////////////////////////////////////////////////////////////////////////
2529
2530// Interface to ATA devices
2531bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
2532{
2533 // No multi-sector support for now, see above
2534 // warning about IOCTL_ATA_PASS_THROUGH
2535 if (!ata_cmd_is_ok(in,
2536 true, // data_out_support
2537 false, // !multi_sector_support
2538 true) // ata_48bit_support
2539 )
2540 return false;
2541
2542 // Determine ioctl functions valid for this ATA cmd
2543 const char * valid_options = 0;
2544
2545 switch (in.in_regs.command) {
2546 case ATA_IDENTIFY_DEVICE:
2547 case ATA_IDENTIFY_PACKET_DEVICE:
2548 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2549 // and SCSI_MINIPORT_* if requested by user
2550 valid_options = (m_usr_options ? "saicmf" : "saicf");
2551 break;
2552
2553 case ATA_CHECK_POWER_MODE:
2554 // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
2555 valid_options = "pai3";
2556 break;
2557
2558 case ATA_SMART_CMD:
2559 switch (in.in_regs.features) {
2560 case ATA_SMART_READ_VALUES:
2561 case ATA_SMART_READ_THRESHOLDS:
2562 case ATA_SMART_AUTOSAVE:
2563 case ATA_SMART_ENABLE:
2564 case ATA_SMART_DISABLE:
2565 case ATA_SMART_AUTO_OFFLINE:
2566 // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
2567 // and SCSI_MINIPORT_* if requested by user
2568 valid_options = (m_usr_options ? "saicmf" : "saicf");
2569 break;
2570
2571 case ATA_SMART_IMMEDIATE_OFFLINE:
2572 // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
a23d5117 2573 valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ?
2127e193
GI
2574 "saicm3" : "aicm3");
2575 break;
2576
2577 case ATA_SMART_READ_LOG_SECTOR:
2578 // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
2579 // Try SCSI_MINIPORT also to skip buggy class driver
2580 // SMART functions do not support multi sector I/O.
2581 if (in.size == 512)
a23d5117 2582 valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3");
2127e193
GI
2583 else
2584 valid_options = "a";
2585 break;
2586
2587 case ATA_SMART_WRITE_LOG_SECTOR:
2588 // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
2589 // but SCSI_MINIPORT_* only if requested by user and single sector.
2590 valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
2591 break;
2592
2593 case ATA_SMART_STATUS:
2594 // May require lba_mid,lba_high register return
2595 if (in.out_needed.is_set())
2596 valid_options = (m_usr_options ? "saimf" : "saif");
2597 else
2598 valid_options = (m_usr_options ? "saicmf" : "saicf");
2599 break;
2600
2601 default:
2602 // Unknown SMART command, handle below
2603 break;
2604 }
2605 break;
2606
2607 default:
2608 // Other ATA command, handle below
2609 break;
2610 }
2611
2612 if (!valid_options) {
2613 // No special ATA command found above, select a generic pass through ioctl.
2614 if (!( in.direction == ata_cmd_in::no_data
2615 || (in.direction == ata_cmd_in::data_in && in.size == 512))
2616 || in.in_regs.is_48bit_cmd() )
2617 // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
2618 valid_options = "a";
2619 else if (in.out_needed.is_set())
2620 // Need output registers: ATA/IDE_PASS_THROUGH
2621 valid_options = "ai";
2622 else
2623 valid_options = "aic";
2624 }
2625
2626 if (!m_admin) {
2627 // Restrict to IOCTL_STORAGE_*
2628 if (strchr(valid_options, 'f'))
2629 valid_options = "f";
2630 else if (strchr(valid_options, 'p'))
2631 valid_options = "p";
2632 else
2633 return set_err(ENOSYS, "Function requires admin rights");
2634 }
2635
2636 // Set IDEREGS
2637 IDEREGS regs, prev_regs;
2638 {
2639 const ata_in_regs & lo = in.in_regs;
2640 regs.bFeaturesReg = lo.features;
2641 regs.bSectorCountReg = lo.sector_count;
2642 regs.bSectorNumberReg = lo.lba_low;
2643 regs.bCylLowReg = lo.lba_mid;
2644 regs.bCylHighReg = lo.lba_high;
2645 regs.bDriveHeadReg = lo.device;
2646 regs.bCommandReg = lo.command;
2647 regs.bReserved = 0;
2648 }
2649 if (in.in_regs.is_48bit_cmd()) {
2650 const ata_in_regs & hi = in.in_regs.prev;
2651 prev_regs.bFeaturesReg = hi.features;
2652 prev_regs.bSectorCountReg = hi.sector_count;
2653 prev_regs.bSectorNumberReg = hi.lba_low;
2654 prev_regs.bCylLowReg = hi.lba_mid;
2655 prev_regs.bCylHighReg = hi.lba_high;
2656 prev_regs.bDriveHeadReg = hi.device;
2657 prev_regs.bCommandReg = hi.command;
2658 prev_regs.bReserved = 0;
2659 }
2660
2661 // Set data direction
2662 int datasize = 0;
2663 char * data = 0;
2664 switch (in.direction) {
2665 case ata_cmd_in::no_data:
2666 break;
2667 case ata_cmd_in::data_in:
2668 datasize = (int)in.size;
2669 data = (char *)in.buffer;
2670 break;
2671 case ata_cmd_in::data_out:
2672 datasize = -(int)in.size;
2673 data = (char *)in.buffer;
2674 break;
2675 default:
2676 return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
2677 (int)in.direction);
2678 }
2679
2680
2681 // Try all valid ioctls in the order specified in m_options
2682 bool powered_up = false;
2683 bool out_regs_set = false;
a23d5117 2684 bool id_is_cached = false;
2127e193
GI
2685 const char * options = m_options.c_str();
2686
2687 for (int i = 0; ; i++) {
2688 char opt = options[i];
2689
2690 if (!opt) {
2691 if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
2692 // Power up reported by GetDevicePowerState() and no ioctl available
2693 // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
2694 regs.bSectorCountReg = 0xff;
2695 out_regs_set = true;
2696 break;
2697 }
2698 // No IOCTL found
2699 return set_err(ENOSYS);
2700 }
2701 if (!strchr(valid_options, opt))
2702 // Invalid for this command
2703 continue;
2704
2705 errno = 0;
2706 assert( datasize == 0 || datasize == 512
2707 || (datasize == -512 && strchr("am", opt))
2708 || (datasize > 512 && opt == 'a'));
2709 int rc;
2710 switch (opt) {
2711 default: assert(0);
2712 case 's':
2713 // call SMART_GET_VERSION once for each drive
2714 if (m_smartver_state > 1) {
2715 rc = -1; errno = ENOSYS;
2716 break;
2717 }
2718 if (!m_smartver_state) {
2719 assert(m_port == -1);
2720 if (smart_get_version(get_fh()) < 0) {
2721 if (!con->permissive) {
2722 m_smartver_state = 2;
2723 rc = -1; errno = ENOSYS;
2724 break;
2725 }
2726 con->permissive--;
2727 }
2728 m_smartver_state = 1;
2729 }
2730 rc = smart_ioctl(get_fh(), m_drive, &regs, data, datasize, m_port);
2731 out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
a23d5117 2732 id_is_cached = (m_port < 0 && !win9x); // Not cached by 3ware or Win9x/ME driver
2127e193
GI
2733 break;
2734 case 'm':
2735 rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
a23d5117 2736 id_is_cached = (m_port < 0 && !win9x);
2127e193
GI
2737 break;
2738 case 'a':
2739 rc = ata_pass_through_ioctl(get_fh(), &regs,
2740 (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
2741 data, datasize);
2742 out_regs_set = true;
2743 break;
2744 case 'i':
2745 rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
2746 out_regs_set = true;
2747 break;
2748 case 'c':
2749 rc = ata_via_scsi_pass_through_ioctl(get_fh(), &regs, data, datasize);
2750 break;
2751 case 'f':
2752 if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
2753 rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
a23d5117 2754 id_is_cached = true;
2127e193
GI
2755 }
2756 else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
2757 case ATA_SMART_READ_VALUES:
2758 rc = storage_predict_failure_ioctl(get_fh(), data);
2759 if (rc > 0)
2760 rc = 0;
2761 break;
2127e193
GI
2762 case ATA_SMART_ENABLE:
2763 rc = 0;
2764 break;
2765 case ATA_SMART_STATUS:
2766 rc = storage_predict_failure_ioctl(get_fh());
a23d5117
GI
2767 if (rc == 0) {
2768 // Good SMART status
2769 out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
2770 }
2771 else if (rc > 0) {
2772 // Bad SMART status
2773 out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
2774 rc = 0;
2127e193
GI
2775 }
2776 break;
2777 default:
2778 errno = ENOSYS; rc = -1;
2779 }
2780 else {
2781 errno = ENOSYS; rc = -1;
2782 }
2783 break;
2784 case '3':
2785 rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
2786 out_regs_set = true;
2787 break;
2788 case 'p':
2789 assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
2790 rc = get_device_power_state(get_fh());
2791 if (rc == 0) {
2792 // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
2793 // spin up the drive => simulate ATA result STANDBY.
2794 regs.bSectorCountReg = 0x00;
2795 out_regs_set = true;
2796 }
2797 else if (rc > 0) {
2798 // Power up reported by GetDevicePowerState(), but this reflects the actual mode
2799 // only if it is selected by the device driver => try a passthrough ioctl to get the
2800 // actual mode, if none available simulate ACTIVE/IDLE.
2801 powered_up = true;
2802 rc = -1; errno = ENOSYS;
2803 }
2804 break;
2805 }
2806
2807 if (!rc)
2808 // Working ioctl found
2809 break;
2810
2811 if (errno != ENOSYS)
2812 // Abort on I/O error
2813 return set_err(errno);
2814
2815 out_regs_set = false;
2816 // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
2817 }
2818
2819 // Return IDEREGS if set
2820 if (out_regs_set) {
2821 ata_out_regs & lo = out.out_regs;
2822 lo.error = regs.bFeaturesReg;
2823 lo.sector_count = regs.bSectorCountReg;
2824 lo.lba_low = regs.bSectorNumberReg;
2825 lo.lba_mid = regs.bCylLowReg;
2826 lo.lba_high = regs.bCylHighReg;
2827 lo.device = regs.bDriveHeadReg;
2828 lo.status = regs.bCommandReg;
2829 if (in.in_regs.is_48bit_cmd()) {
2830 ata_out_regs & hi = out.out_regs.prev;
2831 hi.sector_count = prev_regs.bSectorCountReg;
2832 hi.lba_low = prev_regs.bSectorNumberReg;
2833 hi.lba_mid = prev_regs.bCylLowReg;
2834 hi.lba_high = prev_regs.bCylHighReg;
2835 }
2836 }
a23d5117
GI
2837
2838 if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
2839 || in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE)
2840 // Update ata_identify_is_cached() result according to ioctl used.
2841 m_id_is_cached = id_is_cached;
2842
2127e193
GI
2843 return true;
2844}
2845
2846// Return true if OS caches the ATA identify sector
2847bool win_ata_device::ata_identify_is_cached() const
2848{
a23d5117 2849 return m_id_is_cached;
4d59bff9
GG
2850}
2851
832b75ed
GG
2852
2853/////////////////////////////////////////////////////////////////////////////
a23d5117 2854// ASPI Interface (for SCSI devices on 9x/ME)
832b75ed
GG
2855/////////////////////////////////////////////////////////////////////////////
2856
a23d5117
GI
2857#if WIN9X_SUPPORT
2858
832b75ed
GG
2859#pragma pack(1)
2860
2861#define ASPI_SENSE_SIZE 18
2862
2863// ASPI SCSI Request block header
2864
2865typedef struct {
2127e193
GI
2866 unsigned char cmd; // 00: Command code
2867 unsigned char status; // 01: ASPI status
2868 unsigned char adapter; // 02: Host adapter number
2869 unsigned char flags; // 03: Request flags
2870 unsigned char reserved[4]; // 04: 0
832b75ed
GG
2871} ASPI_SRB_HEAD;
2872
2873// SRB for host adapter inquiry
2874
2875typedef struct {
2127e193
GI
2876 ASPI_SRB_HEAD h; // 00: Header
2877 unsigned char adapters; // 08: Number of adapters
2878 unsigned char target_id; // 09: Target ID ?
2879 char manager_id[16]; // 10: SCSI manager ID
2880 char adapter_id[16]; // 26: Host adapter ID
2881 unsigned char parameters[16]; // 42: Host adapter unique parmameters
832b75ed
GG
2882} ASPI_SRB_INQUIRY;
2883
2884// SRB for get device type
2885
2886typedef struct {
2127e193
GI
2887 ASPI_SRB_HEAD h; // 00: Header
2888 unsigned char target_id; // 08: Target ID
2889 unsigned char lun; // 09: LUN
2890 unsigned char devtype; // 10: Device type
2891 unsigned char reserved; // 11: Reserved
832b75ed
GG
2892} ASPI_SRB_DEVTYPE;
2893
2894// SRB for SCSI I/O
2895
2896typedef struct {
2127e193
GI
2897 ASPI_SRB_HEAD h; // 00: Header
2898 unsigned char target_id; // 08: Target ID
2899 unsigned char lun; // 09: LUN
2900 unsigned char reserved[2]; // 10: Reserved
2901 unsigned long data_size; // 12: Data alloc. lenght
2902 void * data_addr; // 16: Data buffer pointer
2903 unsigned char sense_size; // 20: Sense alloc. length
2904 unsigned char cdb_size; // 21: CDB length
2905 unsigned char host_status; // 22: Host status
2906 unsigned char target_status; // 23: Target status
2907 void * event_handle; // 24: Event handle
2908 unsigned char workspace[20]; // 28: ASPI workspace
2909 unsigned char cdb[16+ASPI_SENSE_SIZE];
832b75ed
GG
2910} ASPI_SRB_IO;
2911
2912// Macro to retrieve start of sense information
2913#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
2914
2915// SRB union
2916
2917typedef union {
2127e193
GI
2918 ASPI_SRB_HEAD h; // Common header
2919 ASPI_SRB_INQUIRY q; // Inquiry
2920 ASPI_SRB_DEVTYPE t; // Device type
2921 ASPI_SRB_IO i; // I/O
832b75ed
GG
2922} ASPI_SRB;
2923
2924#pragma pack()
2925
2926// ASPI commands
2927#define ASPI_CMD_ADAPTER_INQUIRE 0x00
2928#define ASPI_CMD_GET_DEVICE_TYPE 0x01
2929#define ASPI_CMD_EXECUTE_IO 0x02
2930#define ASPI_CMD_ABORT_IO 0x03
2931
2932// Request flags
2933#define ASPI_REQFLAG_DIR_TO_HOST 0x08
2934#define ASPI_REQFLAG_DIR_TO_TARGET 0x10
2935#define ASPI_REQFLAG_DIR_NO_XFER 0x18
2936#define ASPI_REQFLAG_EVENT_NOTIFY 0x40
2937
2938// ASPI status
2939#define ASPI_STATUS_IN_PROGRESS 0x00
2940#define ASPI_STATUS_NO_ERROR 0x01
2941#define ASPI_STATUS_ABORTED 0x02
2942#define ASPI_STATUS_ABORT_ERR 0x03
2943#define ASPI_STATUS_ERROR 0x04
2944#define ASPI_STATUS_INVALID_COMMAND 0x80
2945#define ASPI_STATUS_INVALID_ADAPTER 0x81
2946#define ASPI_STATUS_INVALID_TARGET 0x82
2947#define ASPI_STATUS_NO_ADAPTERS 0xE8
2948
2949// Adapter (host) status
2950#define ASPI_HSTATUS_NO_ERROR 0x00
2951#define ASPI_HSTATUS_SELECTION_TIMEOUT 0x11
2952#define ASPI_HSTATUS_DATA_OVERRUN 0x12
2953#define ASPI_HSTATUS_BUS_FREE 0x13
2954#define ASPI_HSTATUS_BUS_PHASE_ERROR 0x14
2955#define ASPI_HSTATUS_BAD_SGLIST 0x1A
2956
2957// Target status
2958#define ASPI_TSTATUS_NO_ERROR 0x00
2959#define ASPI_TSTATUS_CHECK_CONDITION 0x02
2960#define ASPI_TSTATUS_BUSY 0x08
2961#define ASPI_TSTATUS_RESERV_CONFLICT 0x18
2962
2963
2964static HINSTANCE h_aspi_dll; // DLL handle
2965static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
2966static unsigned num_aspi_adapters;
2967
2968#ifdef __CYGWIN__
2969// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
2970static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
2971#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
2972#else
2973#define aspi_entry_valid() (!!aspi_entry)
2974#endif
2975
2976
2977static int aspi_call(ASPI_SRB * srb)
2978{
2127e193
GI
2979 int i;
2980 aspi_entry(srb);
2981 i = 0;
2982 while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
2983 if (++i > 100/*10sek*/) {
2984 pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
2985 aspi_entry = 0;
2986 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
2987 errno = EIO;
2988 return -1;
2989 }
2990 if (con->reportscsiioctl > 1)
2991 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
2992 Sleep(100);
2993 }
2994 return 0;
832b75ed
GG
2995}
2996
2997
2998// Get ASPI entrypoint from wnaspi32.dll
2999
3000static FARPROC aspi_get_address(const char * name, int verbose)
3001{
2127e193
GI
3002 FARPROC addr;
3003 assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
3004
3005 if (!(addr = GetProcAddress(h_aspi_dll, name))) {
3006 if (verbose)
3007 pout("Missing %s() in WNASPI32.DLL\n", name);
3008 aspi_entry = 0;
3009 FreeLibrary(h_aspi_dll);
3010 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
3011 errno = ENOSYS;
3012 return 0;
3013 }
3014 return addr;
832b75ed
GG
3015}
3016
3017
3018static int aspi_open_dll(int verbose)
3019{
2127e193
GI
3020 UINT (*aspi_info)(void);
3021 UINT info, rc;
3022
3023 assert(!aspi_entry_valid());
3024
3025 // Check structure layout
3026 assert(sizeof(ASPI_SRB_HEAD) == 8);
3027 assert(sizeof(ASPI_SRB_INQUIRY) == 58);
3028 assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
3029 assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
3030 assert(offsetof(ASPI_SRB,h.cmd) == 0);
3031 assert(offsetof(ASPI_SRB,h.flags) == 3);
3032 assert(offsetof(ASPI_SRB_IO,lun) == 9);
3033 assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
3034 assert(offsetof(ASPI_SRB_IO,workspace) == 28);
3035 assert(offsetof(ASPI_SRB_IO,cdb) == 48);
3036
3037 if (h_aspi_dll == INVALID_HANDLE_VALUE) {
3038 // do not retry
3039 errno = ENOENT;
3040 return -1;
3041 }
3042
3043 // Load ASPI DLL
3044 if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
3045 if (verbose)
3046 pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
3047 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
3048 errno = ENOENT;
3049 return -1;
3050 }
3051 if (con->reportscsiioctl > 1) {
3052 // Print full path of WNASPI32.DLL
3053 char path[MAX_PATH];
3054 if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
3055 strcpy(path, "*unknown*");
3056 pout("Using ASPI interface \"%s\"\n", path);
3057 }
3058
3059 // Get ASPI entrypoints
3060 if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
3061 return -1;
3062 if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
3063 return -1;
3064
3065 // Init ASPI manager and get number of adapters
3066 info = (aspi_info)();
3067 if (con->reportscsiioctl > 1)
3068 pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
3069 rc = (info >> 8) & 0xff;
3070 if (rc == ASPI_STATUS_NO_ADAPTERS) {
3071 num_aspi_adapters = 0;
3072 }
3073 else if (rc == ASPI_STATUS_NO_ERROR) {
3074 num_aspi_adapters = info & 0xff;
3075 }
3076 else {
3077 if (verbose)
3078 pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
3079 aspi_entry = 0;
3080 FreeLibrary(h_aspi_dll);
3081 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
3082 errno = ENOENT;
3083 return -1;
3084 }
3085
3086 if (con->reportscsiioctl)
3087 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
832b75ed
GG
3088
3089#ifdef __CYGWIN__
2127e193
GI
3090 // save PID to detect fork() in aspi_entry_valid()
3091 aspi_dll_pid = GetCurrentProcessId();
832b75ed 3092#endif
2127e193
GI
3093 assert(aspi_entry_valid());
3094 return 0;
832b75ed
GG
3095}
3096
3097
3098static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
3099{
2127e193
GI
3100 HANDLE event;
3101 // Create event
3102 if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
3103 pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
3104 }
3105 srb->i.event_handle = event;
3106 srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
3107 // Start ASPI request
3108 aspi_entry(srb);
3109 if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
3110 // Wait for event
3111 DWORD rc = WaitForSingleObject(event, timeout*1000L);
3112 if (rc != WAIT_OBJECT_0) {
3113 if (rc == WAIT_TIMEOUT) {
3114 pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
3115 srb->h.adapter, srb->i.target_id, timeout);
3116 }
3117 else {
3118 pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
3119 (unsigned long)event, rc, rc, GetLastError());
3120 }
3121 // TODO: ASPI_ABORT_IO command
3122 aspi_entry = 0;
3123 h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
3124 return -EIO;
3125 }
3126 }
3127 CloseHandle(event);
3128 return 0;
832b75ed
GG
3129}
3130
3131
2127e193
GI
3132win_aspi_device::win_aspi_device(smart_interface * intf,
3133 const char * dev_name, const char * req_type)
3134: smart_device(intf, dev_name, "scsi", req_type),
3135 m_adapter(-1), m_id(0)
3136{
3137}
3138
3139bool win_aspi_device::is_open() const
3140{
3141 return (m_adapter >= 0);
3142}
3143
3144bool win_aspi_device::open()
3145{
3146 // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
3147 unsigned adapter = ~0, id = ~0; int n1 = -1;
3148 const char * name = skipdev(get_dev_name());
3149 if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name)
3150 && adapter <= 9 && id < 16))
3151 return set_err(EINVAL);
3152
3153 if (!aspi_entry_valid()) {
3154 if (aspi_open_dll(1/*verbose*/))
3155 return set_err(ENOENT);
3156 }
3157
3158 // Adapter OK?
3159 if (adapter >= num_aspi_adapters) {
3160 pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
3161 adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
3162 if (!is_permissive())
3163 return set_err(ENOENT);
3164 }
3165
3166 // Device present ?
3167 ASPI_SRB srb;
3168 memset(&srb, 0, sizeof(srb));
3169 srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
3170 srb.h.adapter = adapter; srb.i.target_id = id;
3171 if (aspi_call(&srb))
3172 return set_err(EIO);
3173 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
3174 pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
3175 if (!is_permissive())
3176 return set_err(srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
3177 }
3178 else if (con->reportscsiioctl)
3179 pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
3180
3181 m_adapter = (int)adapter; m_id = (unsigned char)id;
3182 return true;
3183}
3184
3185
3186bool win_aspi_device::close()
3187{
3188 // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
3189 return true;
3190}
3191
3192
3193// Scan for ASPI drives
832b75ed 3194
2127e193
GI
3195bool win9x_smart_interface::scsi_scan(smart_device_list & devlist)
3196{
3197 if (!aspi_entry_valid()) {
3198 if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
3199 return true;
3200 }
3201
3202 for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {
3203 ASPI_SRB srb;
3204
3205 if (ad > 9) {
3206 if (con->reportscsiioctl)
3207 pout(" ASPI Adapter %u: Ignored\n", ad);
3208 continue;
3209 }
3210
3211 // Get adapter name
3212 memset(&srb, 0, sizeof(srb));
3213 srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
3214 srb.h.adapter = ad;
3215 if (aspi_call(&srb))
3216 break;
3217
3218 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
3219 if (con->reportscsiioctl)
3220 pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
3221 continue;
3222 }
3223
3224 if (con->reportscsiioctl) {
3225 for (int i = 1; i < 16 && srb.q.adapter_id[i]; i++)
3226 if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
3227 srb.q.adapter_id[i] = '?';
3228 pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
3229 }
3230
3231 bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5);
3232
3233 for (unsigned id = 0; id <= 7; id++) {
3234 // Get device type
3235 memset(&srb, 0, sizeof(srb));
3236 srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
3237 srb.h.adapter = ad; srb.i.target_id = id;
3238 if (aspi_call(&srb))
3239 return 0;
3240 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
3241 if (con->reportscsiioctl > 1)
3242 pout(" ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
3243 continue;
3244 }
3245
3246 if (!ignore && srb.t.devtype == 0x00/*HDD*/) {
3247 if (con->reportscsiioctl)
3248 pout(" ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
3249 char name[20];
3250 sprintf(name, "/dev/scsi%u%u", ad, id);
bed94269 3251 devlist.push_back( new win_aspi_device(this, name, "scsi") );
2127e193
GI
3252 }
3253 else if (con->reportscsiioctl)
3254 pout(" ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);
3255 }
3256 }
3257 return true;
3258}
3259
3260
3261// Interface to ASPI SCSI devices
3262bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)
3263{
3264 int report = con->reportscsiioctl; // TODO
3265
3266 if (m_adapter < 0) {
3267 set_err(EBADF);
3268 return false;
3269 }
3270
3271 if (!aspi_entry_valid()) {
3272 set_err(EBADF);
3273 return false;
3274 }
3275
3276 if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
3277 set_err(EINVAL, "bad CDB length");
3278 return false;
3279 }
3280
3281 if (report > 0) {
3282 // From os_linux.c
3283 int k, j;
3284 const unsigned char * ucp = iop->cmnd;
3285 const char * np;
3286 char buff[256];
3287 const int sz = (int)sizeof(buff);
3288
3289 np = scsi_get_opcode_name(ucp[0]);
3290 j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
3291 for (k = 0; k < (int)iop->cmnd_len; ++k)
3292 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
3293 if ((report > 1) &&
3294 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
3295 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3296
3297 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing "
3298 "data, len=%d%s:\n", (int)iop->dxfer_len,
3299 (trunc ? " [only first 256 bytes shown]" : ""));
3300 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
3301 }
3302 else
3303 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
3304 pout(buff);
3305 }
3306
3307 ASPI_SRB srb;
3308 memset(&srb, 0, sizeof(srb));
3309 srb.h.cmd = ASPI_CMD_EXECUTE_IO;
3310 srb.h.adapter = m_adapter;
3311 srb.i.target_id = m_id;
3312 //srb.i.lun = 0;
3313 srb.i.sense_size = ASPI_SENSE_SIZE;
3314 srb.i.cdb_size = iop->cmnd_len;
3315 memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
3316
3317 switch (iop->dxfer_dir) {
3318 case DXFER_NONE:
3319 srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
3320 break;
3321 case DXFER_FROM_DEVICE:
3322 srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
3323 srb.i.data_size = iop->dxfer_len;
3324 srb.i.data_addr = iop->dxferp;
3325 break;
3326 case DXFER_TO_DEVICE:
3327 srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
3328 srb.i.data_size = iop->dxfer_len;
3329 srb.i.data_addr = iop->dxferp;
3330 break;
3331 default:
3332 set_err(EINVAL, "bad dxfer_dir");
3333 return false;
3334 }
3335
3336 iop->resp_sense_len = 0;
3337 iop->scsi_status = 0;
3338 iop->resid = 0;
3339
3340 if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
3341 // Timeout
3342 set_err(EIO, "ASPI Timeout"); return false;
3343 }
3344
3345 if (srb.h.status != ASPI_STATUS_NO_ERROR) {
3346 if ( srb.h.status == ASPI_STATUS_ERROR
3347 && srb.i.host_status == ASPI_HSTATUS_NO_ERROR
3348 && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
3349 // Sense valid
3350 const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
3351 int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
3352 iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
3353 if (len > 0 && iop->sensep) {
3354 memcpy(iop->sensep, sense, len);
3355 iop->resp_sense_len = len;
3356 if (report > 1) {
3357 pout(" >>> Sense buffer, len=%d:\n", (int)len);
3358 dStrHex(iop->sensep, len , 1);
3359 }
3360 }
3361 if (report) {
3362 pout(" sense_key=%x asc=%x ascq=%x\n",
3363 sense[2] & 0xf, sense[12], sense[13]);
3364 }
3365 return true;
3366 }
3367 else {
3368 if (report)
3369 pout(" ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
3370 set_err(EIO);
3371 return false;
3372 }
3373 }
3374
3375 if (report > 0)
3376 pout(" OK\n");
3377
3378 if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
3379 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3380 pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
3381 (trunc ? " [only first 256 bytes shown]" : ""));
3382 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
3383 }
3384
3385 return true;
832b75ed 3386}
ba59cff1 3387
a23d5117 3388#endif // WIN9X_SUPPORT
ba59cff1
GG
3389
3390/////////////////////////////////////////////////////////////////////////////
3391// SPT Interface (for SCSI devices and ATA devices behind SATLs)
3392// Only supported in NT and later
3393/////////////////////////////////////////////////////////////////////////////
3394
2127e193
GI
3395win_scsi_device::win_scsi_device(smart_interface * intf,
3396 const char * dev_name, const char * req_type)
3397: smart_device(intf, dev_name, "scsi", req_type)
3398{
3399}
ba59cff1 3400
2127e193
GI
3401bool win_scsi_device::open()
3402{
3403 const char * name = skipdev(get_dev_name()); int len = strlen(name);
3404 // sd[a-z],N => Physical drive 0-26, RAID port N
3405 char drive[1+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
3406 if ( sscanf(name, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
3407 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0)) ) {
3408 return open(drive[0] - 'a', -1, -1, sub_addr);
3409 }
3410 // pd<m>,N => Physical drive <m>, RAID port N
3411 int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
3412 if ( sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
3413 && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
3414 return open(pd_num, -1, -1, sub_addr);
3415 }
3416 // [a-zA-Z]: => Physical drive behind logical drive 0-25
3417 int logdrive = drive_letter(name);
3418 if (logdrive >= 0) {
3419 return open(-1, logdrive, -1, -1);
3420 }
3421 // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
3422 int tape_num = -1; n1 = -1;
3423 if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
3424 return open(-1, -1, tape_num, -1);
3425 }
3426 tape_num = -1; n1 = -1;
3427 if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
3428 return open(-1, -1, tape_num, -1);
3429 }
3430 // tape<m> => tape drive <m>
3431 tape_num = -1; n1 = -1;
3432 if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
3433 return open(-1, -1, tape_num, -1);
3434 }
3435
3436 return set_err(EINVAL);
3437}
ba59cff1 3438
2127e193
GI
3439bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
3440{
3441 char b[128];
3442 b[sizeof(b) - 1] = '\0';
3443 if (pd_num >= 0)
3444 snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
3445 else if (ld_num >= 0)
3446 snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
3447 else if (tape_num >= 0)
3448 snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
3449 else {
3450 set_err(EINVAL);
3451 return false;
3452 }
3453
3454 // Open device
3455 HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
3456 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
3457 OPEN_EXISTING, 0, 0);
3458 if (h == INVALID_HANDLE_VALUE) {
3459 set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());
3460 return false;
3461 }
3462 set_fh(h);
3463 return true;
3464}
3465
3466
3467bool winnt_smart_interface::scsi_scan(smart_device_list & devlist)
3468{
3469 char name[20];
3470 for (int i = 0; i <= 9; i++) {
3471 if (get_phy_drive_type(i) != DEV_SCSI)
3472 continue;
3473 // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
3474 sprintf(name, "/dev/sd%c", 'a'+i);
bed94269 3475 devlist.push_back( new win_scsi_device(this, name, "scsi") );
2127e193
GI
3476 }
3477 return true;
a37e7145
GG
3478}
3479
3480
ba59cff1 3481#define IOCTL_SCSI_PASS_THROUGH_DIRECT \
2127e193 3482 CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
ba59cff1
GG
3483
3484typedef struct _SCSI_PASS_THROUGH_DIRECT {
2127e193
GI
3485 USHORT Length;
3486 UCHAR ScsiStatus;
3487 UCHAR PathId;
3488 UCHAR TargetId;
3489 UCHAR Lun;
3490 UCHAR CdbLength;
3491 UCHAR SenseInfoLength;
3492 UCHAR DataIn;
3493 ULONG DataTransferLength;
3494 ULONG TimeOutValue;
3495 PVOID DataBuffer;
3496 ULONG SenseInfoOffset;
3497 UCHAR Cdb[16];
ba59cff1
GG
3498} SCSI_PASS_THROUGH_DIRECT;
3499
3500typedef struct {
2127e193
GI
3501 SCSI_PASS_THROUGH_DIRECT spt;
3502 ULONG Filler;
3503 UCHAR ucSenseBuf[64];
ba59cff1
GG
3504} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
3505
3506
2127e193
GI
3507// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
3508// Used if DataTransferLength not supported by *_DIRECT.
3509static long scsi_pass_through_indirect(HANDLE h,
3510 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
3511{
3512 struct SCSI_PASS_THROUGH_WITH_BUFFERS {
3513 SCSI_PASS_THROUGH spt;
3514 ULONG Filler;
3515 UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
3516 UCHAR ucDataBuf[512];
3517 };
3518
3519 SCSI_PASS_THROUGH_WITH_BUFFERS sb;
3520 memset(&sb, 0, sizeof(sb));
3521
3522 // DATA_OUT not implemented yet
3523 if (!( sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
3524 && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
3525 return ERROR_INVALID_PARAMETER;
3526
3527 sb.spt.Length = sizeof(sb.spt);
3528 sb.spt.CdbLength = sbd->spt.CdbLength;
3529 memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
3530 sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
3531 sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
3532 sb.spt.DataIn = sbd->spt.DataIn;
3533 sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
3534 sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
3535 sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
3536
3537 DWORD num_out;
3538 if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
3539 &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
3540 return GetLastError();
3541
3542 sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
3543 if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
3544 memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
3545
3546 sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
3547 if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
3548 memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
3549 return 0;
3550}
3551
3552
ba59cff1 3553// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
2127e193
GI
3554bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
3555{
3556 int report = con->reportscsiioctl; // TODO
3557
3558 if (report > 0) {
3559 int k, j;
3560 const unsigned char * ucp = iop->cmnd;
3561 const char * np;
3562 char buff[256];
3563 const int sz = (int)sizeof(buff);
3564
3565 np = scsi_get_opcode_name(ucp[0]);
3566 j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
3567 for (k = 0; k < (int)iop->cmnd_len; ++k)
3568 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
3569 if ((report > 1) &&
3570 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
3571 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3572
3573 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing "
3574 "data, len=%d%s:\n", (int)iop->dxfer_len,
3575 (trunc ? " [only first 256 bytes shown]" : ""));
3576 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
3577 }
3578 else
3579 j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
3580 pout(buff);
3581 }
3582
3583 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
3584 if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
3585 set_err(EINVAL, "cmnd_len too large");
3586 return false;
3587 }
3588
3589 memset(&sb, 0, sizeof(sb));
3590 sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
3591 sb.spt.CdbLength = iop->cmnd_len;
3592 memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
3593 sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
3594 sb.spt.SenseInfoOffset =
3595 offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
3596 sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
3597
3598 bool direct = true;
3599 switch (iop->dxfer_dir) {
3600 case DXFER_NONE:
3601 sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
3602 break;
3603 case DXFER_FROM_DEVICE:
3604 sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
3605 sb.spt.DataTransferLength = iop->dxfer_len;
3606 sb.spt.DataBuffer = iop->dxferp;
3607 // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
a23d5117 3608 // transfers (needed for SMART STATUS check of JMicron USB bridges)
2127e193
GI
3609 if (sb.spt.DataTransferLength == 1)
3610 direct = false;
3611 break;
3612 case DXFER_TO_DEVICE:
3613 sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
3614 sb.spt.DataTransferLength = iop->dxfer_len;
3615 sb.spt.DataBuffer = iop->dxferp;
3616 break;
3617 default:
3618 set_err(EINVAL, "bad dxfer_dir");
3619 return false;
3620 }
3621
3622 long err = 0;
3623 if (direct) {
3624 DWORD num_out;
3625 if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
3626 &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
3627 err = GetLastError();
3628 }
3629 else
3630 err = scsi_pass_through_indirect(get_fh(), &sb);
3631
3632 if (err)
3633 return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
3634 "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
3635 (direct ? "_DIRECT" : ""), err);
3636
3637 iop->scsi_status = sb.spt.ScsiStatus;
3638 if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
3639 int slen = sb.ucSenseBuf[7] + 8;
3640
3641 if (slen > (int)sizeof(sb.ucSenseBuf))
3642 slen = sizeof(sb.ucSenseBuf);
3643 if (slen > (int)iop->max_sense_len)
3644 slen = iop->max_sense_len;
3645 memcpy(iop->sensep, sb.ucSenseBuf, slen);
3646 iop->resp_sense_len = slen;
3647 if (report) {
3648 if ((iop->sensep[0] & 0x7f) > 0x71)
3649 pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
3650 iop->scsi_status, iop->sensep[1] & 0xf,
3651 iop->sensep[2], iop->sensep[3]);
3652 else
3653 pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
3654 iop->scsi_status, iop->sensep[2] & 0xf,
3655 iop->sensep[12], iop->sensep[13]);
3656 }
3657 } else
3658 iop->resp_sense_len = 0;
3659
3660 if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
3661 iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
3662 else
3663 iop->resid = 0;
3664
3665 if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
3666 int trunc = (iop->dxfer_len > 256) ? 1 : 0;
3667 pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
3668 (trunc ? " [only first 256 bytes shown]" : ""));
3669 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
3670 }
3671 return true;
ba59cff1 3672}
2127e193
GI
3673
3674
3675//////////////////////////////////////////////////////////////////////////////////////////////////
3676
3677
3678} // namespace
3679
3680/////////////////////////////////////////////////////////////////////////////
3681
3682// Initialize platform interface and register with smi()
3683void smart_interface::init()
3684{
3685 // Select interface for Windows flavor
a23d5117
GI
3686 if (GetVersion() & 0x80000000) {
3687#if WIN9X_SUPPORT
2127e193
GI
3688 static os_win32::win9x_smart_interface the_win9x_interface;
3689 smart_interface::set(&the_win9x_interface);
a23d5117
GI
3690#else
3691 throw std::runtime_error("Win9x/ME not supported");
3692#endif
2127e193
GI
3693 }
3694 else {
3695 static os_win32::winnt_smart_interface the_winnt_interface;
3696 smart_interface::set(&the_winnt_interface);
3697 }
3698}
3699