한 걸음씩..

PC의 모든 PCI 장치가 사용중인 리소스 정보를 가져오는 방법 본문

프로그래밍

PC의 모든 PCI 장치가 사용중인 리소스 정보를 가져오는 방법

반엘 2013. 7. 15. 10:53

프로젝트 도중 PCI장치의 Base Address를 임의로 할당 해 줘야 하는 일이 발생하여 다른 PCI장치들이 사용하지 않는 영역을 구하고 물리 메모리를 필요한만큼 할당하기 위해 Application에 기능 구현


1. PCI장치가 사용하는 물리 메모리 영역은 E00000000 ~ FFFFFFFF 까지 이다.

2. 모든 PCI장치들의 리소스 정보는 레지스트리에서 확인할 수 있다.

 HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\PnP Manager\PnpManager

  - 예를 들어 PCI Lan Card 의 정보[ 속성->자세히->실제 장치 개체 이름 ]을 확인하면 [ \Device\NTPNP_PCI0014 ] 라는 값을 확인할 수 있다.

  - 이를 위 레지스트리에서 확인하게 되면 [ \Device\NTPNP_PCI0014.Translated ] 라는 이름으로 존재하고 있음을 확인할 수 있다.

  - 위 레지스트리 정보 중 Raw와 Translated 라는 이름으로 하나의 장치에 두개씩의 이름이 있는것을 확인 할 수 있다.

두가지 정보의 차이점을 msdn에서 찾아보면 아래와 같이 나온다.

 
  • Raw resources are resources that are identified by addresses that are relative to the bus to which the device is connected. Typically, the driver that programs the device provides these addresses to the device.

  • Translated resources are resources that are identified by system physical addresses that drivers use to access the resources.

링크 : http://msdn.microsoft.com/en-us/library/windows/hardware/ff544561(v=vs.85).aspx

  - 따라서 우리가 사용할 정보는 Translated 이다.


3. 구현

  - Application에 이 기능을 구현하기 위해서 WinDDK 에 정의되어 있는 구조체 정보 중에 CM_RESOURCE_LIST 구조체를 복사하였다. 아래 define과 구조체를 복사하여 사용하여도 좋고 wdm.h 파일을 include 하여 사용해도 무관하다.

  - Header

/* Taken from ntddk.h(shorten) */

#define CmResourceTypeNull                0   // ResType_All or ResType_None (0x0000)

#define CmResourceTypePort                1   // ResType_IO (0x0002)

#define CmResourceTypeInterrupt           2   // ResType_IRQ (0x0004)

#define CmResourceTypeMemory              3   // ResType_Mem (0x0001)

#define CmResourceTypeDma                 4   // ResType_DMA (0x0003)

#define CmResourceTypeDeviceSpecific      5   // ResType_ClassSpecific (0xFFFF)

#define CmResourceTypeBusNumber           6   // ResType_BusNumber (0x0006)

#define CmResourceTypeMaximum             7

#define CmResourceTypeAssignedResource    8   // BUGBUG--remove

#define CmResourceTypeSubAllocateFrom     9   // BUGBUG--remove

#define CmResourceTypeNonArbitrated     128   // Not arbitrated if 0x80 bit set

#define CmResourceTypeConfigData        128   // ResType_Reserved (0x8000)

#define CmResourceTypeDevicePrivate     129   // ResType_DevicePrivate (0x8001)

#define CmResourceTypePcCardConfig      130   // ResType_PcCardConfig (0x8002)

#define CmResourceTypeMfCardConfig      131   // ResType_MfCardConfig (0x8003)


typedef enum _INTERFACE_TYPE {

InterfaceTypeUndefined = -1,

Internal,

Isa,

Eisa,

MicroChannel,

TurboChannel,

PCIBus,

VMEBus,

NuBus,

PCMCIABus,

CBus,

MPIBus,

MPSABus,

ProcessorInternal,

InternalPowerBus,

PNPISABus,

PNPBus,

MaximumInterfaceType

}INTERFACE_TYPE, *PINTERFACE_TYPE;

//////////////////////////////////////////////////////////////////////////////

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;


#pragma pack(4)

typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {

UCHAR Type;

UCHAR ShareDisposition;

USHORT Flags;

union {


struct {

PHYSICAL_ADDRESS Start;

ULONG Length;

} Generic;


struct {

PHYSICAL_ADDRESS Start;

ULONG Length;

} Port;


struct {

ULONG Level;

ULONG Vector;

ULONG Affinity;

} Interrupt;


struct {

PHYSICAL_ADDRESS Start;    // 64 bit physical addresses.

ULONG Length;

} Memory;


struct {

ULONG Channel;

ULONG Port;

ULONG Reserved1;

} Dma;


struct {

ULONG Data[3];

} DevicePrivate;


struct {

ULONG Start;

ULONG Length;

ULONG Reserved;

} BusNumber;


struct {

ULONG DataSize;

ULONG Reserved1;

ULONG Reserved2;

} DeviceSpecificData;

} u;

} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;

#pragma pack()


typedef struct _CM_PARTIAL_RESOURCE_LIST {

USHORT Version;

USHORT Revision;

ULONG Count;

CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];

} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;



typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {

INTERFACE_TYPE InterfaceType;

ULONG BusNumber;

CM_PARTIAL_RESOURCE_LIST PartialResourceList;

} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;


typedef struct _CM_RESOURCE_LIST {

ULONG Count;

CM_FULL_RESOURCE_DESCRIPTOR List[1];

} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;

////////////////////////////////////////////////////////////////////////////

/* 사용중인 메모리 영역을 저장하고 필요한 메모리 영역을 구하기 위해 만든 구조체 */

typedef struct _Memory_Range

{

PHYSICAL_ADDRESS Start;

DWORD Len;

}Memory_Range, *PMemory_Range;

  - Sources

      물리 메모리 영역 E00000000 ~ FFFFFFFF 중 F00000000 ~ FFFFFFFF 사이에 비어있는 영역을 구하고 있음

      변수명이 기니까 전체화면으로 보는걸 추천합니다.

#define BufSize 512


HKEY h_Key;

LONG Reg_Result;

DWORD DeviceValueSize;

DWORD dwType;

DWORD dwSize;

TCHAR DeviceValue[MAX_PATH];

PCM_RESOURCE_LIST pPCIDeviceResource; /* 가져온 데이터를 구조체에 저장 */

char szbuf[BufSize];

DWORD TypeMemoryCount = 0;

DWORD SkipMemory;

BOOL Address_Result;

 /* 읽어온 메모리 영역을 저장하기 위해 */

PMemory_Range AddressRange = (PMemory_Range)malloc( BufSize );


DeviceValueSize = sizeof(DeviceValue);

dwSize = sizeof(szbuf);



Reg_Result = RegOpenKeyEx(

HKEY_LOCAL_MACHINE,

 _T("HARDWARE\\RESOURCEMAP\\PnP Manager\\PnpManager"), /*Sub key*/

 0,

 KEY_READ | KEY_QUERY_VALUE,

 &h_Key);   


if(Reg_Result == ERROR_SUCCESS){

for(int i = 0; i < Reg_Result == ERROR_SUCCESS; i++){

/* 지정된 레지스트리 키가 가지는 값을 열거 */

Reg_Result = RegEnumValue(

h_Key,

i,

DeviceValue,     

&DeviceValueSize,     

NULL, NULL, NULL, NULL);

     /* 내가 필요한 정보는 PCI장치의 Translated이므로 */

if( _tcsstr(DeviceValue, _T("\\Device\\NTPNP_PCI") ) != NULL && _tcsstr( DeviceValue, _T("Translated") ) != NULL ){

if(Reg_Result == ERROR_SUCCESS){

/*****************************************************/

//dwSize = BufSize; 꼭 초기화 해주자......

//이 부분이 없어서 0x000000ea Error [ERROR_MORE_DATA] 발생

//If the lpData buffer is too small to receive the data, the function returns

       // ERROR_MORE_DATA

/*****************************************************/

dwSize = BufSize;

  /* 명시된 항목이름의 데이터 형식이나 내용을 얻는데 사용 */

     Reg_Result = RegQueryValueEx(

h_Key,

DeviceValue, 

0, 

&dwType, 

(LPBYTE)&szbuf, 

&dwSize);

if (Reg_Result == ERROR_SUCCESS){

/* 읽어 온 szbuf 를 PCM_RESOURCE_LIST에 적용 */

pPCIDeviceResource = (PCM_RESOURCE_LIST)szbuf;

for ( DWORD ii = 0; ii < pPCIDeviceResource->Count; ii++){

if( pPCIDeviceResource->List[ii].InterfaceType == PCIBus){

for ( DWORD iii = 0; iii < pPCIDeviceResource->List[ii].PartialResourceList.Count; iii++ ){

switch( pPCIDeviceResource->List[ii].PartialResourceList.PartialDescriptors[iii].Type )

     {

case CmResourceTypeMemory:

{

 if( pPCIDeviceResource->List[ii].PartialResourceList.PartialDescriptors[iii].u.Memory.Start.QuadPart >= 0xF0000000 ){

    AddressRange[TypeMemoryCount].Start = pPCIDeviceResource->List[ii].PartialResourceList.PartialDescriptors[iii].u.Memory.Start;

AddressRange[TypeMemoryCount].Len =  pPCIDeviceResource->    List[ii].PartialResourceList.PartialDescriptors[iii].u.Memory.Length;

  TypeMemoryCount++;

    }

}

break;

}

}

                   }

     }

                    } 

                     else

                    {

         //Error Code

                    }

          }

           }

      DeviceValueSize = MAX_PATH;

      }

}

RegCloseKey(h_Key);


/* 0xF0000000  ~ 0xFFFFFFFF 까지 영역중에 사용하지 않고 

16MB ( 0x1000000 ) 의 연속된 공간이 있는 영역 찾기*/

for(DWORD i = 0xF0000000; i < 0xFFFFFFFF; i++)

{

SkipMemory = i;

Address_Result = TRUE;

for(DWORD j = 0; j < TypeMemoryCount; j++)

{

if( SkipMemory > AddressRange[j].Start.LowPart &&  SkipMemory <= AddressRange[j].Start.LowPart + AddressRange[j].Len )

Address_Result = FALSE;

}

if ( Address_Result )

{

SkipMemory += 0x1000000;

for(DWORD j = 0; j < TypeMemoryCount; j++)

{

if( SkipMemory > AddressRange[j].Start.LowPart && SkipMemory <= AddressRange[j].Start.LowPart + AddressRange[j].Len )

Address_Result = FALSE;

}

if( Address_Result )

{

CString Print;

Print.Format(_T("0x%08x"),i);

AfxMessageBox( Print );

SkipMemory = i;

break;

}

}

}


free(AddressRange);

// IOCTL을 통하여 위에서 구한 SkipMemory Address를 전송. Base Address 등록