IRST 14.6以降で、NVMEデバイスに対してNVMEコマンドを直接発行するAPIです。

(Adminコマンドも通ります。)

検証用に作ったテストコードからの抜粋なのでそのままじゃコンパイル出来ません。

あくまでもAPI&構造体の使用例として察してください。

 

CSMINVM.H

//
// Intel Rapid Storage Technology : NVMe Pass-through Support
// for IRST 14.8 Later
// Revision 0.8 : NVMe Pass-Through API

#pragma once

#include “resource.h”

#include “nvme.h”

// IRST NVMe Passthrough IOCTL API Definitions

#define INTELNVM_SIGNATURE “IntelNvm”
#define NVME_PASS_THROUGH_VERSION 1
#define IOCTL_NVME_PASS_THROUGH CTL_CODE(0xF000, 0xA02, METHOD_BUFFERED, FILE_ANY_ACCESS)

#pragma pack(4)
struct NVME_PASS_THROUGH_PARAMETERS {
NVME_COMMAND Command;    //GENERIC_COMMAND Command;
BOOLEAN IsIOCommandSet;
NVME_COMPLETION_ENTRY Completion;    //COMPLETION_QUEUE_ENTRY Completion;
ULONG DataBufferOffset;
ULONG DataBufferLength;
ULONG Reserved[10];
};
#pragma pack()

#pragma pack(1)
struct NVME_IOCTL_PASS_THROUGH {
SRB_IO_CONTROL Header;
UCHAR Version;
UCHAR PathID;
UCHAR TargetID;
UCHAR Lun;
NVME_PASS_THROUGH_PARAMETERS Parameters;
};
#pragma pack()

RaidInterface.cpp

INT CRaidInterface::sendNvmCommand(int port, int pathid, BYTE cmd, BYTE param, PBYTE pResultBuffer, DWORD ResultBufferSize)
{
LOG(0, “IN”);

LOG(0, “Scsi Port = %d, PathID = %d”, port, pathid);
LOG(0, “Command = 0x%02x, Param = 0x%02x”, cmd, param);

INT iRc = 0;
BOOL bRc;

// Device Open
HANDLE m_hHandle = open(port);
if(m_hDevice == INVALID_HANDLE_VALUE)
{
LOG(0, “OUT – Failed open device handle”);
return    FALSE;
}
ASSERT(m_hDevice);

// First let’s allocate memory for buffer.To do so we must first calculate eventual offset with padding :
ULONG offset = ((sizeof(NVME_IOCTL_PASS_THROUGH) – 1) / sizeof(DWORD) + 1) * sizeof(DWORD);
ULONG ioctlBufferSize = offset + sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
BYTE* buf = (BYTE*)VirtualAlloc(NULL, ioctlBufferSize, MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(buf, ioctlBufferSize);

// Prepare header field :
SRB_IO_CONTROL &info = *reinterpret_cast<SRB_IO_CONTROL*>(buf);
info.HeaderLength = sizeof(SRB_IO_CONTROL);
info.ControlCode = IOCTL_NVME_PASS_THROUGH;
info.Length = ioctlBufferSize – sizeof(SRB_IO_CONTROL);
info.Timeout = 500;
memcpy_s(&info.Signature, sizeof(info.Signature), INTELNVM_SIGNATURE, sizeof(info.Signature));
info.ReturnCode = 0;

// Fill version and PathID :
NVME_IOCTL_PASS_THROUGH &ioctl = *reinterpret_cast<NVME_IOCTL_PASS_THROUGH*>(buf);
ioctl.Version = NVME_PASS_THROUGH_VERSION;
ioctl.PathID = pathid;
ioctl.TargetID = 0;
ioctl.Lun = 0;

// Fill Parameters field :
NVME_PASS_THROUGH_PARAMETERS &parameters = *reinterpret_cast<NVME_PASS_THROUGH_PARAMETERS*>(&ioctl.Parameters);
parameters.Command.CDW0.OPC = cmd;
parameters.Command.CDW0.FUSE = NVME_FUSED_OPERATION_NORMAL;
parameters.Command.NSID = 0;    // NVME_NAMESPACE_ALL;;
parameters.Command.u.IDENTIFY.CDW10.AsUlong = NVME_IDENTIFY_CNS_CONTROLLER;
parameters.DataBufferLength = sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
parameters.DataBufferOffset = offset;

LOG(1, “Ioctl Buffer Length = 0x%04x”, ioctlBufferSize);
LOG(1, “SRB_IO_CONTROL.HeaderLength = 0x%04x”, info.HeaderLength);
LOG(1, “SRB_IO_CONTROL.Length= 0x%04x”, info.Length);
LOG(1, “Parameters.DataBufferLength = 0x%04x”, parameters.DataBufferLength);
LOG(1, “Parameters.DataBufferOffset = 0x%04x”, parameters.DataBufferOffset);

//LOG(1, “SRB_IO_CONTROL Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Header));
//LOG(1, “NVME.Version Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Version));
//LOG(1, “NVME.PathID Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, PathID));
//LOG(1, “NVME.TargetID Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, TargetID));
//LOG(1, “NVME.Param Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters));
//LOG(1, “NVME.Param.Command Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, Command));
//LOG(1, “NVME.Param.IsIOCommandSetOffset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, IsIOCommandSet));
//LOG(1, “NVME.Param.Completion Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, Completion));
//LOG(1, “NVME.Param.DataBuffOffset Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, DataBufferOffset));
//LOG(1, “NVME.Param.DataBuffLengtht Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, DataBufferLength));
//LOG(1, “sizeof(NVME_COMMAND) = 0x%04x”, sizeof(NVME_COMMAND));

LOG(1, “Request/Parameters Buffer”);
Logger::BinaryDump((BYTE*)&info, sizeof(NVME_IOCTL_PASS_THROUGH));

bRc = sendCsmiCommand(m_hDevice, IOCTL_NVME_PASS_THROUGH, (SRB_IO_CONTROL*)buf, ioctlBufferSize);
LOG(0, “send(CSMI NVME PASS THROUGH) rc=%d”, bRc);

LOG(1, “Buffer Dump after Command Exec”);
Logger::BinaryDump((BYTE*)buf, ioctlBufferSize);

NVME_IDENTIFY_CONTROLLER_DATA DataBuffer = *reinterpret_cast<NVME_IDENTIFY_CONTROLLER_DATA*>(buf + parameters.DataBufferOffset);
LOG(1, “Buffer Address = %08p\nResult Adrress = %08p”, &buf, &DataBuffer);

if (bRc == 1)
{
// IDENTIFY以外のコマンドに対応する場合は分岐が必要
//if(cmd != ATA_SET_FEATURES && pResultBuffer != NULL)
{
LOG(1, “Copy NVME IDENTIFY DATA”);
memcpy_s(pResultBuffer, ResultBufferSize, &DataBuffer, ResultBufferSize);

LOG(1, “Result CSMI_NVME_PASS_THROUGH”);
Logger::BinaryDump((BYTE*)pResultBuffer, ResultBufferSize);
}
}
else
{
long err = GetLastError();
LOG(0, “sendCsmiCommand(CSMI_NVME_PASS_THROUGH) return code = %d”, err);
LOG(0, “buf->IoctlHeader.ReturnCode = %d”, info.ReturnCode);
if (info.ReturnCode != 0)
iRc = info.ReturnCode;
else
iRc = -2;
}
VirtualFree(buf, 0, MEM_RELEASE);
close();

return    iRc;
}

INT CRaidInterface::sendNvmCommand(CRaidDrive* p, BYTE cmd, BYTE param, PBYTE pInBuffer, DWORD BufferSize)
{
if (p != NULL && pInBuffer != NULL && BufferSize >= sizeof(SRB_IO_CONTROL))
return sendNvmCommand(p->m_ScsiPort, p->m_PortID, cmd, param, pInBuffer, BufferSize);
return -2;
}

SAMPLE CODE:

PBYTE cBuffer2 = (PBYTE)VirtualAlloc(NULL, sizeof(NVME_IDENTIFY_CONTROLLER_DATA), MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));

LOG(0, “NVME ADMIN COMMAND”);
iRc = raidInterface.sendNvmCommand(raidDrive, NVME_ADMIN_COMMAND_IDENTIFY, 0x00, (PBYTE)cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));
//if (iRc == 0)
{
// 取得したドライブ情報を保存
Logger::BinaryDump((BYTE*)cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));
CNVMeParser::parseID((BYTE*)cBuffer2, raidDrive->m_driveID); // PARSE

raidDrive->m_driveID.m_Protocol = PROTOCOL_TYPE_CSMI_NVM;
raidDrive->m_Environment = 3;    // NVMe(IRST)
raidDrive->m_BusType = 0x11;    // NVMe
//raidDrive->m_DeviceType = 0;
//raidDrive->m_ScsiPortProtocol = CSMI_SAS_PROTOCOL_SATA;
}
m_raidDrivesList.Add(raidDrive);

VirtualFree(cBuffer2, 0, MEM_RELEASE);