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