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