DEVICE ID的用途和读取方法
1 用途概述
DEVICE ID可唯一标识一个存储设备,这对于多盘掉电等功能非常重要,因为无论是判定一个盘是否已掉盘还是已上盘都必须知道是哪一个盘。利用这个唯一标识,可以做的事情就很多,包括定位。
一个典型的USB设备的DEVICE ID格式如下:
一个典型的SATA设备的DEVICE ID 格式如下:
2 读取的方法
// const std::string CUSBPort::GetDeviceID(U32 _phyDevNO, std::string& _strHubID) { DEVINST devInst = GetDeviceInst(_phyDevNO); if (0 == devInst) { return ""; }
char szBuff[512] = {0}; CONFIGRET cr = CM_Get_Device_ID(devInst, szBuff, 512, 0); if (cr != CR_SUCCESS) { return ""; }
// 再¨´向¨°上¦?查¨¦找¨°一°?级?即¡ä为aHUBID DEVINST hubDevInst = 0; cr = CM_Get_Parent(&hubDevInst, devInst, 0); if (cr != CR_SUCCESS) { return ""; }
char szHubBuff[512] = {0}; cr = CM_Get_Device_ID(hubDevInst, szHubBuff, 512, 0); if (cr != CR_SUCCESS) { return ""; } _strHubID = szHubBuff;
return std::string(szBuff); }
|
U32 CUSBPort::GetDeviceInst(U32 _phyDevNO) { // Get device interface info set handle // for all devices attached to system GUID* pGUID = (GUID*)&GUID_DEVINTERFACE_DISK; HDEVINFO hDevInfo = SetupDiGetClassDevs(pGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if ( hDevInfo == INVALID_HANDLE_VALUE ) { return 0; } // Retrieve a context structure for a device interface // of a device information set. U32 dwIndex = 0; BOOL bRet = FALSE; BYTE bBuff[1024] = {0}; PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)bBuff; SP_DEVICE_INTERFACE_DATA spdid = {0}; SP_DEVINFO_DATA spdd = {0}; U32 dwSize = 0; spdid.cbSize = sizeof(spdid);
while (TRUE, TRUE) { bRet = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, pGUID, dwIndex, &spdid); if (!bRet) { break; }
dwSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL);
if (dwSize!=0 && dwSize<=sizeof(bBuff)) { pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory((PVOID)&spdd, sizeof(spdd)); spdd.cbSize = sizeof(spdd);
bRet = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd); if (bRet) { //TRACE("DevicePath: %s \n", pspdidd->DevicePath); HANDLE hDrive = CreateFile(pspdidd->DevicePath,0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if ( hDrive != INVALID_HANDLE_VALUE ) { STORAGE_DEVICE_NUMBER sdn; U32 dwBytesReturned = 0; bRet = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL); if (bRet) { if (_phyDevNO == sdn.DeviceNumber) { CloseHandle(hDrive); SetupDiDestroyDeviceInfoList(hDevInfo); return spdd.DevInst; } } CloseHandle(hDrive); } } } dwIndex++; } SetupDiDestroyDeviceInfoList(hDevInfo); return 0; }
|
3 更多方法
实际上对于SATA盘而言,我们还可以通过SATA端口号来唯一标识一个盘。什么是端口号,可以从intel快速存储技术软件界面中看到例子:
这个端口号也是唯一不变的,在BUSHOUND中也可以找到,只不过其名字不一样:
其读取方法如下:
BOOL CDiskInfo::GetTargetID(int hDriver, //驱y动¡¥器¡Â句?柄À¨² PVOID pCBD, //CBD指?令¢?码? DWORD CBDLen, //CBD指?令¢?码?的Ì?长¡è度¨¨ DWORD DataTransferLength , //数ºy据Y传ä?送¨ª长¡è度¨¨ PVOID pBuff , //数ºy据Y指?针?,ê?无T数ºy据Y为aNULL BYTE DataIn , //数ºy据Y传ä?输º?方¤?向¨° SCSI_IOCTL_DATA_OUT OR SCSI_IOCTL_DATA_IN //数ºy据Y由®¨¦设¦¨¨备À?传ä?输º?到Ì?程¨¬序¨°,ê?为aSCSI_IOCTL_DATA_IN PDWORD RetLength //实º¦Ì际¨º返¤¦Ì回?数ºy据Y的Ì?长¡è度¨¨;ê? ) { USES_CONVERSION; char sFilePath[64]={0}; sprintf(sFilePath,"\\\\.\\PHYSICALDRIVE%d",hDriver);
HANDLE hFile = INVALID_HANDLE_VALUE; hFile = ::CreateFile(sFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) return -1; DWORD RetLen; PDWORD pRetLen = NULL; if (NULL == RetLength) { pRetLen = &RetLen; } else { pRetLen = RetLength; } ULONG length = 0;//returned = 0; SCSI_PASS_THROUGH_WITH_BUFFERS sptwb; ZeroMemory(&sptwb,sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS)); sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH); sptwb.spt.PathId = 0; sptwb.spt.TargetId = 0; sptwb.spt.Lun = 0; sptwb.spt.SenseInfoLength = 24; sptwb.spt.TimeOutValue = 10; sptwb.spt.CdbLength = (BYTE)CBDLen; sptwb.spt.DataIn = DataIn; sptwb.spt.DataTransferLength= DataTransferLength; memcpy(sptwb.spt.Cdb, pCBD, CBDLen);
if ((SCSI_IOCTL_DATA_OUT == DataIn) && (NULL != pBuff)) { memcpy(sptwb.ucDataBuf , pBuff, DataTransferLength); }
sptwb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); sptwb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf); length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) + sptwb.spt.DataTransferLength; bool status = DeviceIoControl( hFile, IOCTL_SCSI_PASS_THROUGH, &sptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS), &sptwb, length, pRetLen, NULL);
return sptwb.spt.TargetId; }
|
但在实际操作中并不使用此种方法,因为此方法带有明显的协议性质,USB中并没有这个端口号,PCIE中可能更加没有,而DEVICE ID则不管什么磁盘都有,且目前发现USB连接的设备和SATA设备都可以用相同的函数获取,与协议无关,PCIE应该也可以通过此方法获取DEVICE ID(条件限制还未尝试)。CBD[0] = 0x12是获取TargetID的唯一需要指定的参数信息。
另外切忌,万不得已的情况下对于SSD盘还可以通过序列号&型号名来唯一标示一个盘,序列号和型号名如下图所示:
但鉴于这两个信息量产时可以任意指定,因此可能会重复,如果两个盘同时进行测试就无法区分。但鉴于重复的概率很小,也不失为一种标识磁盘的备选方式。