一个系统上最多有256个PCI总线,每个总线最多有32个设备,每个设备最多有8个功能,每个功能最多有256字节的配置地址空间,所以总的配置地址空间是16M(256字节 ×8 ×32 ×256)。
一个PCI设备可由bus号、device号、function号唯一确定。这三个参数构成PCI设备标识符。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
bus | device | function |
设备标识符
PCI总线规范定义的配置空间总长度为256个字节,配置信息按一定的顺序和大小依次存放。前64个字节的配置空间称为配置头(如下图所示),对于所有的设备都一样,配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问或者存储器访问,还有中断信息)。其余的192个字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。
DW | Byte3 | Byte2 | Byte1 | Byte0 | Offset |
0 | Device ID | Vender ID | 00H | ||
1 | Status | Command | 04H | ||
2 | Class Code | Rev ID | 08H | ||
3 | BIST | Header Type | Latency Timer | Cache Line | 0CH |
4 | Base Address 0 | 10H | |||
5 | Base Address 1 | 14H | |||
6 | Base Address 2 | 18H | |||
7 | Base Address 3 | 1CH | |||
8 | Base Address 4 | 20H | |||
9 | Base Address 5 | 24H | |||
10 | Card Bus CIS pointer | 28H | |||
11 | Subsystem Device ID | Subsystem Vendor ID | 2CH | ||
12 | Expansion ROM Base Address | 30H | |||
13 | Reserved(Capability List) | 34H | |||
14 | Reserved | 38H | |||
15 | Max_Lat | Min_Gnt | IRQ Pin | IRQ Line | 3CH |
X86 CPU可以访问存储器或IO地址空间,但不能直接访问配置地址空间。CPU通过主桥(北桥或MCH)中的一个IO映射的地址端口和数据端口间接访问PCI配置空间。该地址端口位于IO空间CF8h~CFBh,而数据端口映射到CFCh~CFFh。
CF8h: CONFIG_ADDRESS PCI配置空间地址端口。
CFCh: CONFIG_DATA PCI配置空间数据端口。
由于在Windows下有保护模式,不能直接访问底层,需要通过Winio访问,Winio可在以下网址下载:
http://download.csdn.net/source/3524348
代码如下:
#include <windows.h>
#include "winio.h"
#include <stdlib.h>
#include <stdio.h>
#define PCI_CONFIG_ADDRESS 0xcf8
#define PCI_CONFIG_DATA 0xcfc
#pragma comment(lib,"winio.lib")
int main()
{
int bus,dev,func,count=0;DWORD dwAddr,dwData,VID,DID;if(InitializeWinIo()){printf("VID\tDID\tBUS#\tDev#\tFun#\n");for(bus=0;bus<256;bus++){for(dev=0;dev<32;dev++){for(func=0;func<8;func++){dwAddr=0x80000000+(bus<<16)+(dev<<11)+(func<<8);SetPortVal(PCI_CONFIG_ADDRESS,dwAddr,4);GetPortVal(PCI_CONFIG_DATA,&dwData,4);if(dwData!=0xffffffff){count++;VID=dwData&0xffff;DID=(dwData>>16)&0xffff;printf("%04x\t%04x\t%02x\t%02x\t%02x\n",VID,DID,bus,dev,func);}}}}printf("Find PCI Device %d",count);ShutdownWinIo();}else{printf("Initialize Win IO Fail!\n");exit(255);}getchar();return 0;
}