]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_win32.cpp
Imported Upstream version 5.42+svn3561
[mirror_smartmontools-debian.git] / os_win32.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * os_win32.cpp
832b75ed
GG
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
d008864d 6 * Copyright (C) 2004-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
f4e463df 7 * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
832b75ed
GG
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
2127e193 15 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
832b75ed
GG
16 *
17 */
18
19#include "config.h"
cfbba5b9
GI
20#define WINVER 0x0502
21#define _WIN32_WINNT WINVER
7f0798ef 22
832b75ed
GG
23#include "int64.h"
24#include "atacmds.h"
832b75ed
GG
25#include "scsicmds.h"
26#include "utility.h"
cfbba5b9 27#include "smartctl.h" // TODO: Do not use smartctl only variables here
2127e193
GI
28
29#include "dev_interface.h"
30#include "dev_ata_cmd_set.h"
832b75ed 31
cfbba5b9
GI
32#include "os_win32/wmiquery.h"
33
832b75ed 34#include <errno.h>
2127e193 35
832b75ed
GG
36#ifdef _DEBUG
37#include <assert.h>
38#else
2127e193 39#undef assert
a23d5117 40#define assert(x) /* */
832b75ed 41#endif
2127e193 42
832b75ed
GG
43#include <stddef.h> // offsetof()
44#include <io.h> // access()
45
cfbba5b9
GI
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)
7f0798ef
GI
60#include <ddk/ntdddisk.h>
61#include <ddk/ntddscsi.h>
62#include <ddk/ntddstor.h>
63#else
d008864d
GI
64// MSVC10, older MinGW
65// (Missing: IOCTL_SCSI_MINIPORT_*)
7f0798ef 66#include <ntddscsi.h>
cfbba5b9 67#include <winioctl.h>
7f0798ef
GI
68#endif
69
cfbba5b9
GI
70// CSMI support
71#include "csmisas.h"
72
a23d5117
GI
73#ifdef __CYGWIN__
74#include <cygwin/version.h> // CYGWIN_VERSION_DLL_MAJOR
75#endif
76
4d59bff9
GG
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
7f0798ef
GI
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
f4e463df 89const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 3558 2012-06-05 16:42:05Z chrfranke $";
a23d5117
GI
90
91// Disable Win9x/ME specific code if no longer supported by compiler.
7f0798ef
GI
92#ifdef _WIN64
93 #undef WIN9X_SUPPORT
94#elif !defined(WIN9X_SUPPORT)
a23d5117
GI
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
832b75ed 103
7f0798ef
GI
104/////////////////////////////////////////////////////////////////////////////
105// Windows I/O-controls, some declarations are missing in the include files
106
107extern "C" {
108
109// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
110
111ASSERT_CONST(SMART_GET_VERSION, 0x074080);
112ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
113ASSERT_CONST(SMART_RCV_DRIVE_DATA, 0x07c088);
114ASSERT_SIZEOF(GETVERSIONINPARAMS, 24);
115ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
116ASSERT_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
130typedef struct {
131 IDEREGS IdeReg;
132 ULONG DataBufferSize;
133 UCHAR DataBuffer[1];
134} ATA_PASS_THROUGH;
135
136#pragma pack()
137
138ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
139ASSERT_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
149typedef 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
173ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
174ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, SELECT_WIN_32_64(40, 48));
175
176
177// IOCTL_SCSI_PASS_THROUGH[_DIRECT]
178
179ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
180ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH_DIRECT, 0x04d014);
181ASSERT_SIZEOF(SCSI_PASS_THROUGH, SELECT_WIN_32_64(44, 56));
182ASSERT_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
7f0798ef 188#define FILE_DEVICE_SCSI 0x001b
d008864d
GI
189#endif
190
191#ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
7f0798ef
GI
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
d008864d 207#endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
7f0798ef
GI
208
209ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
210ASSERT_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
220typedef 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
236typedef enum _STORAGE_QUERY_TYPE {
237 PropertyStandardQuery = 0,
238 PropertyExistsQuery,
239 PropertyMaskQuery,
240 PropertyQueryMaxDefined
241} STORAGE_QUERY_TYPE;
242
243typedef enum _STORAGE_PROPERTY_ID {
244 StorageDeviceProperty = 0,
245 StorageAdapterProperty,
246 StorageDeviceIdProperty,
247 StorageDeviceUniqueIdProperty,
248 StorageDeviceWriteCacheProperty,
249 StorageMiniportProperty,
250 StorageAccessAlignmentProperty
251} STORAGE_PROPERTY_ID;
252
253typedef 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
261ASSERT_CONST(IOCTL_STORAGE_QUERY_PROPERTY, 0x002d1400);
262ASSERT_SIZEOF(STORAGE_DEVICE_DESCRIPTOR, 36+1+3);
263ASSERT_SIZEOF(STORAGE_PROPERTY_QUERY, 8+1+3);
264
265
266// IOCTL_STORAGE_PREDICT_FAILURE
267
268ASSERT_CONST(IOCTL_STORAGE_PREDICT_FAILURE, 0x002d1100);
269ASSERT_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
278typedef 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
290typedef 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
302ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONINPARAMS));
303ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
304
cfbba5b9
GI
305
306// CSMI structs
307
308ASSERT_SIZEOF(IOCTL_HEADER, sizeof(SRB_IO_CONTROL));
309ASSERT_SIZEOF(CSMI_SAS_DRIVER_INFO_BUFFER, 204);
310ASSERT_SIZEOF(CSMI_SAS_PHY_INFO_BUFFER, 2080);
311ASSERT_SIZEOF(CSMI_SAS_STP_PASSTHRU_BUFFER, 168);
312
7f0798ef
GI
313} // extern "C"
314
2127e193
GI
315/////////////////////////////////////////////////////////////////////////////
316
317namespace os_win32 { // no need to publish anything, name provided for Doxygen
318
319#ifdef _MSC_VER
320#pragma warning(disable:4250)
321#endif
322
a23d5117
GI
323// Running on Win9x/ME ?
324#if WIN9X_SUPPORT
325// Set true in win9x_smart_interface ctor.
326static bool win9x = false;
327#else
328// Never true (const allows compiler to remove dead code).
329const bool win9x = false;
330#endif
331
332
2127e193
GI
333class win_smart_device
334: virtual public /*implements*/ smart_device
335{
336public:
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
348protected:
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
357private:
358 HANDLE m_fh; ///< File handle
359};
360
361
362/////////////////////////////////////////////////////////////////////////////
363
364class win_ata_device
365: public /*implements*/ ata_device,
366 public /*extends*/ win_smart_device
367{
368public:
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
379private:
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?
a23d5117 385 bool m_id_is_cached; // ata_identify_is_cached() return value.
a7e8ffec 386 bool m_is_3ware; // AMCC/3ware controller detected?
2127e193
GI
387 int m_drive, m_port;
388 int m_smartver_state;
389};
390
391
392/////////////////////////////////////////////////////////////////////////////
393
394class win_scsi_device
395: public /*implements*/ scsi_device,
396 virtual public /*extends*/ win_smart_device
397{
398public:
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
405private:
406 bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
407};
408
409
410/////////////////////////////////////////////////////////////////////////////
411
a23d5117
GI
412#if WIN9X_SUPPORT
413
2127e193
GI
414class win_aspi_device
415: public /*implements*/ scsi_device
416{
417public:
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
428private:
429 int m_adapter;
430 unsigned char m_id;
431};
432
a23d5117 433#endif // WIN9X_SUPPORT
2127e193 434
cfbba5b9
GI
435
436//////////////////////////////////////////////////////////////////////
437
438class csmi_device
439: virtual public /*extends*/ smart_device
440{
441public:
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
448protected:
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
464private:
465 CSMI_SAS_PHY_ENTITY m_phy_ent; ///< CSMI info for this phy
466};
467
468
469class csmi_ata_device
470: virtual public /*extends*/ csmi_device,
471 virtual public /*implements*/ ata_device
472{
473public:
474 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
475
476protected:
477 csmi_ata_device()
478 : smart_device(never_called) { }
479};
480
481
482//////////////////////////////////////////////////////////////////////
483
484class win_csmi_device
485: public /*implements*/ csmi_ata_device
486{
487public:
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
501protected:
502 virtual bool csmi_ioctl(unsigned code, IOCTL_HEADER * csmi_buffer,
503 unsigned csmi_bufsiz);
504
505private:
506 HANDLE m_fh; ///< Controller device handle
507 unsigned m_phy_no; ///< Physical drive number
508};
509
510
2127e193
GI
511//////////////////////////////////////////////////////////////////////
512
513class win_tw_cli_device
514: public /*implements*/ ata_device_with_command_set
515{
516public:
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
525protected:
526 virtual int ata_command_interface(smart_command_set command, int select, char * data);
527
528private:
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
f4e463df
GI
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
548typedef 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
558typedef 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
564class win_areca_device
565: public /*implements*/ ata_device,
566 public /*extends*/ win_smart_device
567{
568public:
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
573protected:
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
580private:
581 int m_disknum; ///< Disk number.
582 int m_encnum; ///< Enclosure number.
583};
584
585
2127e193
GI
586//////////////////////////////////////////////////////////////////////
587// Platform specific interfaces
588
589// Common to all windows flavors
590class win_smart_interface
591: public /*implements part of*/ smart_interface
592{
593public:
54965743 594 virtual std::string get_os_version_str();
2127e193 595
54965743 596 virtual std::string get_app_examples(const char * appname);
2127e193 597
e165493d
GI
598#ifndef __CYGWIN__
599 virtual int64_t get_timer_usec();
600#endif
601
cfbba5b9
GI
602//virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
603// const char * pattern = 0);
2127e193
GI
604
605protected:
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);
2127e193
GI
611};
612
a23d5117
GI
613#if WIN9X_SUPPORT
614
2127e193
GI
615// Win9x/ME reduced functionality
616class win9x_smart_interface
617: public /*extends*/ win_smart_interface
618{
a23d5117
GI
619public:
620 win9x_smart_interface()
621 { win9x = true; }
622
cfbba5b9
GI
623 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
624 const char * pattern = 0);
625
2127e193
GI
626protected:
627 virtual scsi_device * get_scsi_device(const char * name, const char * type);
628
cfbba5b9
GI
629private:
630 bool ata_scan(smart_device_list & devlist);
2127e193 631
cfbba5b9 632 bool scsi_scan(smart_device_list & devlist);
2127e193
GI
633};
634
a23d5117
GI
635#endif // WIN9X_SUPPORT
636
2127e193
GI
637// WinNT,2000,XP,...
638class winnt_smart_interface
639: public /*extends*/ win_smart_interface
640{
cfbba5b9 641public:
d008864d
GI
642 virtual bool disable_system_auto_standby(bool disable);
643
cfbba5b9
GI
644 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
645 const char * pattern = 0);
646
2127e193
GI
647protected:
648 virtual scsi_device * get_scsi_device(const char * name, const char * type);
649
650 virtual smart_device * autodetect_smart_device(const char * name);
f4e463df
GI
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();
2127e193
GI
655};
656
657
658//////////////////////////////////////////////////////////////////////
659
7f0798ef 660#ifndef _WIN64
a37e7145
GG
661// Running on 64-bit Windows as 32-bit app ?
662static bool is_wow64()
663{
2127e193 664 BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
cfbba5b9
GI
665 (BOOL (WINAPI *)(HANDLE, PBOOL))
666 GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
2127e193
GI
667 if (!IsWow64Process_p)
668 return false;
669 BOOL w64 = FALSE;
670 if (!IsWow64Process_p(GetCurrentProcess(), &w64))
671 return false;
672 return !!w64;
a37e7145 673}
7f0798ef 674#endif // _WIN64
a37e7145 675
54965743
GI
676// Return info string about build host and OS version
677std::string win_smart_interface::get_os_version_str()
2127e193 678{
54965743 679 char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
2127e193
GI
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;
d008864d
GI
722 case VER_PLATFORM_WIN32_NT <<16|0x0600| 2:
723 w = (vi.wProductType == VER_NT_WORKSTATION ? "win8"
724 : "win8s"); break;
2127e193
GI
725 default: w = 0; break;
726 }
727
7f0798ef
GI
728 const char * w64 = "";
729#ifndef _WIN64
730 if (is_wow64())
731 w64 = "(64)";
732#endif
733
2127e193
GI
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}
ba59cff1 746
e165493d
GI
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)
752int64_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
2127e193
GI
772// Return value for device detection functions
773enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
832b75ed 774
2127e193 775static win_dev_type get_phy_drive_type(int drive);
cfbba5b9 776static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex);
2127e193
GI
777static win_dev_type get_log_drive_type(int drive);
778static bool get_usb_id(int drive, unsigned short & vendor_id,
779 unsigned short & product_id);
ba59cff1 780
2127e193 781static const char * ata_get_def_options(void);
ba59cff1 782
832b75ed
GG
783
784static int is_permissive()
785{
cfbba5b9 786 if (!failuretest_permissive) {
2127e193
GI
787 pout("To continue, add one or more '-T permissive' options.\n");
788 return 0;
789 }
cfbba5b9 790 failuretest_permissive--;
2127e193 791 return 1;
832b75ed
GG
792}
793
a37e7145
GG
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
797static int drive_letter(const char * s)
798{
2127e193
GI
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);
a37e7145
GG
804}
805
806// Skip trailing "/dev/", do not allow "/dev/X:"
832b75ed
GG
807static const char * skipdev(const char * s)
808{
2127e193
GI
809 return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
810}
811
812ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
813{
814 const char * testname = skipdev(name);
cfbba5b9
GI
815 if (!strncmp(testname, "csmi", 4))
816 return new win_csmi_device(this, name, type);
2127e193
GI
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
a23d5117
GI
822#ifdef WIN9X_SUPPORT
823
2127e193
GI
824scsi_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
a23d5117
GI
829#endif
830
2127e193
GI
831scsi_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))
a23d5117 835#if WIN9X_SUPPORT
2127e193 836 return new win_aspi_device(this, name, type);
a23d5117
GI
837#else
838 return (set_err(EINVAL, "ASPI interface not supported"), (scsi_device *)0);
839#endif
2127e193
GI
840 return new win_scsi_device(this, name, type);
841}
842
843static 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
872smart_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, "");
a23d5117 877#if WIN9X_SUPPORT
2127e193
GI
878 if (!strncmp(testname, "scsi", 4))
879 return new win_aspi_device(this, name, "");
a23d5117 880#endif
2127e193
GI
881 if (!strncmp(testname, "tw_cli", 6))
882 return new win_tw_cli_device(this, name, "");
883 return 0;
884}
885
f4e463df
GI
886
887smart_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
936std::string winnt_smart_interface::get_valid_custom_dev_types_str()
937{
938 return "areca,N[/E]";
939}
940
941
2127e193
GI
942smart_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
cfbba5b9
GI
948 if (!strncmp(skipdev(name), "csmi", 4))
949 return new win_csmi_device(this, name, "");
950
2127e193
GI
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
cfbba5b9
GI
978#if WIN9X_SUPPORT
979
980// Scan for devices on Win9x/ME
981
982bool win9x_smart_interface::scan_smart_devices(smart_device_list & devlist,
2127e193
GI
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
cfbba5b9
GI
1002#endif // WIN9X_SUPPORT
1003
1004
1005// Scan for devices
1006
1007bool 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
2127e193
GI
1128
1129// get examples for smartctl
54965743 1130std::string win_smart_interface::get_app_examples(const char * appname)
2127e193
GI
1131{
1132 if (strcmp(appname, "smartctl"))
54965743
GI
1133 return "";
1134 return "=================================================== SMARTCTL EXAMPLES =====\n\n"
832b75ed 1135 " smartctl -a /dev/hda (Prints all SMART information)\n\n"
832b75ed
GG
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"
7f0798ef 1141#if WIN9X_SUPPORT
832b75ed
GG
1142 " smartctl -a /dev/scsi21\n"
1143 " (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
7f0798ef 1144#endif
ba59cff1
GG
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"
4d59bff9
GG
1151 " smartctl -A /dev/hdb,3\n"
1152 " (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
9ebc753d
GG
1153 " smartctl -A /dev/tw_cli/c0/p1\n"
1154 " (Prints Attributes for 3ware controller 0, port 1 using tw_cli)\n"
f4e463df
GI
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"
4d59bff9
GG
1158 "\n"
1159 " ATA SMART access methods and ordering may be specified by modifiers\n"
a37e7145 1160 " following the device name: /dev/hdX:[saicm], where\n"
4d59bff9 1161 " 's': SMART_* IOCTLs, 'a': IOCTL_ATA_PASS_THROUGH,\n"
a37e7145 1162 " 'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH,\n"
1953ff6d 1163 " 'f': IOCTL_STORAGE_*, 'm': IOCTL_SCSI_MINIPORT_*.\n"
54965743 1164 + strprintf(
1953ff6d 1165 " The default on this system is /dev/sdX:%s\n", ata_get_def_options()
54965743 1166 );
832b75ed
GG
1167}
1168
1169
d008864d
GI
1170bool 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
832b75ed
GG
1192/////////////////////////////////////////////////////////////////////////////
1193// ATA Interface
1194/////////////////////////////////////////////////////////////////////////////
1195
832b75ed
GG
1196#define SMART_CYL_LOW 0x4F
1197#define SMART_CYL_HI 0xC2
1198
832b75ed
GG
1199static void print_ide_regs(const IDEREGS * r, int out)
1200{
2127e193
GI
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);
832b75ed
GG
1204}
1205
1206static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
1207{
2127e193
GI
1208 pout(" Input : "); print_ide_regs(ri, 0);
1209 if (ro) {
1210 pout(" Output: "); print_ide_regs(ro, 1);
1211 }
832b75ed
GG
1212}
1213
1214/////////////////////////////////////////////////////////////////////////////
1215
4d59bff9
GG
1216// call SMART_GET_VERSION, return device map or -1 on error
1217
a37e7145 1218static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
4d59bff9 1219{
7f0798ef 1220 GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
2127e193
GI
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)) {
cfbba5b9 1226 if (ata_debugmode)
2127e193
GI
1227 pout(" SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
1228 errno = ENOSYS;
1229 return -1;
1230 }
7f0798ef 1231 assert(num_out == sizeof(GETVERSIONINPARAMS));
2127e193 1232
cfbba5b9 1233 if (ata_debugmode > 1) {
2127e193
GI
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;
4d59bff9
GG
1248}
1249
1250
832b75ed
GG
1251// call SMART_* ioctl
1252
4d59bff9 1253static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
832b75ed 1254{
2127e193
GI
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();
cfbba5b9 1299 if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
2127e193
GI
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) {
cfbba5b9 1313 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1322 if (ata_debugmode > 1) {
2127e193
GI
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) {
a23d5117 1332 if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
cfbba5b9 1333 memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
2127e193 1334 else { // Workaround for driver not returning regs
cfbba5b9 1335 if (ata_debugmode)
2127e193
GI
1336 pout(" WARNING: driver does not return ATA registers in output buffer!\n");
1337 *regs = inpar.irDriveRegs;
1338 }
1339 }
1340
1341 return 0;
832b75ed
GG
1342}
1343
1344
1345/////////////////////////////////////////////////////////////////////////////
4d59bff9 1346// IDE PASS THROUGH (2000, XP, undocumented)
832b75ed
GG
1347//
1348// Based on WinATA.cpp, 2002 c't/Matthias Withopf
1349// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
1350
832b75ed 1351static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
2127e193
GI
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();
cfbba5b9 1375 if (ata_debugmode) {
2127e193
GI
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) {
cfbba5b9 1386 if (ata_debugmode) {
2127e193
GI
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))) {
cfbba5b9 1399 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1411 if (ata_debugmode > 1) {
2127e193
GI
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;
832b75ed
GG
1421}
1422
1423
4d59bff9 1424/////////////////////////////////////////////////////////////////////////////
4d59bff9
GG
1425// ATA PASS THROUGH (Win2003, XP SP2)
1426
2127e193
GI
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
1436static 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();
cfbba5b9 1496 if (ata_debugmode) {
2127e193
GI
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*/)) {
cfbba5b9 1506 if (ata_debugmode) {
2127e193
GI
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))) {
cfbba5b9 1518 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1528 if (ata_debugmode > 1) {
2127e193
GI
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;
4d59bff9
GG
1537}
1538
832b75ed
GG
1539
1540/////////////////////////////////////////////////////////////////////////////
4d59bff9 1541// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
832b75ed 1542
832b75ed
GG
1543// undocumented SCSI opcode to for ATA passthrough
1544#define SCSIOP_ATA_PASSTHROUGH 0xCC
1545
832b75ed
GG
1546static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
1547{
2127e193
GI
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();
cfbba5b9 1595 if (ata_debugmode)
2127e193
GI
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))) {
cfbba5b9 1607 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1617 if (ata_debugmode > 1) {
2127e193
GI
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;
832b75ed
GG
1622}
1623
ba59cff1
GG
1624
1625/////////////////////////////////////////////////////////////////////////////
a37e7145
GG
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)
ba59cff1 1632
a37e7145
GG
1633static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
1634{
2127e193
GI
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();
cfbba5b9 1716 if (ata_debugmode) {
2127e193
GI
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) {
cfbba5b9 1726 if (ata_debugmode) {
2127e193
GI
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) {
cfbba5b9 1735 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1744 if (ata_debugmode > 1) {
2127e193
GI
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)
cfbba5b9 1754 memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
2127e193
GI
1755
1756 return 0;
a37e7145
GG
1757}
1758
1759
ba59cff1
GG
1760/////////////////////////////////////////////////////////////////////////////
1761
a37e7145
GG
1762// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
1763
ba59cff1
GG
1764static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
1765{
2127e193
GI
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();
cfbba5b9 1791 if (ata_debugmode) {
2127e193
GI
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) {
cfbba5b9 1800 if (ata_debugmode) {
2127e193
GI
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
cfbba5b9 1812 if (ata_debugmode > 1) {
2127e193
GI
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;
ba59cff1
GG
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
1827static int update_3ware_devicemap_ioctl(HANDLE hdevice)
1828{
2127e193
GI
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();
cfbba5b9 1842 if (ata_debugmode)
2127e193
GI
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) {
cfbba5b9 1848 if (ata_debugmode)
2127e193
GI
1849 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
1850 errno = EIO;
1851 return -1;
1852 }
cfbba5b9 1853 if (ata_debugmode > 1)
2127e193
GI
1854 pout(" UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
1855 return 0;
ba59cff1
GG
1856}
1857
1858
9ebc753d
GG
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
1868static int get_clipboard(char * data, int datasize)
1869{
2127e193
GI
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;
9ebc753d
GG
1885}
1886
1887
1888// Run a command, write stdout to dataout
1889// TODO: Combine with daemon_win32.cpp:daemon_spawn()
1890
1891static int run_cmd(const char * cmd, char * dataout, int outsize)
1892{
2127e193
GI
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;
9ebc753d
GG
1942}
1943
1944
1945static const char * findstr(const char * str, const char * sub)
1946{
2127e193
GI
1947 const char * s = strstr(str, sub);
1948 return (s ? s+strlen(sub) : "");
9ebc753d
GG
1949}
1950
1951
1952static void copy_swapped(unsigned char * dest, const char * src, int destsize)
1953{
2127e193
GI
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
1966win_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
1975bool win_tw_cli_device::is_open() const
1976{
1977 return (m_ident_valid || m_smart_valid);
1978}
1979
1980
1981bool 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);
cfbba5b9 1998 if (ata_debugmode > 1)
2127e193
GI
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
cfbba5b9 2006 if (ata_debugmode > 1)
2127e193
GI
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;
cfbba5b9 2014 if (ata_debugmode > 1)
2127e193
GI
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 }
2127e193
GI
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:");
f4ebf3d1
GI
2036 if (!*s)
2037 s = findstr(buffer, "Drive SMART Data:"); // tw_cli from 9.5.x
2127e193
GI
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;
d008864d 2072 return set_err(EIO, "%s", err);
2127e193
GI
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
2085bool win_tw_cli_device::close()
2086{
2087 m_ident_valid = m_smart_valid = false;
2088 return true;
2089}
2090
2091
2092int 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;
2127e193
GI
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;
9ebc753d
GG
2115}
2116
2117
a37e7145 2118/////////////////////////////////////////////////////////////////////////////
a37e7145
GG
2119// IOCTL_STORAGE_QUERY_PROPERTY
2120
1953ff6d 2121union STORAGE_DEVICE_DESCRIPTOR_DATA {
2127e193
GI
2122 STORAGE_DEVICE_DESCRIPTOR desc;
2123 char raw[256];
1953ff6d 2124};
a37e7145 2125
1953ff6d
GG
2126// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
2127// (This works without admin rights)
2128
2129static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
a37e7145 2130{
2127e193
GI
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)) {
cfbba5b9 2137 if (ata_debugmode > 1 || scsi_debugmode > 1)
2127e193
GI
2138 pout(" IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
2139 errno = ENOSYS;
2140 return -1;
2141 }
2142
cfbba5b9 2143 if (ata_debugmode > 1 || scsi_debugmode > 1) {
2127e193
GI
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",
e9583e0c
GI
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)"),
2127e193
GI
2153 (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
2154 );
2155 }
2156 return 0;
1953ff6d
GG
2157}
2158
2159
2160/////////////////////////////////////////////////////////////////////////////
1953ff6d
GG
2161// IOCTL_STORAGE_PREDICT_FAILURE
2162
1953ff6d
GG
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
2167static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
2168{
2127e193
GI
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)) {
cfbba5b9 2175 if (ata_debugmode > 1)
2127e193
GI
2176 pout(" IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
2177 errno = ENOSYS;
2178 return -1;
2179 }
2180
cfbba5b9 2181 if (ata_debugmode > 1) {
2127e193
GI
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);
a37e7145
GG
2193}
2194
2195
2196/////////////////////////////////////////////////////////////////////////////
2197
d008864d
GI
2198// Return true if Intel ICHxR RAID volume
2199static 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
2127e193
GI
2211// get DEV_* for open handle
2212static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
a37e7145 2213{
2127e193
GI
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
7f0798ef 2219 // Newer BusType* values are missing in older includes
cfbba5b9 2220 switch ((int)data.desc.BusType) {
2127e193 2221 case BusTypeAta:
cfbba5b9 2222 case 0x0b: // BusTypeSata
2127e193
GI
2223 if (ata_version_ex)
2224 memset(ata_version_ex, 0, sizeof(*ata_version_ex));
2225 return DEV_ATA;
d008864d 2226
2127e193 2227 case BusTypeScsi:
d008864d
GI
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
cfbba5b9
GI
2237 case 0x09: // BusTypeiScsi
2238 case 0x0a: // BusTypeSas
2127e193 2239 return DEV_SCSI;
d008864d 2240
2127e193
GI
2241 case BusTypeUsb:
2242 return DEV_USB;
d008864d 2243
2127e193
GI
2244 default:
2245 return DEV_UNKNOWN;
2246 }
2247 /*NOTREACHED*/
a37e7145
GG
2248}
2249
2127e193
GI
2250// get DEV_* for device path
2251static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
a37e7145 2252{
2127e193
GI
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 }
cfbba5b9 2263 if (ata_debugmode > 1 || scsi_debugmode > 1)
2127e193
GI
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;
a37e7145
GG
2268}
2269
2127e193
GI
2270// get DEV_* for physical drive number
2271static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
a37e7145 2272{
2127e193
GI
2273 char path[30];
2274 snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
2275 return get_controller_type(path, ata_version_ex);
a37e7145
GG
2276}
2277
2127e193 2278static win_dev_type get_phy_drive_type(int drive)
a37e7145 2279{
2127e193 2280 return get_phy_drive_type(drive, 0);
a37e7145
GG
2281}
2282
2127e193
GI
2283// get DEV_* for logical drive number
2284static win_dev_type get_log_drive_type(int drive)
a37e7145 2285{
2127e193
GI
2286 char path[30];
2287 snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
2288 return get_controller_type(path);
a37e7145
GG
2289}
2290
1953ff6d
GG
2291// Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
2292static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
2293{
2127e193
GI
2294 STORAGE_DEVICE_DESCRIPTOR_DATA data;
2295 if (storage_query_property_ioctl(hdevice, &data))
2296 return -1;
2297
2298 memset(id, 0, sizeof(*id));
e9583e0c
GI
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--;
a7e8ffec
GI
2313 // Ignore VendorId "ATA"
2314 if (i <= 4 && !strncmp(model, "ATA", 3) && (i == 3 || model[3] == ' '))
2315 i = 0;
e9583e0c
GI
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
2127e193
GI
2325 if (data.desc.ProductRevisionOffset)
2326 copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
e9583e0c 2327
2127e193
GI
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
1953ff6d 2336
2127e193
GI
2337// Get USB ID for a physical drive number
2338static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
2339{
cfbba5b9
GI
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
2127e193 2349 // Get device name
cfbba5b9
GI
2350 wbem_object wo;
2351 if (!ws.query1(wo, "SELECT Model FROM Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\"", drive))
2127e193
GI
2352 return false;
2353
cfbba5b9
GI
2354 std::string name = wo.get_str("Model");
2355 if (debug)
2356 pout("PhysicalDrive%d, \"%s\":\n", drive, name.c_str());
2127e193
GI
2357
2358 // Get USB_CONTROLLER -> DEVICE associations
cfbba5b9
GI
2359 wbem_enumerator we;
2360 if (!ws.query(we, "SELECT Antecedent,Dependent FROM Win32_USBControllerDevice"))
2127e193
GI
2361 return false;
2362
d008864d
GI
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;
cfbba5b9 2366 std::string prev_ant, ant, dep;
2127e193 2367
cfbba5b9 2368 const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
2127e193 2369
cfbba5b9
GI
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());
2127e193 2384 continue;
cfbba5b9 2385 }
2127e193 2386
cfbba5b9 2387 std::string devid(dep.c_str()+match[1].rm_so, match[1].rm_eo-match[1].rm_so);
2127e193 2388
cfbba5b9
GI
2389 if (str_starts_with(devid, "USB\\\\VID_")) {
2390 // USB bridge entry, save CONTROLLER, ID
d008864d
GI
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 }
cfbba5b9 2396 prev_usb_ant = ant;
d008864d
GI
2397 if (debug)
2398 pout(" +-> \"%s\" [0x%04x:0x%04x]\n", devid.c_str(), prev_usb_venid, prev_usb_proid);
cfbba5b9
GI
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");
2127e193 2411
cfbba5b9
GI
2412 // Continue if not name of physical disk drive
2413 if (name2 != name) {
2414 if (debug)
d008864d 2415 pout(" +---> (\"%s\")\n", name2.c_str());
cfbba5b9
GI
2416 continue;
2417 }
cfbba5b9 2418
d008864d
GI
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());
cfbba5b9 2423 return false;
d008864d 2424 }
cfbba5b9
GI
2425
2426 // Handle multiple devices with same name
d008864d 2427 if (usb_venid) {
cfbba5b9 2428 // Fail if multiple devices with same name have different USB bridge types
d008864d
GI
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());
cfbba5b9 2432 return false;
d008864d 2433 }
cfbba5b9 2434 }
2127e193 2435
cfbba5b9 2436 // Found
d008864d
GI
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);
2127e193 2441
cfbba5b9
GI
2442 // Continue to check for duplicate names ...
2443 }
2444 else {
2445 if (debug)
2446 pout(" | \"%s\"\n", devid.c_str());
2447 }
2448 }
2127e193 2449
d008864d 2450 if (!usb_venid)
2127e193
GI
2451 return false;
2452
d008864d
GI
2453 vendor_id = usb_venid;
2454 product_id = usb_proid;
2455
2127e193 2456 return true;
1953ff6d
GG
2457}
2458
a37e7145 2459
4d59bff9
GG
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
2466static int get_device_power_state(HANDLE hdevice)
2467{
cfbba5b9
GI
2468 static bool unsupported = false;
2469 if (unsupported) {
2470 errno = ENOSYS;
2471 return -1;
2472 }
2473
4d59bff9 2474#ifdef __CYGWIN__
2127e193 2475 static DWORD kernel_dll_pid = 0;
4d59bff9 2476#endif
2127e193 2477 static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
4d59bff9 2478
2127e193 2479 if (!GetDevicePowerState_p
4d59bff9 2480#ifdef __CYGWIN__
2127e193 2481 || kernel_dll_pid != GetCurrentProcessId() // detect fork()
4d59bff9 2482#endif
2127e193 2483 ) {
2127e193 2484 if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *))
cfbba5b9
GI
2485 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDevicePowerState"))) {
2486 if (ata_debugmode)
2127e193 2487 pout(" GetDevicePowerState() not found, Error=%ld\n", GetLastError());
cfbba5b9 2488 unsupported = true;
2127e193
GI
2489 errno = ENOSYS;
2490 return -1;
2491 }
4d59bff9 2492#ifdef __CYGWIN__
2127e193 2493 kernel_dll_pid = GetCurrentProcessId();
4d59bff9 2494#endif
2127e193
GI
2495 }
2496
cfbba5b9 2497 BOOL state = TRUE;
2127e193
GI
2498 if (!GetDevicePowerState_p(hdevice, &state)) {
2499 long err = GetLastError();
cfbba5b9 2500 if (ata_debugmode)
2127e193
GI
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
cfbba5b9 2508 if (ata_debugmode > 1)
2127e193
GI
2509 pout(" GetDevicePowerState() succeeded, state=%d\n", state);
2510 return state;
4d59bff9
GG
2511}
2512
832b75ed
GG
2513
2514/////////////////////////////////////////////////////////////////////////////
2515
a23d5117 2516#if WIN9X_SUPPORT
832b75ed
GG
2517// Print SMARTVSD error message, return errno
2518
2519static int smartvsd_error()
2520{
2127e193
GI
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 }
832b75ed
GG
2562}
2563
a23d5117 2564#endif // WIN9X_SUPPORT
832b75ed 2565
4d59bff9
GG
2566// Get default ATA device options
2567
2568static const char * ata_get_def_options()
2569{
2127e193
GI
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_*
4d59bff9
GG
2578}
2579
2580
2127e193 2581// Common routines for devices with HANDLEs
4d59bff9 2582
2127e193
GI
2583win_smart_device::~win_smart_device() throw()
2584{
2585 if (m_fh != INVALID_HANDLE_VALUE)
2586 ::CloseHandle(m_fh);
832b75ed
GG
2587}
2588
2127e193
GI
2589bool win_smart_device::is_open() const
2590{
2591 return (m_fh != INVALID_HANDLE_VALUE);
2592}
832b75ed 2593
2127e193
GI
2594bool 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}
832b75ed 2602
2127e193 2603// ATA
832b75ed 2604
2127e193
GI
2605win_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),
a23d5117 2609 m_id_is_cached(false),
a7e8ffec 2610 m_is_3ware(false),
2127e193
GI
2611 m_drive(0),
2612 m_port(-1),
2613 m_smartver_state(0)
2614{
2615}
2616
2617win_ata_device::~win_ata_device() throw()
832b75ed 2618{
832b75ed
GG
2619}
2620
2621
2127e193
GI
2622// Open ATA device
2623
2624bool win_ata_device::open()
832b75ed 2625{
2127e193
GI
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);
832b75ed
GG
2653}
2654
2127e193
GI
2655
2656bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
832b75ed 2657{
2127e193 2658 // path depends on Windows Version
2127e193
GI
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();
a23d5117
GI
2688#if WIN9X_SUPPORT
2689 if (win9x && phydrive <= 3 && err == ERROR_FILE_NOT_FOUND)
2690 smartvsd_error();
2691#endif
2127e193 2692 if (err == ERROR_FILE_NOT_FOUND)
a23d5117 2693 set_err(ENOENT, "%s: not found", devpath);
2127e193
GI
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
a23d5117
GI
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
cfbba5b9 2711 if (ata_debugmode > 1)
2127e193
GI
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;
a7e8ffec
GI
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);
2127e193
GI
2740
2741 unsigned long portmap = 0;
2742 if (port >= 0 && devmap >= 0) {
2743 // 3ware RAID: check vendor id
a7e8ffec
GI
2744 if (!m_is_3ware) {
2745 pout("SMART_GET_VERSION returns unknown Identifier = 0x%04x\n"
2127e193
GI
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;
832b75ed
GG
2796}
2797
2127e193 2798
a23d5117
GI
2799#if WIN9X_SUPPORT
2800
2127e193
GI
2801// Scan for ATA drives on Win9x/ME
2802
2803bool win9x_smart_interface::ata_scan(smart_device_list & devlist)
832b75ed 2804{
2127e193
GI
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) {
cfbba5b9 2810 if (ata_debugmode > 1)
2127e193
GI
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);
bed94269 2828 devlist.push_back( new win_ata_device(this, name, "ata") );
2127e193
GI
2829 }
2830 return true;
832b75ed
GG
2831}
2832
a23d5117
GI
2833#endif // WIN9X_SUPPORT
2834
2127e193 2835
2127e193
GI
2836/////////////////////////////////////////////////////////////////////////////
2837
2838// Interface to ATA devices
2839bool 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
a7e8ffec
GI
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
2127e193
GI
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
a23d5117 2887 valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || win9x ?
2127e193
GI
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)
a23d5117 2896 valid_options = (m_usr_options || win9x ? "saicm3" : "aicm3");
2127e193
GI
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;
a23d5117 2998 bool id_is_cached = false;
2127e193
GI
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);
a7e8ffec
GI
3034 GETVERSIONINPARAMS_EX vers_ex;
3035 if (smart_get_version(get_fh(), &vers_ex) < 0) {
cfbba5b9 3036 if (!failuretest_permissive) {
2127e193
GI
3037 m_smartver_state = 2;
3038 rc = -1; errno = ENOSYS;
3039 break;
3040 }
cfbba5b9 3041 failuretest_permissive--;
2127e193 3042 }
a7e8ffec
GI
3043 else {
3044 // 3ware RAID if vendor id present
3045 m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
3046 }
3047
2127e193
GI
3048 m_smartver_state = 1;
3049 }
3050 rc = smart_ioctl(get_fh(), m_drive, &regs, data, datasize, m_port);
3051 out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
a23d5117 3052 id_is_cached = (m_port < 0 && !win9x); // Not cached by 3ware or Win9x/ME driver
2127e193
GI
3053 break;
3054 case 'm':
3055 rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
a23d5117 3056 id_is_cached = (m_port < 0 && !win9x);
2127e193
GI
3057 break;
3058 case 'a':
3059 rc = ata_pass_through_ioctl(get_fh(), &regs,
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(), &regs, data, datasize);
3066 out_regs_set = true;
3067 break;
3068 case 'c':
3069 rc = ata_via_scsi_pass_through_ioctl(get_fh(), &regs, 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);
a23d5117 3074 id_is_cached = true;
2127e193
GI
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;
2127e193
GI
3082 case ATA_SMART_ENABLE:
3083 rc = 0;
3084 break;
3085 case ATA_SMART_STATUS:
3086 rc = storage_predict_failure_ioctl(get_fh());
a23d5117
GI
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;
2127e193
GI
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(), &regs, 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 }
a23d5117
GI
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
2127e193
GI
3163 return true;
3164}
3165
3166// Return true if OS caches the ATA identify sector
3167bool win_ata_device::ata_identify_is_cached() const
3168{
a23d5117 3169 return m_id_is_cached;
4d59bff9
GG
3170}
3171
832b75ed 3172
cfbba5b9
GI
3173//////////////////////////////////////////////////////////////////////
3174// csmi_ata_device
3175
3176bool 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
3224bool 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
3247bool 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
3262bool 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
3361win_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
3368win_csmi_device::~win_csmi_device() throw()
3369{
3370 if (m_fh != INVALID_HANDLE_VALUE)
3371 CloseHandle(m_fh);
3372}
3373
3374bool win_csmi_device::is_open() const
3375{
3376 return (m_fh != INVALID_HANDLE_VALUE);
3377}
3378
3379bool 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
3389bool 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
3426bool 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
3441bool 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
832b75ed 3495/////////////////////////////////////////////////////////////////////////////
a23d5117 3496// ASPI Interface (for SCSI devices on 9x/ME)
832b75ed
GG
3497/////////////////////////////////////////////////////////////////////////////
3498
a23d5117
GI
3499#if WIN9X_SUPPORT
3500
832b75ed
GG
3501#pragma pack(1)
3502
3503#define ASPI_SENSE_SIZE 18
3504
3505// ASPI SCSI Request block header
3506
3507typedef struct {
2127e193
GI
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
832b75ed
GG
3513} ASPI_SRB_HEAD;
3514
3515// SRB for host adapter inquiry
3516
3517typedef struct {
2127e193
GI
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
832b75ed
GG
3524} ASPI_SRB_INQUIRY;
3525
3526// SRB for get device type
3527
3528typedef struct {
2127e193
GI
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
832b75ed
GG
3534} ASPI_SRB_DEVTYPE;
3535
3536// SRB for SCSI I/O
3537
3538typedef struct {
2127e193
GI
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];
832b75ed
GG
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
3559typedef union {
2127e193
GI
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
832b75ed
GG
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
3606static HINSTANCE h_aspi_dll; // DLL handle
3607static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
3608static unsigned num_aspi_adapters;
3609
3610#ifdef __CYGWIN__
3611// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
3612static 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
3619static int aspi_call(ASPI_SRB * srb)
3620{
2127e193
GI
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 }
cfbba5b9 3632 if (scsi_debugmode > 1)
2127e193
GI
3633 pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
3634 Sleep(100);
3635 }
3636 return 0;
832b75ed
GG
3637}
3638
3639
3640// Get ASPI entrypoint from wnaspi32.dll
3641
3642static FARPROC aspi_get_address(const char * name, int verbose)
3643{
2127e193
GI
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;
832b75ed
GG
3657}
3658
3659
3660static int aspi_open_dll(int verbose)
3661{
2127e193
GI
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 }
cfbba5b9 3693 if (scsi_debugmode > 1) {
2127e193
GI
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)();
cfbba5b9 3709 if (scsi_debugmode > 1)
2127e193
GI
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
cfbba5b9 3728 if (scsi_debugmode)
2127e193 3729 pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
832b75ed
GG
3730
3731#ifdef __CYGWIN__
2127e193
GI
3732 // save PID to detect fork() in aspi_entry_valid()
3733 aspi_dll_pid = GetCurrentProcessId();
832b75ed 3734#endif
2127e193
GI
3735 assert(aspi_entry_valid());
3736 return 0;
832b75ed
GG
3737}
3738
3739
3740static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
3741{
2127e193
GI
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",
7f0798ef 3761 (unsigned long)(ULONG_PTR)event, rc, rc, GetLastError());
2127e193
GI
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;
832b75ed
GG
3771}
3772
3773
2127e193
GI
3774win_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
3781bool win_aspi_device::is_open() const
3782{
3783 return (m_adapter >= 0);
3784}
3785
3786bool 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 }
cfbba5b9 3820 else if (scsi_debugmode)
2127e193
GI
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
3828bool 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
832b75ed 3836
2127e193
GI
3837bool win9x_smart_interface::scsi_scan(smart_device_list & devlist)
3838{
3839 if (!aspi_entry_valid()) {
cfbba5b9 3840 if (aspi_open_dll(scsi_debugmode/*default is quiet*/))
2127e193
GI
3841 return true;
3842 }
3843
3844 for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {
3845 ASPI_SRB srb;
3846
3847 if (ad > 9) {
cfbba5b9 3848 if (scsi_debugmode)
2127e193
GI
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) {
cfbba5b9 3861 if (scsi_debugmode)
2127e193
GI
3862 pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
3863 continue;
3864 }
3865
cfbba5b9 3866 if (scsi_debugmode) {
2127e193
GI
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) {
cfbba5b9 3883 if (scsi_debugmode > 1)
2127e193
GI
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*/) {
cfbba5b9 3889 if (scsi_debugmode)
2127e193
GI
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);
bed94269 3893 devlist.push_back( new win_aspi_device(this, name, "scsi") );
2127e193 3894 }
cfbba5b9 3895 else if (scsi_debugmode)
2127e193
GI
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
3904bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)
3905{
cfbba5b9 3906 int report = scsi_debugmode; // TODO
2127e193
GI
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");
d008864d 3946 pout("%s", buff);
2127e193
GI
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;
832b75ed 4028}
ba59cff1 4029
a23d5117 4030#endif // WIN9X_SUPPORT
ba59cff1
GG
4031
4032/////////////////////////////////////////////////////////////////////////////
4033// SPT Interface (for SCSI devices and ATA devices behind SATLs)
4034// Only supported in NT and later
4035/////////////////////////////////////////////////////////////////////////////
4036
2127e193
GI
4037win_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}
ba59cff1 4042
2127e193
GI
4043bool 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}
ba59cff1 4080
2127e193
GI
4081bool 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
ba59cff1 4109typedef struct {
2127e193
GI
4110 SCSI_PASS_THROUGH_DIRECT spt;
4111 ULONG Filler;
4112 UCHAR ucSenseBuf[64];
ba59cff1
GG
4113} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
4114
4115
2127e193
GI
4116// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
4117// Used if DataTransferLength not supported by *_DIRECT.
4118static 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
ba59cff1 4162// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
2127e193
GI
4163bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
4164{
cfbba5b9 4165 int report = scsi_debugmode; // TODO
2127e193
GI
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");
d008864d 4189 pout("%s", buff);
2127e193
GI
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
a23d5117 4217 // transfers (needed for SMART STATUS check of JMicron USB bridges)
2127e193
GI
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) {
a7e8ffec
GI
4257 if (report > 1) {
4258 pout(" >>> Sense buffer, len=%d:\n", slen);
4259 dStrHex(iop->sensep, slen , 1);
4260 }
2127e193
GI
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;
ba59cff1 4285}
2127e193 4286
f4e463df
GI
4287// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
4288static long scsi_pass_through_direct(HANDLE fd, 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 = 127;
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
4417static 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"
4487int 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, &io_hdr);
4556 if ( ioctlreturn || io_hdr.scsi_status )
4557 {
4558 // errors found
4559 break;
4560 }
4561
4562 if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER )
4563 {
4564 // if succeeded, just returns the length of outgoing data
4565 return data_len;
4566 }
4567
4568 if ( sBuf.srbioctl.Length )
4569 {
4570 //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
4571 memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
4572 ptr += sBuf.srbioctl.Length;
4573 total += sBuf.srbioctl.Length;
4574 // the returned bytes enough to compute payload length ?
4575 if ( expected < 0 && total >= 5 )
4576 {
4577 areca_return_packet = (unsigned char *)&return_buff[0];
4578 if ( areca_return_packet[0] == 0x5E &&
4579 areca_return_packet[1] == 0x01 &&
4580 areca_return_packet[2] == 0x61 )
4581 {
4582 // valid header, let's compute the returned payload length,
4583 // we expected the total length is
4584 // payload + 3 bytes header + 2 bytes length + 1 byte checksum
4585 expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6;
4586 }
4587 }
4588
4589 if ( total >= 7 && total >= expected )
4590 {
4591 //printf("total bytes received = %d, expected length = %d\n", total, expected);
4592
4593 // ------ Okay! we received enough --------
4594 break;
4595 }
4596 }
4597 }
4598
4599 // Deal with the different error cases
4600 if ( arcmsr_cmd == ARCMSR_IOCTL_RETURN_CODE_3F )
4601 {
4602 // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...)
4603 return -4;
4604 }
4605
4606 if ( ioctlreturn )
4607 {
4608 pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn);
4609 return -2;
4610 }
4611
4612 if ( io_hdr.scsi_status )
4613 {
4614 pout("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status);
4615 return -3;
4616 }
4617
4618 if ( data )
4619 {
4620 memcpy(data, return_buff, total);
4621 }
4622
4623 return total;
4624}
4625
4626
4627win_areca_device::win_areca_device(smart_interface * intf, const char * dev_name, HANDLE fh, int disknum, int encnum)
4628: smart_device(intf, dev_name, "areca", "areca"),
4629 m_disknum(disknum),
4630 m_encnum(encnum)
4631{
4632 set_fh(fh);
4633 set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
4634}
4635
4636bool win_areca_device::open()
4637{
4638 HANDLE hFh;
4639
4640 if( is_open() )
4641 {
4642 return true;
4643 }
4644
4645 hFh = CreateFile( get_dev_name(),
4646 GENERIC_READ|GENERIC_WRITE,
4647 FILE_SHARE_READ|FILE_SHARE_WRITE,
4648 NULL,
4649 OPEN_EXISTING,
4650 0,
4651 NULL );
4652 if(hFh == INVALID_HANDLE_VALUE)
4653 {
4654 return false;
4655 }
4656
4657 set_fh(hFh);
4658 return true;
4659}
4660
4661// Areca RAID Controller
4662bool win_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
4663{
4664 // ATA input registers
4665 typedef struct _ATA_INPUT_REGISTERS
4666 {
4667 unsigned char features;
4668 unsigned char sector_count;
4669 unsigned char sector_number;
4670 unsigned char cylinder_low;
4671 unsigned char cylinder_high;
4672 unsigned char device_head;
4673 unsigned char command;
4674 unsigned char reserved[8];
4675 unsigned char data[512]; // [in/out] buffer for outgoing/incoming data
4676 } sATA_INPUT_REGISTERS;
4677
4678 // ATA output registers
4679 // Note: The output registers is re-sorted for areca internal use only
4680 typedef struct _ATA_OUTPUT_REGISTERS
4681 {
4682 unsigned char error;
4683 unsigned char status;
4684 unsigned char sector_count;
4685 unsigned char sector_number;
4686 unsigned char cylinder_low;
4687 unsigned char cylinder_high;
4688 } sATA_OUTPUT_REGISTERS;
4689
4690 // Areca packet format for outgoing:
4691 // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
4692 // B[3~4] : 2 bytes command length + variant data length, little endian
4693 // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c
4694 // B[6~last-1] : variant bytes payload data
4695 // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
4696 //
4697 //
4698 // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte
4699 // +--------------------------------------------------------------------------------+
4700 // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 |
4701 // +--------------------------------------------------------------------------------+
4702 //
4703
4704 //Areca packet format for incoming:
4705 // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
4706 // B[3~4] : 2 bytes payload length, little endian
4707 // B[5~last-1] : variant bytes returned payload data
4708 // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
4709 //
4710 //
4711 // header 3 bytes length 2 bytes payload data x bytes cs 1 byte
4712 // +-------------------------------------------------------------------+
4713 // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 |
4714 // +-------------------------------------------------------------------+
4715 unsigned char areca_packet[640];
4716 int areca_packet_len = sizeof(areca_packet);
4717 unsigned char cs = 0;
4718
4719 sATA_INPUT_REGISTERS *ata_cmd;
4720
4721 // For debugging
4722#if 0
4723 memset(sInq, 0, sizeof(sInq));
4724 scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq));
4725 dumpdata((unsigned char *)sInq, sizeof(sInq));
4726#endif
4727 memset(areca_packet, 0, areca_packet_len);
4728
4729 // ----- BEGIN TO SETUP HEADERS -------
4730 areca_packet[0] = 0x5E;
4731 areca_packet[1] = 0x01;
4732 areca_packet[2] = 0x61;
4733 areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff);
4734 areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
4735 areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command
4736
4737 // ----- BEGIN TO SETUP PAYLOAD DATA -----
4738 memcpy(&areca_packet[7], "SmrT", 4); // areca defined password
4739 ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
4740
4741 // Set registers
4742 {
4743 const ata_in_regs & r = in.in_regs;
4744 ata_cmd->features = r.features;
4745 ata_cmd->sector_count = r.sector_count;
4746 ata_cmd->sector_number = r.lba_low;
4747 ata_cmd->cylinder_low = r.lba_mid;
4748 ata_cmd->cylinder_high = r.lba_high;
4749 ata_cmd->device_head = r.device;
4750 ata_cmd->command = r.command;
4751 }
4752 bool readdata = false;
4753 if (in.direction == ata_cmd_in::data_in) {
4754 readdata = true;
4755 // the command will read data
4756 areca_packet[6] = 0x13;
4757 }
4758 else if ( in.direction == ata_cmd_in::no_data )
4759 {
4760 // the commands will return no data
4761 areca_packet[6] = 0x15;
4762 }
4763 else if (in.direction == ata_cmd_in::data_out)
4764 {
4765 // the commands will write data
4766 memcpy(ata_cmd->data, in.buffer, in.size);
4767 areca_packet[6] = 0x14;
4768 }
4769 else {
4770 // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
4771 return set_err(ENOSYS);
4772 }
4773
4774 areca_packet[11] = m_disknum - 1; // disk#
4775 areca_packet[19] = m_encnum - 1; // enc#
4776
4777 // ----- BEGIN TO SETUP CHECKSUM -----
4778 for ( int loop = 3; loop < areca_packet_len - 1; loop++ )
4779 {
4780 cs += areca_packet[loop];
4781 }
4782 areca_packet[areca_packet_len-1] = cs;
4783
4784 // ----- BEGIN TO SEND TO ARECA DRIVER ------
4785 int expected = 0;
4786 unsigned char return_buff[2048];
4787 memset(return_buff, 0, sizeof(return_buff));
4788
4789 expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0);
4790 if (expected==-3) {
4791 return set_err(EIO);
4792 }
4793
4794 expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0);
4795 expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len);
4796 if ( expected > 0 )
4797 {
4798 expected = arcmsr_command_handler(get_fh(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff));
4799 }
4800 if ( expected < 0 )
4801 {
4802 return set_err(EIO);
4803 }
4804
4805 // ----- VERIFY THE CHECKSUM -----
4806 cs = 0;
4807 for ( int loop = 3; loop < expected - 1; loop++ )
4808 {
4809 cs += return_buff[loop];
4810 }
4811
4812 if ( return_buff[expected - 1] != cs )
4813 {
4814 return set_err(EIO);
4815 }
4816
4817 sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ;
4818 if ( ata_out->status )
4819 {
4820 if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
4821 && !nonempty((unsigned char *)in.buffer, in.size))
4822 {
4823 return set_err(ENODEV, "No drive on port %d", m_disknum);
4824 }
4825 }
4826
4827 // returns with data
4828 if (readdata)
4829 {
4830 memcpy(in.buffer, &return_buff[7], in.size);
4831 }
4832
4833 // Return register values
4834 {
4835 ata_out_regs & r = out.out_regs;
4836 r.error = ata_out->error;
4837 r.sector_count = ata_out->sector_count;
4838 r.lba_low = ata_out->sector_number;
4839 r.lba_mid = ata_out->cylinder_low;
4840 r.lba_high = ata_out->cylinder_high;
4841 r.status = ata_out->status;
4842 }
4843 return true;
4844}
4845
4846
4847bool win_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
4848{
4849#define SYNCOBJNAME "Global\\SynIoctlMutex"
4850 int ctlrnum = -1;
4851 char mutexstr[64];
4852 SECURITY_ATTRIBUTES sa;
4853 PSECURITY_DESCRIPTOR pSD;
4854 HANDLE hmutex;
4855
4856 if (!ata_cmd_is_ok(in,
4857 true, // data_out_support
4858 false, // TODO: multi_sector_support
4859 true) // ata_48bit_support
4860 )
4861 return false;
4862
4863 // Support 48-bit commands with zero high bytes
4864 if (in.in_regs.is_real_48bit_cmd())
4865 return set_err(ENOSYS, "48-bit ATA commands not fully supported by Areca");
4866
4867 if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
4868 return set_err(EINVAL, "unable to parse device name");
4869
4870 memset(mutexstr, 0, sizeof(mutexstr));
4871 sprintf(mutexstr, "%s%d",SYNCOBJNAME, ctlrnum);
4872 pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
4873 if ( !InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) )
4874 {
4875 LocalFree((HLOCAL)pSD);
4876 return set_err(EIO, "InitializeSecurityDescriptor failed");
4877 }
4878
4879 if ( !SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE) )
4880 {
4881 LocalFree((HLOCAL)pSD);
4882 return set_err(EIO, "SetSecurityDescriptor failed");
4883 }
4884
4885 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
4886 sa.lpSecurityDescriptor = pSD;
4887 sa.bInheritHandle = TRUE;
4888 hmutex = CreateMutex(&sa, FALSE, mutexstr);
4889 if ( hmutex == NULL )
4890 {
4891 LocalFree((HLOCAL)pSD);
4892 return set_err(EIO, "CreateMutex failed");
4893 }
4894
4895 // atomic access to driver
4896 WaitForSingleObject(hmutex, INFINITE);
4897 bool ok = arcmsr_ata_pass_through(in,out);
4898 ReleaseMutex(hmutex);
4899
4900 if(hmutex)
4901 {
4902 CloseHandle(hmutex);
4903 }
4904
4905 if ( (HLOCAL)pSD )
4906 {
4907 LocalFree((HLOCAL)pSD);
4908 }
4909
4910 return ok;
4911}
4912
2127e193
GI
4913
4914//////////////////////////////////////////////////////////////////////////////////////////////////
4915
4916
4917} // namespace
4918
4919/////////////////////////////////////////////////////////////////////////////
4920
4921// Initialize platform interface and register with smi()
4922void smart_interface::init()
4923{
cfbba5b9
GI
4924 {
4925 // Remove "." from DLL search path if supported
4926 // to prevent DLL preloading attacks
4927 BOOL (WINAPI * SetDllDirectoryA_p)(LPCSTR) = (BOOL (WINAPI *)(LPCSTR))
4928 GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetDllDirectoryA");
4929 if (SetDllDirectoryA_p)
4930 SetDllDirectoryA_p("");
4931 }
4932
2127e193 4933 // Select interface for Windows flavor
a23d5117
GI
4934 if (GetVersion() & 0x80000000) {
4935#if WIN9X_SUPPORT
2127e193
GI
4936 static os_win32::win9x_smart_interface the_win9x_interface;
4937 smart_interface::set(&the_win9x_interface);
a23d5117
GI
4938#else
4939 throw std::runtime_error("Win9x/ME not supported");
4940#endif
2127e193
GI
4941 }
4942 else {
4943 static os_win32::winnt_smart_interface the_winnt_interface;
4944 smart_interface::set(&the_winnt_interface);
4945 }
4946}
4947
e9583e0c
GI
4948
4949#ifndef __CYGWIN__
4950
4951// Get exe directory
4952// (prototype in utiliy.h)
4953std::string get_exe_dir()
4954{
4955 char path[MAX_PATH];
4956 // Get path of this exe
4957 if (!GetModuleFileNameA(GetModuleHandleA(0), path, sizeof(path)))
4958 throw std::runtime_error("GetModuleFileName() failed");
4959 // Replace backslash by slash
4960 int sl = -1;
4961 for (int i = 0; path[i]; i++)
4962 if (path[i] == '\\') {
4963 path[i] = '/'; sl = i;
4964 }
4965 // Remove filename
4966 if (sl >= 0)
4967 path[sl] = 0;
4968 return path;
4969}
4970
4971#endif