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