1. 程式人生 > >Windows下遍歷所有PCI裝置

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;
}