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