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