Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

724 lines
25 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
devenum.c
Abstract:
Code for enum IDE ans SCSI controllers and attached to them storage devices
and calculate for them SCSI Address.
Author:
Souren Aghajanyan (sourenag) 05-June-2001
Revision History:
--*/
#include "precomp.h"
#include "devenum.h"
typedef struct tagIDEController
{
PCTSTR pnpId;
UINT defaultSCSIPort;
}IDE_CONTROLLER, *PIDE_CONTROLLER;
#define DEVICE_CURRENT_DRIVE_LETTER_TEXT_ASSIGNMENT TEXT("CurrentDriveLetterAssignment")
IDE_CONTROLLER g_knownIDEControllers[] =
{
{TEXT("MF\\GOODPRIMARY"), 0},
{TEXT("MF\\GOODSECONDARY"), 1},
{TEXT("*PNP0600"), 1}
};
PCTSTR
pRegQueryStringValue(
IN HKEY hKey,
IN PCTSTR ValueName,
OUT PVOID Buffer,
IN UINT BufferSize
)
{
static TCHAR defaultBuffer[MAX_REG_SIZE];
DWORD valueType;
MYASSERT((hKey && ValueName) && ((Buffer && BufferSize) || (!Buffer)));
if(!Buffer){
Buffer = (PVOID)defaultBuffer;
BufferSize = sizeof(defaultBuffer);
}
if(ERROR_SUCCESS != RegQueryValueEx(hKey,
ValueName,
0,
&valueType,
(PBYTE)Buffer,
(PULONG)&BufferSize) ||
REG_SZ != valueType){
return NULL;
}
return (PCTSTR)Buffer;
}
BOOL
pDoesDriveExist(
IN HKEY hDevice,
OUT DWORD* DriveType
)
{
DWORD driveType;
PCTSTR pBufferKeyValue;
TCHAR drivePath[] = TEXT("?:\\");
BOOL bCDROMDevice = TRUE;
if(!hDevice){
return FALSE;
}
pBufferKeyValue = pRegQueryStringValue(hDevice, TEXT("Class"), NULL, 0);
if(!pBufferKeyValue){
return FALSE;
}
bCDROMDevice = !_tcsicmp(pBufferKeyValue, TEXT("CDROM"));
pBufferKeyValue = pRegQueryStringValue(hDevice, DEVICE_CURRENT_DRIVE_LETTER_TEXT_ASSIGNMENT, NULL, 0);
if(!pBufferKeyValue){
return FALSE;
}
drivePath[0] = pBufferKeyValue[0];
driveType = GetDriveType(drivePath);
if(DriveType){
*DriveType = driveType;
}
return bCDROMDevice? (DRIVE_CDROM == driveType):
(DRIVE_NO_ROOT_DIR != driveType && DRIVE_UNKNOWN != driveType);
}
BOOL
pGetDeviceType(
IN HKEY hDevice,
OUT DWORD* DriveType
)
{
if(!DriveType){
return FALSE;
}
return pDoesDriveExist(hDevice, DriveType);
}
VOID
pPreparePNPIDName(
IN PTSTR deviceInfoRegKey
)
{
MYASSERT(deviceInfoRegKey);
//
// Replace '\\' with '&' in registry key to make PNPID
//
while(deviceInfoRegKey = _tcschr(deviceInfoRegKey, '\\')){
*deviceInfoRegKey = '&';
}
}
int __cdecl
pControllerInfoCompare(
IN const void * elem1,
IN const void * elem2
)
{
MYASSERT(elem1 && elem2);
//
// Sort controlers in next order: First IDE, after SCSI,
// inside each group(IDE and SCSI) sort by preliminary defined SCSIPortNumber
//
#define PCONTROLLER_INFO_CAST(x) ((PCONTROLLER_INFO)x)
if(PCONTROLLER_INFO_CAST(elem1)->ControllerType > PCONTROLLER_INFO_CAST(elem2)->ControllerType){
return 1;
}
if(PCONTROLLER_INFO_CAST(elem1)->ControllerType < PCONTROLLER_INFO_CAST(elem2)->ControllerType){
return -1;
}
if(PCONTROLLER_INFO_CAST(elem1)->SCSIPortNumber > PCONTROLLER_INFO_CAST(elem2)->SCSIPortNumber){
return 1;
}
if(PCONTROLLER_INFO_CAST(elem1)->SCSIPortNumber < PCONTROLLER_INFO_CAST(elem2)->SCSIPortNumber){
return -1;
}
MYASSERT(INVALID_SCSI_PORT == PCONTROLLER_INFO_CAST(elem1)->SCSIPortNumber);
return 0;
}
BOOL
pGatherControllersInfo(
IN OUT PCONTROLLER_INFO ActiveControllersOut,
IN OUT PUINT NumberOfActiveControllersOut
)
{
TCHAR regkeyName[MAX_REG_SIZE];
TCHAR deviceInfoRegKey[MAX_REG_SIZE];
TCHAR deviceData[MAX_REG_SIZE];
TCHAR ideHardwareID[MAX_PNPID_SIZE];
HKEY hActiveDevicesRoot = NULL;
HKEY hActiveDeviceRoot = NULL;
HKEY hDevice = NULL;
UINT itemIndexRoot;
DWORD bufferLength;
PTSTR pDelimeter;
UINT indexAvailable = 0;
UINT scsiPortNumber;
UINT controllerStartIndex = 0;
UINT controllersSubNumber;
PCONTROLLER_INFO controllerInfo;
UINT i;
UINT j;
CONTROLLER_TYPE deviceType;
BOOL bROOTDevice;
UINT ideCounter;
DWORD rcResult;
static CONTROLLER_TYPE controllerTypes[] = {CONTROLLER_ON_BOARD_IDE, CONTROLLER_EXTRA_IDE, CONTROLLER_SCSI};
if(!NumberOfActiveControllersOut){
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
__try{
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_DYN_DATA, TEXT("Config Manager\\Enum"), 0, KEY_READ, &hActiveDevicesRoot)){
return FALSE;
}
//
// Looking for IDE and SCSI controllers in list of active hardware
// under "HKDD\Config Manager\Enum"
//
for(itemIndexRoot = 0; ;itemIndexRoot++){
bufferLength = ARRAYSIZE(regkeyName);
rcResult = RegEnumKeyEx(hActiveDevicesRoot,
itemIndexRoot,
regkeyName,
&bufferLength,
0,
NULL,
NULL,
NULL);
if(ERROR_SUCCESS != rcResult){
break;
}
if(ERROR_SUCCESS != RegOpenKeyEx(hActiveDevicesRoot, regkeyName, 0, KEY_READ, &hActiveDeviceRoot)){
continue;
}
do{
//
// "HardWareKey" consist key path to real device
//
if(pRegQueryStringValue(hActiveDeviceRoot,
TEXT("HardWareKey"),
regkeyName,
sizeof(regkeyName))){
if(!_tcsnicmp(regkeyName, TEXT("ROOT"), 4)){
//
// Sometime on board IDE controllers has preserved PNPID under ROOT,
// and is not represented in MF\CHILD000x.
//
bROOTDevice = TRUE;
deviceType = CONTROLLER_ON_BOARD_IDE;
}else
{
if(!_tcsnicmp(regkeyName, TEXT("MF\\CHILD"), 8)){
deviceType = CONTROLLER_ON_BOARD_IDE;
}else if(!_tcsnicmp(regkeyName, TEXT("PCI"), 3)){
deviceType = CONTROLLER_SCSI;
}else{
//deviceType = CONTROLLER_UNKNOWN;
break;
}
bROOTDevice = FALSE;
}
_tcscpy(deviceInfoRegKey, TEXT("Enum\\"));
_tcscat(deviceInfoRegKey, regkeyName);
//
// Open reg key where resides all device infomation
//
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, deviceInfoRegKey, 0, KEY_READ, &hDevice)){
controllerInfo = ActiveControllersOut + indexAvailable;
//
// Replace '\\' with '&' in registry key to make PNPID
//
pPreparePNPIDName(regkeyName);
switch(deviceType){
case CONTROLLER_ON_BOARD_IDE:
{
if(pRegQueryStringValue(hDevice,
TEXT("HardwareID"),
ideHardwareID,
sizeof(deviceData))){
scsiPortNumber = INVALID_SCSI_PORT;
//
// "MF\\GOODPRIMARY" and "MF\\GOODSECONDARY" are pnpid for
// on board IDE Primary and Secondary Channel controllers.
// And they always has constant SCSIPortNumber 0 or 1 respectively
// for NT enum and marked as CONTROLLER_ON_BOARD_IDE.
// Leave INVALID_SCSI_PORT(SCSIPortNumber) for extra IDE controllers
// and mark them as CONTROLLER_EXTRA_IDE.
//
for(ideCounter = 0; ideCounter < ARRAYSIZE(g_knownIDEControllers); ideCounter++){
if(!_tcsnicmp(ideHardwareID,
g_knownIDEControllers[ideCounter].pnpId,
_tcslen(g_knownIDEControllers[ideCounter].pnpId))){
scsiPortNumber = g_knownIDEControllers[ideCounter].defaultSCSIPort;
break;
}
}
if(bROOTDevice && INVALID_SCSI_PORT == scsiPortNumber){
//
// Ignore this case, devices is not IDE controller.
//
break;
}
if(ActiveControllersOut){
MYASSERT(controllerInfo->SCSIPortNumber == INVALID_SCSI_PORT);
_tcscpy(controllerInfo->PNPID, regkeyName);
controllerInfo->SCSIPortNumber = scsiPortNumber;
controllerInfo->ControllerType = scsiPortNumber != INVALID_SCSI_PORT?
CONTROLLER_ON_BOARD_IDE: CONTROLLER_EXTRA_IDE;
}
indexAvailable++;
}
}
break;
case CONTROLLER_SCSI:
{
//
// For SCSI controllers SCSIPortNumber calaculated from
// "Driver" value and have "SCSIAdapter\000x" where x
// is SCSIPortNumber. For SCSI controllers SCSIPortNumber
// will be postprocessed after enum.
// Mark as CONTROLLER_SCSI.
//
if(pRegQueryStringValue(hDevice,
TEXT("Driver"),
deviceData,
sizeof(deviceData))){
pDelimeter = _tcschr(deviceData, '\\');
if(pDelimeter){
*pDelimeter = '\0';
if(!_tcsicmp(deviceData, TEXT("SCSIAdapter"))){
scsiPortNumber = _ttoi(++pDelimeter);
if(ActiveControllersOut){
MYASSERT(controllerInfo->SCSIPortNumber == INVALID_SCSI_PORT);
_tcscpy(controllerInfo->PNPID, regkeyName);
controllerInfo->SCSIPortNumber = scsiPortNumber;
controllerInfo->ControllerType = CONTROLLER_SCSI;
}
indexAvailable++;
}
}
}
}
break;
default:
MYASSERT(FALSE);
}
RegCloseKey(hDevice);hDevice = NULL;
}
}
}while(FALSE);
RegCloseKey(hActiveDeviceRoot);hActiveDeviceRoot = NULL;
}
*NumberOfActiveControllersOut = indexAvailable;
if(ActiveControllersOut){
//
// Sort controlers in next order: First IDE, after SCSI,
// inside each group(IDE and SCSI) sort by preliminary defined SCSIPortNumber
//
qsort(ActiveControllersOut, indexAvailable, sizeof(ActiveControllersOut[0]), pControllerInfoCompare);
//
// Update port number for SCSI devices.
// User could add new SCSIAdapter and after remove old SCSIAdapter,
// it cause that SCSIAdapterNumber will be not effective,
// because for NT it will be (SCSIAdapterNumber - 1)
//
for(i = 0, j = 0; j < indexAvailable; j++){
if(CONTROLLER_SCSI != ActiveControllersOut[j].ControllerType){
continue;
}
//
// Now SCSI controllers sorted, reassign PortNumber
// by right order, in order to recognize in NT.
//
ActiveControllersOut[j].SCSIPortNumber = i++;
}
//
// Calculate effective SCSIPortNumber,
// 0 - IDE Primary, 1 - IDE Secondary, 2 and ... - SCSI
//
for(controllerStartIndex = 0, i = 0;
i < ARRAYSIZE(controllerTypes);
i++, controllerStartIndex += controllersSubNumber){
for(controllersSubNumber = 0, j = 0; j < indexAvailable; j++){
if(controllerTypes[i] != ActiveControllersOut[j].ControllerType){
continue;
}
if(INVALID_SCSI_PORT != ActiveControllersOut[j].SCSIPortNumber){
ActiveControllersOut[j].SCSIPortNumber += controllerStartIndex;
}
controllersSubNumber++;
}
}
}
}
__finally{
if(hDevice){
RegCloseKey(hDevice);
}
if(hActiveDeviceRoot){
RegCloseKey(hActiveDeviceRoot);
}
if(hActiveDevicesRoot){
RegCloseKey(hActiveDevicesRoot);
}
}
return TRUE;
}
BOOL
GatherControllersInfo(
IN OUT PCONTROLLERS_COLLECTION * ControllersCollectionOut
)
{
DWORD rcResult = ERROR_ACCESS_DENIED;
UINT i;
PCONTROLLERS_COLLECTION activeControllersCollection = NULL;
BOOL bResult = FALSE;
UINT activeControllersNumber;
if(!ControllersCollectionOut){
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
__try{
activeControllersCollection = (PCONTROLLERS_COLLECTION)MALLOC(sizeof(CONTROLLERS_COLLECTION));
if(!activeControllersCollection){
rcResult = ERROR_NOT_ENOUGH_MEMORY;
__leave;
}
//
// Acquiring number of controllers in system
//
if(!pGatherControllersInfo(NULL, &activeControllersCollection->NumberOfControllers)){
rcResult = ERROR_ACCESS_DENIED;
__leave;
}
//
// Proceed only if we have positive controllers number
//
if(activeControllersCollection->NumberOfControllers){
activeControllersCollection->ControllersInfo = (PCONTROLLER_INFO)
MALLOC(activeControllersCollection->NumberOfControllers * sizeof(CONTROLLER_INFO));
if(!activeControllersCollection->ControllersInfo){
rcResult = ERROR_NOT_ENOUGH_MEMORY;
__leave;
}
//
// Initialize array
//
memset(activeControllersCollection->ControllersInfo,
0,
activeControllersCollection->NumberOfControllers * sizeof(CONTROLLER_INFO));
for(i = 0; i < activeControllersCollection->NumberOfControllers; i++){
activeControllersCollection->ControllersInfo[i].SCSIPortNumber = INVALID_SCSI_PORT;
}
//
// fill out controllers info array
//
activeControllersNumber = activeControllersCollection->NumberOfControllers;
if(!pGatherControllersInfo(activeControllersCollection->ControllersInfo,
&activeControllersNumber)){
rcResult = ERROR_ACCESS_DENIED;
__leave;
}
}
else{
activeControllersCollection->ControllersInfo = NULL;
}
*ControllersCollectionOut = activeControllersCollection;
rcResult = ERROR_SUCCESS;
}
__finally{
if(ERROR_SUCCESS != rcResult){
if(activeControllersCollection){
ReleaseControllersInfo(activeControllersCollection);
}
}
}
SetLastError(rcResult);
return ERROR_SUCCESS == rcResult;
}
BOOL
ReleaseControllersInfo(
IN PCONTROLLERS_COLLECTION ControllersCollection
)
{
if(!ControllersCollection){
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(ControllersCollection){
if(ControllersCollection->ControllersInfo){
FREE(ControllersCollection->ControllersInfo);
}
FREE(ControllersCollection);
}
return TRUE;
}
BOOL
IsInControllerCollection(
IN PCONTROLLERS_COLLECTION ControllersCollection,
IN PCTSTR PnPIdString,
OUT PUINT Index
)
{
UINT i;
if(!ControllersCollection || !PnPIdString || !Index){
return FALSE;
}
for(i = 0; i < ControllersCollection->NumberOfControllers; i++){
if(!_tcsnicmp(PnPIdString,
ControllersCollection->ControllersInfo[i].PNPID,
_tcslen(ControllersCollection->ControllersInfo[i].PNPID))){
*Index = i;
return TRUE;
}
}
return FALSE;
}
BOOL
GetSCSIAddressFromPnPId(
IN PCONTROLLERS_COLLECTION ControllersCollection,
IN HKEY hDeviceRegKey,
IN PCTSTR PnPIdString,
OUT DRIVE_SCSI_ADDRESS * ScsiAddressOut
)
{
UINT i;
PCTSTR pBufferKeyValue;
BOOL bResult;
if(!ControllersCollection || !hDeviceRegKey || !PnPIdString || !ScsiAddressOut){
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
bResult = FALSE;
do{
//
// Check for presence in controllers list controller PNPID of device
// After complete SCSI_ADDRESS structure with
// DriveLetter, DriveType, TargetID, Lun.
//
if(IsInControllerCollection(ControllersCollection, PnPIdString, &i)){
memset(ScsiAddressOut, 0, sizeof(*ScsiAddressOut));
ScsiAddressOut->PortNumber = (UCHAR)ControllersCollection->ControllersInfo[i].SCSIPortNumber;
bResult = pGetDeviceType(hDeviceRegKey, &ScsiAddressOut->DriveType);
MYASSERT(bResult);
pBufferKeyValue = pRegQueryStringValue(hDeviceRegKey,
DEVICE_CURRENT_DRIVE_LETTER_TEXT_ASSIGNMENT,
NULL,
0);
if(!pBufferKeyValue){
break;
}
ScsiAddressOut->DriveLetter = pBufferKeyValue[0];
pBufferKeyValue = pRegQueryStringValue(hDeviceRegKey, TEXT("ScsiTargetId"), NULL, 0);
if(!pBufferKeyValue){
break;
}
ScsiAddressOut->TargetId = (UCHAR)_ttoi(pBufferKeyValue);
pBufferKeyValue = pRegQueryStringValue(hDeviceRegKey, TEXT("ScsiLun"), NULL, 0);
if(pBufferKeyValue){
//
//For most cases ScsiLun is zero, so it is not fatal.
//
ScsiAddressOut->Lun = (UCHAR)_ttoi(pBufferKeyValue);
}
bResult = TRUE;
}
}while(FALSE);
return bResult;
}
BOOL
DeviceEnum(
IN PCONTROLLERS_COLLECTION ControllersCollection,
IN PCTSTR DeviceCategory,
IN PDEVICE_ENUM_CALLBACK_FUNCTION DeviceEnumCallbackFunction,
IN PVOID CallbackData
)
{
TCHAR deviceType[MAX_REG_SIZE];
TCHAR regkeyName[MAX_PNPID_SIZE];
TCHAR deviceInfoRegKey[MAX_REG_SIZE];
HKEY hActiveDevicesRoot = NULL;
HKEY hActiveDeviceRoot = NULL;
HKEY hDevice = NULL;
UINT itemIndexRoot;
DWORD bufferLength;
UINT controllerIndex;
PTSTR pDevicePNPIDName;
UINT deviceTypeLen;
DWORD rcResult;
if(!ControllersCollection || !DeviceCategory || !DeviceEnumCallbackFunction){
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
__try{
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_DYN_DATA, TEXT("Config Manager\\Enum"), 0, KEY_READ, &hActiveDevicesRoot)){
return FALSE;
}
_tcscpy(deviceType, DeviceCategory);
_tcscat(deviceType, TEXT("\\"));
deviceTypeLen = _tcslen(deviceType);
//
// Looking for devices that attached to controllers in our list
//
for(itemIndexRoot = 0; ;itemIndexRoot++){
bufferLength = ARRAYSIZE(regkeyName);
rcResult = RegEnumKeyEx(hActiveDevicesRoot,
itemIndexRoot,
regkeyName,
&bufferLength,
0,
NULL,
NULL,
NULL);
if(ERROR_SUCCESS != rcResult){
break;
}
if(ERROR_SUCCESS != RegOpenKeyEx(hActiveDevicesRoot, regkeyName, 0, KEY_READ, &hActiveDeviceRoot)){
continue;
}
//
// "HardWareKey" consist key path to real device
//
if(pRegQueryStringValue(hActiveDeviceRoot,
TEXT("HardWareKey"),
regkeyName,
sizeof(regkeyName))){
if(!_tcsnicmp(regkeyName, deviceType, deviceTypeLen)){
_tcscpy(deviceInfoRegKey, TEXT("Enum\\"));
_tcscat(deviceInfoRegKey, regkeyName);
//
// Make a Controller PNPID from device PNPID
//
pDevicePNPIDName = _tcsrchr(regkeyName, '\\');
MYASSERT(pDevicePNPIDName);
pDevicePNPIDName++;
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, deviceInfoRegKey, 0, KEY_READ, &hDevice)){
//
// Check for presence Controller PNPID in controllers list and
// for device availability.
//
if(IsInControllerCollection(ControllersCollection, pDevicePNPIDName, &controllerIndex) &&
pDoesDriveExist(hDevice, NULL)){
//
// Call callback for every active device we found,
// which controller in our list
// Stop enum, if user does not want to.
//
if(!DeviceEnumCallbackFunction(hDevice, ControllersCollection, controllerIndex, CallbackData)){
//
// Stop enum, if user does not want to.
//
__leave;
}
}
RegCloseKey(hDevice);hDevice = NULL;
}
}
}
RegCloseKey(hActiveDeviceRoot);hActiveDeviceRoot = NULL;
}
}
__finally{
if(hDevice){
RegCloseKey(hDevice);
}
if(hActiveDeviceRoot){
RegCloseKey(hActiveDeviceRoot);
}
if(hActiveDevicesRoot){
RegCloseKey(hActiveDevicesRoot);
}
}
return TRUE;
}