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