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