Intel NVMe Command Sample (C++)

This article only has code sample. If you need explanation, see this article.

#include <string>
#include <windows.h>
#include <Ntddscsi.h>
#include <tchar.h>

const TCHAR* IntelTestPath = _T("\\\\.\\PhysicalDrive5");

#define NVME_STORPORT_DRIVER 0xE000
#define NVME_PASS_THROUGH_SRB_IO_CODE \
    CTL_CODE( NVME_STORPORT_DRIVER, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define NVME_SIG_STR "NvmeMini"
#define NVME_SIG_STR_LEN 8
#define NVME_FROM_DEV_TO_HOST 2
#define NVME_IOCTL_VENDOR_SPECIFIC_DW_SIZE 6
#define NVME_IOCTL_CMD_DW_SIZE 16
#define NVME_IOCTL_COMPLETE_DW_SIZE 4
#define NVME_PT_TIMEOUT 40

struct NVME_PASS_THROUGH_IOCTL{
    SRB_IO_CONTROL SrbIoCtrl;
    DWORD          VendorSpecific[NVME_IOCTL_VENDOR_SPECIFIC_DW_SIZE];
    DWORD          NVMeCmd[NVME_IOCTL_CMD_DW_SIZE];
    DWORD          CplEntry[NVME_IOCTL_COMPLETE_DW_SIZE];
    DWORD          Direction;
    DWORD          QueueId;
    DWORD          DataBufferLen;
    DWORD          MetaDataLen;
    DWORD          ReturnBufferLen;
    UCHAR          DataBuffer[4096];
};

std::wstring GetSCSIPath(const TCHAR* Path) {
    HANDLE hIoCtrl = CreateFile(Path, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    SCSI_ADDRESS sadr;
    BOOL bRet = 0;
    DWORD dwReturned;

    bRet = DeviceIoControl(hIoCtrl, IOCTL_SCSI_GET_ADDRESS,
        NULL, 0, &sadr, sizeof(sadr), &dwReturned, NULL);

    std::wstring result = _T("\\\\.\\SCSI");
    wchar_t PortNumberStr[4];
    _itow_s(sadr.PortNumber, PortNumberStr, 10);
    result.append(PortNumberStr);
    result.append(_T(":"));
    return result;
}

void IntelNVMeIdentify(const TCHAR* Path) {
    HANDLE hIoCtrl = CreateFile(&GetSCSIPath(Path)[0], GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    BOOL bRet = 0;
    NVME_PASS_THROUGH_IOCTL nptwb;
    DWORD length = sizeof(nptwb);
    DWORD dwReturned;

    ZeroMemory(&nptwb, sizeof(NVME_PASS_THROUGH_IOCTL));

    nptwb.SrbIoCtrl.ControlCode = NVME_PASS_THROUGH_SRB_IO_CODE;
    nptwb.SrbIoCtrl.HeaderLength = sizeof(SRB_IO_CONTROL);
    memcpy((UCHAR*)(&nptwb.SrbIoCtrl.Signature[0]), NVME_SIG_STR, NVME_SIG_STR_LEN);
    nptwb.SrbIoCtrl.Timeout = NVME_PT_TIMEOUT;
    nptwb.SrbIoCtrl.Length = length - sizeof(SRB_IO_CONTROL);
    nptwb.DataBufferLen = sizeof(nptwb.DataBuffer);
    nptwb.ReturnBufferLen = sizeof(nptwb);
    nptwb.Direction = NVME_FROM_DEV_TO_HOST;

    nptwb.NVMeCmd[0] = 6;  // Identify
    nptwb.NVMeCmd[10] = 1; // Return to Host

    bRet = DeviceIoControl(hIoCtrl, IOCTL_SCSI_MINIPORT,
        &nptwb, length, &nptwb, length, &dwReturned, NULL);                                 
}

int main() {
    IntelNVMeIdentify(IntelTestPath);
}

Leave a Reply

Your email address will not be published. Required fields are marked *