Windows下遍历所有PCI设备

一、PCI配置空间简介

PCI有三个相互独立的物理地址空间:设备存储器地址空间、I/O地址空间和配置空间。配置空间是PCI所特有的一个物理空间。由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是由操作系统决定其映射的基址。

系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置。所以,所有的PCI设备必须实现配置空间,从而能够实现参数的自动配置,实现真正的即插即用。

PCI总线规范定义的配置空间总长度为256个字节,配置信息按一定的顺序和大小依次存放。前64个字节的配置空间称为配置头,对于所有的设备都一样,配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问或者存储器访问,还有中断信息)。其余的192个字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。

PPCI设备有三个空间——内存地址空间、IO地址空间和配置空间。由于PCI支持即插即用,所以PCI设备不是占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。怎么配置呢?这就是配置空间的作用。

		DW |    Byte3    |    Byte2    |    Byte1    |     Byte0     | Addr
		---+---------------------------------------------------------+-----
		 0 |     Device ID     |     Vendor ID      | 00
		---+---------------------------------------------------------+-----
		 1 |      Status     |      Command      | 04
		---+---------------------------------------------------------+-----
		 2 |        Class Code        | Revision ID | 08
		---+---------------------------------------------------------+-----
		 3 |   BIST  | Header Type | Latency Timer | Cache Line  | 0C
		---+---------------------------------------------------------+-----
		 4 |           Base Address 0           | 10
		---+---------------------------------------------------------+-----
		 5 |           Base Address 1           | 14
		---+---------------------------------------------------------+-----
		 6 |           Base Address 2           | 18
		---+---------------------------------------------------------+-----
		 7 |           Base Address 3           | 1C
		---+---------------------------------------------------------+-----
		 8 |           Base Address 4           | 20
		---+---------------------------------------------------------+-----
		 9 |           Base Address 5           | 24
		---+---------------------------------------------------------+-----
		10 |          CardBus CIS pointer          | 28
		---+---------------------------------------------------------+-----
		11 |  Subsystem Device ID  |   Subsystem Vendor ID   | 2C
		---+---------------------------------------------------------+-----
		12 |        Expansion ROM Base Address        | 30
		---+---------------------------------------------------------+-----
		13 |        Reserved(Capability List)         | 34
		---+---------------------------------------------------------+-----
		14 |            Reserved             | 38
		---+---------------------------------------------------------+-----
		15 |  Max_Lat  |  Min_Gnt  |  IRQ Pin  |  IRQ Line  | 3C
		-------------------------------------------------------------------

配置空间中最重要的有:

	Vendor  ID:厂商ID。知名的设备厂商的ID。FFFFh是一个非法厂商ID,可它来判断PCI设备是否存在。
	Device  ID:设备ID。某厂商生产的设备的ID。操作系统就是凭着 Vendor ID和Device ID 找到对应驱动程序的。
	Class Code:类代码。共三字节,分别是 类代码、子类代码、编程接口。类代码不仅用于区分设备类型,还是编程接口的规范,这就是为什么会有通用驱动程序。
	IRQ   Line:IRQ编号。PC机以前是靠两片8259芯片来管理16个硬件中断。现在为了支持对称多处理器,有了APIC(高级可编程中断控制器),它支持管理24个中断。
	IRQ    Pin:中断引脚。PCI有4个中断引脚,该寄存器表明该设备连接的是哪个引脚。

二、如何访问配置空间

  如何访问配置空间呢?可通过访问CF8h、CFCh端口来实现。
    CF8h: CONFIG_ADDRESS。PCI配置空间地址端口。
    CFCh: CONFIG_DATA。PCI配置空间数据端口。
  CONFIG_ADDRESS寄存器格式:
         31 位:Enabled位。
        23:16 位:总线编号。
        15:11 位:设备编号。
        10: 8 位:功能编号。
         7: 2 位:配置空间寄存器编号。
         1: 0 位:恒为“00”。这是因为CF8h、CFCh端口是32位端口。
#include <tchar.h>
#include <stdio.h>
#include <conio.h>
#include "HwRwDrv.h"

#define PCI_CONFIG_ADDRESS 0xcf8
#define PCI_CONFIG_DATA 0xcfc

int _tmain(int argc, _TCHAR* argv[])
{
	int bus, dev, func, count=0;
	DWORD dwAddr, dwData, VID, DID, SVID, SDID, dwData1, dwClassCode;

	if(!LoadHwRwDrv())
	{
		textcolor(RED);
		cprintf("Load driver error!\r\nReturn 1\r\n");
		return 1;
	}
	printf("BUS#\tDEV#\tFUNC#\tVID\tDID\tSVID\tSDID\tClass code\n");
	for(bus = 0; bus <= 255; bus++)
	{
		for(dev = 0; dev < 32; dev++)
		{
			for(func = 0; func < 8; func++)
			{
				dwAddr=0x80000000+(bus<<16)+(dev<<11)+(func<<8);
				/* read vendor id */
				WriteIoPortDword(PCI_CONFIG_ADDRESS, dwAddr);
				dwData=ReadIoPortDword(PCI_CONFIG_DATA);

				/*  read sub-vendor id*/
				WriteIoPortDword(PCI_CONFIG_ADDRESS, dwAddr|0x2C);
				dwData1=ReadIoPortDword(PCI_CONFIG_DATA);

				/*  read class code*/
				WriteIoPortDword(PCI_CONFIG_ADDRESS, dwAddr|0x08);
				dwClassCode=ReadIoPortDword(PCI_CONFIG_DATA);

				if(dwData!=0xffffffff)
				{
					count++;
					VID=dwData&0xffff;
					DID=(dwData>>16)&0xffff;
                    SVID = dwData1&0xffff;
					SDID=(dwData1>>16)&0xffff;
					printf("%02X\t%02X\t%02X\t%04X\t%04X\t%04X\t%04X\t%6.6lX\n",
							bus,dev,func,VID,DID,SVID,SDID,dwClassCode>>8);
                }
			}
		}
	}

        printf("\nTotal devices: %d", count);

	UnLoadHwRwDrv();
	return 0;
}

你可能感兴趣的:(Windows下遍历所有PCI设备)