PCI总线体系结构概述
生活随笔
收集整理的這篇文章主要介紹了
PCI总线体系结构概述
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
§1.PCI總線體系結(jié)構(gòu)概述?
PCI總線體系結(jié)構(gòu)是一種層次式的(Hierarchical)體系結(jié)構(gòu)。在這種層次式體系結(jié)構(gòu)中,PCI橋設備占據(jù)著重要的地位,它將父總線與子總線連接在一起,從而使整個系統(tǒng)看起來像一顆倒置的樹型結(jié)構(gòu)。樹的頂端是系統(tǒng)的CPU,它通過一個較為特殊的PCI橋設備——Host/PCI橋設備與根PCI總線(root?pci?bus)連接起來。下圖1是一個較為典型的PCI總線體系結(jié)構(gòu)圖。?
(圖1)?
從上圖也可以看出,作為一種特殊的PCI設備,PCI橋又包括以下幾種:?
(1)Host/PCI橋:用于連接CPU與PCI根總線(注意,the?first?root?bus總是編號為0)。由于像Memory?Controller這樣的設備通常也被集成到Host/PCI橋設備芯片中,因此,Host/PCI橋通常也被稱為“北橋芯片組(North?Bridge?Chipset)”。?
(2)PCI/ISA橋:用于連接遺留的ISA總線。通常,像i8359A中斷控制器這樣的設備也會被集成到PCI/ISA橋設備中,因此,PCI/ISA橋通常也被稱為“南橋芯片組(South?Bridge?Chipset)”。?
(3)PCI-to-PCI橋:用于連接PCI主總線(primary?bus)與次總線(secondary?bus)。PCI橋所處的PCI總線稱為“主總線”(即次總線的父總線),橋設備所連接的PCI總線稱為“次總線”(即主總線的子總線)。?
更多類型的PCI橋分類可以參見《PCI?Local?Bus?Specification》Revision2.2的附錄D。?
本文以下部分假設讀者對PCI總線規(guī)范有一定的了解,因此本文是為那些想要深入了解Linux內(nèi)核是如何實現(xiàn)PCI總線驅(qū)動程序的Kernel?Hacker而寫的。?
§2?PCI總線驅(qū)動程序的核心數(shù)據(jù)結(jié)構(gòu)?
從前面的圖1可以看出,在PCI總線體系結(jié)構(gòu)中,有兩個核心的概念存在:PCI總線和PCI設備(橋設備是一種特殊的PCI設備)?;趯@兩個核心概念的抽象,Linux?PCI總線驅(qū)動程序定義了兩個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):結(jié)構(gòu)類型pci_bus和結(jié)構(gòu)類型pci_dev,以分別描述pci總線和pci設備。在此基礎上,Linux?PCI總線驅(qū)動程序又將系統(tǒng)中當前存在的所有PCI總線的pci_bus結(jié)構(gòu)組織成一張層次式的鏈表圖,顯然該鏈表圖的頂層鏈表是系統(tǒng)中所有根總線的pci_bus結(jié)構(gòu)鏈表,因此用一個list_head結(jié)構(gòu)類型的全局變量pci_root_buses來描述該鏈表圖中的頂層鏈表表頭,也即根總線鏈表的表頭。系統(tǒng)中當前存在的所有PCI設備的pci_dev結(jié)構(gòu)也被組織成一條鏈表,稱為“全局pci設備鏈表”,用一個list_head結(jié)構(gòu)類型的變量pci_devices來表示該鏈表的表頭。通過這兩條總鏈表,Linux內(nèi)核就可以得到一張當前系統(tǒng)的PCI總線體系結(jié)構(gòu)視圖。?
§2.1?PCI設備描述符——pci_dev結(jié)構(gòu)類型?
所有種類的PCI設備都可以用結(jié)構(gòu)類型pci_dev來描述。更為準確地說,應該是每一個PCI功能,即PCI邏輯設備都唯一地對應有一個pci_dev設備描述符。該數(shù)據(jù)結(jié)構(gòu)的定義如下(include/linux/pci.h):?
/*?
*?The?pci_dev?structure?is?used?to?describe?both?PCI?and?ISAPnP?devices.?
*/?
struct?pci_dev?{?
struct?list_head?global_list;?/*?node?in?list?of?all?PCI?devices?*/?
struct?list_head?bus_list;?/*?node?in?per-bus?list?*/?
struct?pci_bus?*bus;?/*?bus?this?device?is?on?*/?
struct?pci_bus?*subordinate;?/*?bus?this?device?bridges?to?*/?
void?*sysdata;?/*?hook?for?sys-specific?extension?*/?
struct?proc_dir_entry?*procent;?/*?device?entry?in?/proc/bus/pci?*/?
unsigned?int?devfn;?/*?encoded?device?&?function?index?*/?
unsigned?short?vendor;?
unsigned?short?device;?
unsigned?short?subsystem_vendor;?
unsigned?short?subsystem_device;?
unsigned?int?class;?/*?3?bytes:?(base,sub,prog-if)?*/?
u8?hdr_type;?/*?PCI?header?type?(`multi'?flag?masked?out)?*/?
u8?rom_base_reg;?/*?which?config?register?controls?the?ROM?*/?
struct?pci_driver?*driver;?/*?which?driver?has?allocated?this?device?*/?
void?*driver_data;?/*?data?private?to?the?driver?*/?
dma_addr_t?dma_mask;?/*?Mask?of?the?bits?of?bus?address?this?
device?implements.?Normally?this?is?
0xffffffff.?You?only?need?to?change?
this?if?your?device?has?broken?DMA?
or?supports?64-bit?transfers.?*/?
/*?device?is?compatible?with?these?IDs?*/?
unsigned?short?vendor_compatible[DEVICE_COUNT_COMPATIBLE];?
unsigned?short?device_compatible[DEVICE_COUNT_COMPATIBLE];?
/*?
*?Instead?of?touching?interrupt?line?and?base?address?registers?
*?directly,?use?the?values?stored?here.?They?might?be?different!?
*/?
unsigned?int?irq;?
struct?resource?resource[DEVICE_COUNT_RESOURCE];?/*?I/O?and?memory?regions?+?expansion?ROMs?*/?
struct?resource?dma_resource[DEVICE_COUNT_DMA];?
struct?resource?irq_resource[DEVICE_COUNT_IRQ];?
char?name[80];?/*?device?name?*/?
char?slot_name[8];?/*?slot?name?*/?
int?active;?/*?ISAPnP:?device?is?active?*/?
int?ro;?/*?ISAPnP:?read?only?*/?
unsigned?short?regs;?/*?ISAPnP:?supported?registers?*/?
int?(*prepare)(struct?pci_dev?*dev);?/*?ISAPnP?hooks?*/?
int?(*activate)(struct?pci_dev?*dev);?
int?(*deactivate)(struct?pci_dev?*dev);?
};?
各成員的含義如下:?
(1)全局鏈表元素global_list:每一個pci_dev結(jié)構(gòu)都通過該成員連接到全局pci設備鏈表pci_devices中。?
(2)總線設備鏈表元素bus_list:每一個pci_dev結(jié)構(gòu)除了鏈接到全局設備鏈表中外,還會通過這個成員連接到其所屬PCI總線的設備鏈表中。每一條PCI總線都維護一條它自己的設備鏈表視圖,以便描述所有連接在該PCI總線上的設備,其表頭由PCI總線的pci_bus結(jié)構(gòu)中的devices成員所描述。?
(3)總線指針bus:指向這個PCI設備所在的PCI總線的pci_bus結(jié)構(gòu)。因此,對于橋設備而言,bus指針將指向橋設備的主總線(primary?bus),也即指向橋設備所在的PCI總線。?
(4)指針subordinate:指向這個PCI設備所橋接的下級總線。這個指針成員僅對橋設備才有意義,而對于一般的非橋PCI設備而言,該指針成員總是為NULL。?
(5)無類型指針sysdata:指向一片特定于系統(tǒng)的擴展數(shù)據(jù)。?
(6)指針procent:指向該PCI設備在/proc文件系統(tǒng)中對應的目錄項。?
(7)devfn:這個PCI設備的設備功能號,也成為PCI邏輯設備號(0-255)。其中bit[7:3]是物理設備號(取值范圍0-31),bit[2:0]是功能號(取值范圍0-7)。?
(8)vendor:這是一個16無符號整數(shù),表示PCI設備的廠商ID。?
(9)device:這是一個16無符號整數(shù),表示PCI設備的設備ID。?
(10)subsystem_vendor:這是一個16無符號整數(shù),表示PCI設備的子系統(tǒng)廠商ID。?
(11)subsystem_device:這是一個16無符號整數(shù),表示PCI設備的子系統(tǒng)設備ID。?
(12)class:32位的無符號整數(shù),表示該PCI設備的類別,其中,bit[7:0]為編程接口,bit[15:8]為子類別代碼,bit[23:16]為基類別代碼,bit[31:24]無意義。顯然,class成員的低3字節(jié)剛好對應與PCI配置空間中的類代碼。?
(13)hdr_type:8位符號整數(shù),表示PCI配置空間頭部的類型。其中,bit[7]=1表示這是一個多功能設備,bit[7]=0表示這是一個單功能設備。Bit[6:0]則表示PCI配置空間頭部的布局類型,值00h表示這是一個一般PCI設備的配置空間頭部,值01h表示這是一個PCI-to-PCI橋的配置空間頭部,值02h表示CardBus橋的配置空間頭部。?
(14)rom_base_reg:8位無符號整數(shù),表示PCI配置空間中的ROM基地址寄存器在PCI配置空間中的位置。ROM基地址寄存器在不同類型的PCI配置空間頭部的位置是不一樣的,對于type?0的配置空間布局,ROM基地址寄存器的起始位置是30h,而對于PCI-to-PCI橋所用的type?1配置空間布局,ROM基地址寄存器的起始位置是38h。?
(15)指針driver:指向這個PCI設備所對應的驅(qū)動程序定義的pci_driver結(jié)構(gòu)。每一個pci設備驅(qū)動程序都必須定義它自己的pci_driver結(jié)構(gòu)來描述它自己。這個數(shù)據(jù)結(jié)構(gòu)將在后面分析。?
(16)無類型指針driver_data:指向驅(qū)動程序為這個PCI設備所分配的一塊私有數(shù)據(jù)區(qū)。通常,設備驅(qū)動程序會為它所支持的每一種設備類型定義一個特定于設備類型的數(shù)據(jù)結(jié)構(gòu),以描述該類型設備的信息。而且,driver會為它所找到的每一個設備實例分配一個特定于該設備類型的數(shù)據(jù)結(jié)構(gòu)實例。指針driver_data一般就用來指向這個有驅(qū)動程序所分配的數(shù)據(jù)結(jié)構(gòu)實例。?
(17)dma_mask:用于DMA的總線地址掩碼,一般來說,這個成員的值是0xffffffff。數(shù)據(jù)類型dma_addr_t定義在include/asm/types.h中,在x86平臺上,dma_addr_t類型就是u32類型。?
(18)vendor_compatible[DEVICE_COUNT_COMPATIBLE]和device_compatible[DEVICE_COUNT_COMPATIBLE]:定義這個PCI設備與哪些設備相兼容。?
(19)無符號的整數(shù)irq:表示這個PCI設備通過哪根IRQ輸入線產(chǎn)生中斷,一般為0-15之間的某個值。?
(20)資源數(shù)組resource[DEVICE_COUNT_RESOURCE]:表示該設備可能用到的資源,包括:I/O斷口區(qū)域、設備內(nèi)存地址區(qū)域以及擴展ROM地址區(qū)域。宏DEVICE_COUNT_DEVICE在pci.h頭文件中被定義為常值12。但是PCI設備通常僅使用這12各資源區(qū)域中的一部分。其中,resource[5:0]分別對應于配置空間中的BAR0-BAR5(注意,橋設備只有BAR0和BAR1),resource[6]對應于配置空間中的ROM基地址寄存器所描述的ROM區(qū)域,而resource[10:7]分別對應于橋設備的地址過濾窗口。?
(21)數(shù)組dma_resource[DEVICE_COUNT_DMA]:用于DMA的資源,DEVICE_COUNT_DMA為2。?
(22)數(shù)組irq_resource[DEVICE_COUNT_IRQ]:用于IRQ的資源,DEVICE_COUNT_IRQ為2。?
(23)name[80]數(shù)組:定義這個PCI邏輯設備的名字字符串。?
(24)slot_name[8]數(shù)組:如果這個PCI邏輯設備是通過PCI插槽連接到PCI總線上的,則slot_name數(shù)組表示該插槽的名字字符串。?
(25)active:被ISAPnP模塊用來表示設備是否被激活。?
(26)ro:被ISAPnP模塊用來表示設備是否為只讀設備。?
(27)regs:被ISAPnP模塊用來表示設備設備所支持的寄存器個數(shù)。?
(28)函數(shù)指針prepare、activate和deactivate:都是僅被ISAPnP模塊鉤掛的函數(shù)指針。?
§2.2?PCI總線描述符——pci_bus結(jié)構(gòu)類型?
該結(jié)構(gòu)定義在include/linux/pci.h頭文件中,如下所示:?
struct?pci_bus?{?
struct?list_head?node;?/*?node?in?list?of?buses?*/?
struct?pci_bus?*parent;?/*?parent?bus?this?bridge?is?on?*/?
struct?list_head?children;?/*?list?of?child?buses?*/?
struct?list_head?devices;?/*?list?of?devices?on?this?bus?*/?
struct?pci_dev?*self;?/*?bridge?device?as?seen?by?parent?*/?
struct?resource?*resource[4];?/*?address?space?routed?to?this?bus?*/?
struct?pci_ops?*ops;?/*?configuration?access?functions?*/?
void?*sysdata;?/*?hook?for?sys-specific?extension?*/?
struct?proc_dir_entry?*procdir;?/*?directory?entry?in?/proc/bus/pci?*/?
unsigned?char?number;?/*?bus?number?*/?
unsigned?char?primary;?/*?number?of?primary?bridge?*/?
unsigned?char?secondary;?/*?number?of?secondary?bridge?*/?
unsigned?char?subordinate;?/*?max?number?of?subordinate?buses?*/?
char?name[48];?
unsigned?short?vendor;?
unsigned?short?device;?
unsigned?int?serial;?/*?serial?number?*/?
unsigned?char?pnpver;?/*?Plug?&?Play?version?*/?
unsigned?char?productver;?/*?product?version?*/?
unsigned?char?checksum;?/*?if?zero?-?checksum?passed?*/?
unsigned?char?pad1;?
};?
各成員的含義如下:?
(1)鏈表元素node:對于PCI根總線而言,其pci_bus結(jié)構(gòu)通過node成員鏈接到本節(jié)一開始所述的根總線鏈表中,根總線鏈表的表頭由一個list_head類型的全局變量pci_root_buses所描述。而對于非根pci總線,其pci_bus結(jié)構(gòu)通過node成員鏈接到其父總線的子總線鏈表children中(見下面)。?
(2)parent指針:指向該pci總線的父總線,即pci橋所在的那條總線。?
(3)children指針:描述了這條PCI總線的子總線鏈表的表頭。這條PCI總線的所有子總線都通過上述的node鏈表元素鏈接成一條子總線鏈表,而該鏈表的表頭就由父總線的children指針所描述。?
(4)devices鏈表頭:描述了這條PCI總線的邏輯設備鏈表的表頭。除了鏈接在全局PCI設備鏈表中之外,每一個PCI邏輯設備也通過其pci_dev結(jié)構(gòu)中的bus_list成員鏈入其所在PCI總線的局部設備鏈表中,而這個局部的總線設備鏈表的表頭就由pci_bus結(jié)構(gòu)中的devices成員所描述。?
(5)指針self:指向引出這條PCI總線的橋設備的pci_dev結(jié)構(gòu)。?
(6)資源指針數(shù)組resource[4]:指向應路由到這條pci總線的地址空間資源,通常是指向?qū)獦蛟O備的pci_dev結(jié)構(gòu)中的資源數(shù)組resource[10:7]。?
(7)指針ops:指向一個pci_ops結(jié)構(gòu),表示這條pci總線所使用的配置空間訪問函數(shù)。下一節(jié)將詳細討論這個數(shù)據(jù)結(jié)構(gòu)。?
(8)無類型指針sysdata:指向系統(tǒng)特定的擴展數(shù)據(jù)。?
(9)指針procdir:指向該PCI總線在/proc文件系統(tǒng)中對應的目錄項。?
(10)number:這條PCI總線的總線編號(bus?number),取值范圍0-255。?
(11)primary:表示引出這條PCI總線的“橋設備的主總線”(也即橋設備所在的PCI總線)編號,取值范圍0-255。?
(12)secondary:表示引出這條PCI總線的橋設備的次總線號,因此secondary成員總是等于number成員的值。取值范圍0-255。?
(13)subordinate:這條PCI總線的下屬PCI總線(Subordinate?pci?bus)的總線編號最大值,它應該等于引出這條PCI總線的橋設備的subordinate值。?
(13)name[48]:這條PCI總線的名字字符串。?
(14)vendor和device:表示引出這條PCI總線的橋設備的廠商ID和設備ID。?
(15)serial:系列號。?
(16)pnpver:Plug&Play版本號。?
(17)productver:產(chǎn)品版本號。?
(18)chechsum:pci_bus結(jié)構(gòu)的校驗和,因改為0。?
(19)pad1:為了使pci_bus結(jié)構(gòu)的大小沿內(nèi)存邊界對齊而設立的成員,無實際意義。?
下面對上述結(jié)構(gòu)中的總線編號進行一下說明。假定這樣一個PCI總線體系結(jié)構(gòu):根總線0上有一個PCI橋,它引出子總線bus?1,bus?1上又有一個PCI橋引出bus?2,如下圖2所示:?
(圖2)?
PCI橋的配置空間頭部中定義橋兩側(cè)的主、次總線編號,以及橋后面的下級總線編號的最大可能值。因此在上圖中,bus?0總線的pci_bus結(jié)構(gòu)中的number、primary、secondary都應該為0,因為它是通過Host/PCI橋引出的根總線;而bus?1總線的pci_bus結(jié)構(gòu)中的number=secondary=1,而bus?1的primary應該為0;而bus?2總線的pci_bus結(jié)構(gòu)中的number=secondary=2,其primary則應該等于1。這三條總線的subordinate值都應該等于2。?
§2.3?PCI設備鏈表?
前面已經(jīng)講過,所有的PCI設備都通過其pci_dev結(jié)構(gòu)中的global_list成員鏈接一條“全局pci設備鏈表”pci_devices,表頭pci_devices定義在drivers/pci/pci.c文件中:?
LIST_HEAD(pci_device);?
另外,同屬一條PCI總線上的所有PCI設備也通過其pci_dev結(jié)構(gòu)中的bus_list成員鏈接成一條局部這條PCI總線的“總線設備鏈表”,表頭則由該PCI總線的pci_bus結(jié)構(gòu)中的devices成員所定義。?
下圖3描述可以很清楚地描述可以很清楚地描述上述這兩重鏈表的關(guān)系?
(圖3)?
■遍歷PCI設備鏈表的輔助宏?
為了更為方便地遍歷上述兩類PCI設備鏈表(全局PCI設備鏈表和局部總線設備鏈表),Linux在頭文件中pci.h定義了幾個輔助宏,從而使得遍歷鏈表的代碼更為簡單、易懂。它們是:?
(1)宏pci_for_each_dev(dev):正向遍歷全局設備鏈表pce_devices中的每一個PCI?設備,參數(shù)dev是一個pci_dev結(jié)構(gòu)類型的指針。?其定義如下:?
#define?pci_for_each_dev(dev)?\?
for(dev=pci_dev_g(pci_devices.next);dev!=pci_dev_g(&pci_devices);dev=pci_dev_g(dev->global_list.next))?
上述定義中的宏pci_dev_g()用于將一個list_head類型的指針轉(zhuǎn)換為一個pci_dev類型的指針(下面談及)。對這個宏的典型使用方法如下所示:?
pci_dev?*dev;?
……?
pci_for_each_dev(dev)?
{?
……/*?對dev所指向的每一個pci設備進行處理?*/?
}?
(2)宏pci_for_each_dev_reverse(dev):逆向遍歷全局設備鏈表pci_devices中的每一個pci設備。參數(shù)dev同樣也是一個pci_dev結(jié)構(gòu)類型的指針。其定義如下:?
#define?pci_for_each_dev_reverse(dev)?\?
for(dev=pci_dev_g(pci_devices.prev);dev!=pci_dev_g(&pci_devices);dev=pci_dev_g(dev->global_list.prev))?
顯然,上述宏將從全局設備鏈表中的最后一個元素(由pci_devices.prev所指向)開始逆向遍歷整個鏈表。該宏的使用與pci_for_each_dev()宏相同,只是遍歷方向相反而已。?
(3)宏pci_dev_g(n)和pci_dev_b(n):宏pci_dev_g()用來將與全局設備鏈表對應的list_head類型指針n轉(zhuǎn)換為pci_dev類型的指針,宏pci_dev_b()用來將與總線局部設備鏈表對應的list_head類型指針n轉(zhuǎn)換為pci_dev類型的指針。實際上,它們都是通過list_entry()宏來實現(xiàn)實際的轉(zhuǎn)換工作,如下所示:?
#define?pci_dev_g(n)?list_entry(n,?struct?pci_dev,?global_list)?
#define?pci_dev_b(n)?list_entry(n,?struct?pci_dev,?bus_list)?
§2.4?PCI總線鏈表?
系統(tǒng)中當前存在的所有根總線都通過其pci_bus結(jié)構(gòu)中的node成員鏈接成一條全局的根總線鏈表,其表頭由list類型的全局變量pci_root_buses來描述。它的定義如下(drivers/pci/pci.c):?
LIST_HEAD(pci_root_buses);?
而根總線下面的所有下級總線則都通過其pci_bus結(jié)構(gòu)中的node成員鏈接到其父總線的children鏈表中。這樣,通過這兩種PCI總線鏈表,linux內(nèi)核就將所有的pci_bus結(jié)構(gòu)以一種倒置樹的方式組織起來。假定對于圖4所示的PCI總線體系結(jié)構(gòu),它所對應的總線鏈表結(jié)構(gòu)如圖5所示。?
(圖4)?
(圖5)?
類似地,宏pci_bus_b(n)則被用來將list_head類型的指針n轉(zhuǎn)換為pci_bus類型的指針。該宏也是定義在include/linux/pci.h頭文件中:?
#define?pci_bus_b(n)?list_entry(n,?struct?pci_bus,?node)
PCI總線體系結(jié)構(gòu)是一種層次式的(Hierarchical)體系結(jié)構(gòu)。在這種層次式體系結(jié)構(gòu)中,PCI橋設備占據(jù)著重要的地位,它將父總線與子總線連接在一起,從而使整個系統(tǒng)看起來像一顆倒置的樹型結(jié)構(gòu)。樹的頂端是系統(tǒng)的CPU,它通過一個較為特殊的PCI橋設備——Host/PCI橋設備與根PCI總線(root?pci?bus)連接起來。下圖1是一個較為典型的PCI總線體系結(jié)構(gòu)圖。?
(圖1)?
從上圖也可以看出,作為一種特殊的PCI設備,PCI橋又包括以下幾種:?
(1)Host/PCI橋:用于連接CPU與PCI根總線(注意,the?first?root?bus總是編號為0)。由于像Memory?Controller這樣的設備通常也被集成到Host/PCI橋設備芯片中,因此,Host/PCI橋通常也被稱為“北橋芯片組(North?Bridge?Chipset)”。?
(2)PCI/ISA橋:用于連接遺留的ISA總線。通常,像i8359A中斷控制器這樣的設備也會被集成到PCI/ISA橋設備中,因此,PCI/ISA橋通常也被稱為“南橋芯片組(South?Bridge?Chipset)”。?
(3)PCI-to-PCI橋:用于連接PCI主總線(primary?bus)與次總線(secondary?bus)。PCI橋所處的PCI總線稱為“主總線”(即次總線的父總線),橋設備所連接的PCI總線稱為“次總線”(即主總線的子總線)。?
更多類型的PCI橋分類可以參見《PCI?Local?Bus?Specification》Revision2.2的附錄D。?
本文以下部分假設讀者對PCI總線規(guī)范有一定的了解,因此本文是為那些想要深入了解Linux內(nèi)核是如何實現(xiàn)PCI總線驅(qū)動程序的Kernel?Hacker而寫的。?
§2?PCI總線驅(qū)動程序的核心數(shù)據(jù)結(jié)構(gòu)?
從前面的圖1可以看出,在PCI總線體系結(jié)構(gòu)中,有兩個核心的概念存在:PCI總線和PCI設備(橋設備是一種特殊的PCI設備)?;趯@兩個核心概念的抽象,Linux?PCI總線驅(qū)動程序定義了兩個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):結(jié)構(gòu)類型pci_bus和結(jié)構(gòu)類型pci_dev,以分別描述pci總線和pci設備。在此基礎上,Linux?PCI總線驅(qū)動程序又將系統(tǒng)中當前存在的所有PCI總線的pci_bus結(jié)構(gòu)組織成一張層次式的鏈表圖,顯然該鏈表圖的頂層鏈表是系統(tǒng)中所有根總線的pci_bus結(jié)構(gòu)鏈表,因此用一個list_head結(jié)構(gòu)類型的全局變量pci_root_buses來描述該鏈表圖中的頂層鏈表表頭,也即根總線鏈表的表頭。系統(tǒng)中當前存在的所有PCI設備的pci_dev結(jié)構(gòu)也被組織成一條鏈表,稱為“全局pci設備鏈表”,用一個list_head結(jié)構(gòu)類型的變量pci_devices來表示該鏈表的表頭。通過這兩條總鏈表,Linux內(nèi)核就可以得到一張當前系統(tǒng)的PCI總線體系結(jié)構(gòu)視圖。?
§2.1?PCI設備描述符——pci_dev結(jié)構(gòu)類型?
所有種類的PCI設備都可以用結(jié)構(gòu)類型pci_dev來描述。更為準確地說,應該是每一個PCI功能,即PCI邏輯設備都唯一地對應有一個pci_dev設備描述符。該數(shù)據(jù)結(jié)構(gòu)的定義如下(include/linux/pci.h):?
/*?
*?The?pci_dev?structure?is?used?to?describe?both?PCI?and?ISAPnP?devices.?
*/?
struct?pci_dev?{?
struct?list_head?global_list;?/*?node?in?list?of?all?PCI?devices?*/?
struct?list_head?bus_list;?/*?node?in?per-bus?list?*/?
struct?pci_bus?*bus;?/*?bus?this?device?is?on?*/?
struct?pci_bus?*subordinate;?/*?bus?this?device?bridges?to?*/?
void?*sysdata;?/*?hook?for?sys-specific?extension?*/?
struct?proc_dir_entry?*procent;?/*?device?entry?in?/proc/bus/pci?*/?
unsigned?int?devfn;?/*?encoded?device?&?function?index?*/?
unsigned?short?vendor;?
unsigned?short?device;?
unsigned?short?subsystem_vendor;?
unsigned?short?subsystem_device;?
unsigned?int?class;?/*?3?bytes:?(base,sub,prog-if)?*/?
u8?hdr_type;?/*?PCI?header?type?(`multi'?flag?masked?out)?*/?
u8?rom_base_reg;?/*?which?config?register?controls?the?ROM?*/?
struct?pci_driver?*driver;?/*?which?driver?has?allocated?this?device?*/?
void?*driver_data;?/*?data?private?to?the?driver?*/?
dma_addr_t?dma_mask;?/*?Mask?of?the?bits?of?bus?address?this?
device?implements.?Normally?this?is?
0xffffffff.?You?only?need?to?change?
this?if?your?device?has?broken?DMA?
or?supports?64-bit?transfers.?*/?
/*?device?is?compatible?with?these?IDs?*/?
unsigned?short?vendor_compatible[DEVICE_COUNT_COMPATIBLE];?
unsigned?short?device_compatible[DEVICE_COUNT_COMPATIBLE];?
/*?
*?Instead?of?touching?interrupt?line?and?base?address?registers?
*?directly,?use?the?values?stored?here.?They?might?be?different!?
*/?
unsigned?int?irq;?
struct?resource?resource[DEVICE_COUNT_RESOURCE];?/*?I/O?and?memory?regions?+?expansion?ROMs?*/?
struct?resource?dma_resource[DEVICE_COUNT_DMA];?
struct?resource?irq_resource[DEVICE_COUNT_IRQ];?
char?name[80];?/*?device?name?*/?
char?slot_name[8];?/*?slot?name?*/?
int?active;?/*?ISAPnP:?device?is?active?*/?
int?ro;?/*?ISAPnP:?read?only?*/?
unsigned?short?regs;?/*?ISAPnP:?supported?registers?*/?
int?(*prepare)(struct?pci_dev?*dev);?/*?ISAPnP?hooks?*/?
int?(*activate)(struct?pci_dev?*dev);?
int?(*deactivate)(struct?pci_dev?*dev);?
};?
各成員的含義如下:?
(1)全局鏈表元素global_list:每一個pci_dev結(jié)構(gòu)都通過該成員連接到全局pci設備鏈表pci_devices中。?
(2)總線設備鏈表元素bus_list:每一個pci_dev結(jié)構(gòu)除了鏈接到全局設備鏈表中外,還會通過這個成員連接到其所屬PCI總線的設備鏈表中。每一條PCI總線都維護一條它自己的設備鏈表視圖,以便描述所有連接在該PCI總線上的設備,其表頭由PCI總線的pci_bus結(jié)構(gòu)中的devices成員所描述。?
(3)總線指針bus:指向這個PCI設備所在的PCI總線的pci_bus結(jié)構(gòu)。因此,對于橋設備而言,bus指針將指向橋設備的主總線(primary?bus),也即指向橋設備所在的PCI總線。?
(4)指針subordinate:指向這個PCI設備所橋接的下級總線。這個指針成員僅對橋設備才有意義,而對于一般的非橋PCI設備而言,該指針成員總是為NULL。?
(5)無類型指針sysdata:指向一片特定于系統(tǒng)的擴展數(shù)據(jù)。?
(6)指針procent:指向該PCI設備在/proc文件系統(tǒng)中對應的目錄項。?
(7)devfn:這個PCI設備的設備功能號,也成為PCI邏輯設備號(0-255)。其中bit[7:3]是物理設備號(取值范圍0-31),bit[2:0]是功能號(取值范圍0-7)。?
(8)vendor:這是一個16無符號整數(shù),表示PCI設備的廠商ID。?
(9)device:這是一個16無符號整數(shù),表示PCI設備的設備ID。?
(10)subsystem_vendor:這是一個16無符號整數(shù),表示PCI設備的子系統(tǒng)廠商ID。?
(11)subsystem_device:這是一個16無符號整數(shù),表示PCI設備的子系統(tǒng)設備ID。?
(12)class:32位的無符號整數(shù),表示該PCI設備的類別,其中,bit[7:0]為編程接口,bit[15:8]為子類別代碼,bit[23:16]為基類別代碼,bit[31:24]無意義。顯然,class成員的低3字節(jié)剛好對應與PCI配置空間中的類代碼。?
(13)hdr_type:8位符號整數(shù),表示PCI配置空間頭部的類型。其中,bit[7]=1表示這是一個多功能設備,bit[7]=0表示這是一個單功能設備。Bit[6:0]則表示PCI配置空間頭部的布局類型,值00h表示這是一個一般PCI設備的配置空間頭部,值01h表示這是一個PCI-to-PCI橋的配置空間頭部,值02h表示CardBus橋的配置空間頭部。?
(14)rom_base_reg:8位無符號整數(shù),表示PCI配置空間中的ROM基地址寄存器在PCI配置空間中的位置。ROM基地址寄存器在不同類型的PCI配置空間頭部的位置是不一樣的,對于type?0的配置空間布局,ROM基地址寄存器的起始位置是30h,而對于PCI-to-PCI橋所用的type?1配置空間布局,ROM基地址寄存器的起始位置是38h。?
(15)指針driver:指向這個PCI設備所對應的驅(qū)動程序定義的pci_driver結(jié)構(gòu)。每一個pci設備驅(qū)動程序都必須定義它自己的pci_driver結(jié)構(gòu)來描述它自己。這個數(shù)據(jù)結(jié)構(gòu)將在后面分析。?
(16)無類型指針driver_data:指向驅(qū)動程序為這個PCI設備所分配的一塊私有數(shù)據(jù)區(qū)。通常,設備驅(qū)動程序會為它所支持的每一種設備類型定義一個特定于設備類型的數(shù)據(jù)結(jié)構(gòu),以描述該類型設備的信息。而且,driver會為它所找到的每一個設備實例分配一個特定于該設備類型的數(shù)據(jù)結(jié)構(gòu)實例。指針driver_data一般就用來指向這個有驅(qū)動程序所分配的數(shù)據(jù)結(jié)構(gòu)實例。?
(17)dma_mask:用于DMA的總線地址掩碼,一般來說,這個成員的值是0xffffffff。數(shù)據(jù)類型dma_addr_t定義在include/asm/types.h中,在x86平臺上,dma_addr_t類型就是u32類型。?
(18)vendor_compatible[DEVICE_COUNT_COMPATIBLE]和device_compatible[DEVICE_COUNT_COMPATIBLE]:定義這個PCI設備與哪些設備相兼容。?
(19)無符號的整數(shù)irq:表示這個PCI設備通過哪根IRQ輸入線產(chǎn)生中斷,一般為0-15之間的某個值。?
(20)資源數(shù)組resource[DEVICE_COUNT_RESOURCE]:表示該設備可能用到的資源,包括:I/O斷口區(qū)域、設備內(nèi)存地址區(qū)域以及擴展ROM地址區(qū)域。宏DEVICE_COUNT_DEVICE在pci.h頭文件中被定義為常值12。但是PCI設備通常僅使用這12各資源區(qū)域中的一部分。其中,resource[5:0]分別對應于配置空間中的BAR0-BAR5(注意,橋設備只有BAR0和BAR1),resource[6]對應于配置空間中的ROM基地址寄存器所描述的ROM區(qū)域,而resource[10:7]分別對應于橋設備的地址過濾窗口。?
(21)數(shù)組dma_resource[DEVICE_COUNT_DMA]:用于DMA的資源,DEVICE_COUNT_DMA為2。?
(22)數(shù)組irq_resource[DEVICE_COUNT_IRQ]:用于IRQ的資源,DEVICE_COUNT_IRQ為2。?
(23)name[80]數(shù)組:定義這個PCI邏輯設備的名字字符串。?
(24)slot_name[8]數(shù)組:如果這個PCI邏輯設備是通過PCI插槽連接到PCI總線上的,則slot_name數(shù)組表示該插槽的名字字符串。?
(25)active:被ISAPnP模塊用來表示設備是否被激活。?
(26)ro:被ISAPnP模塊用來表示設備是否為只讀設備。?
(27)regs:被ISAPnP模塊用來表示設備設備所支持的寄存器個數(shù)。?
(28)函數(shù)指針prepare、activate和deactivate:都是僅被ISAPnP模塊鉤掛的函數(shù)指針。?
§2.2?PCI總線描述符——pci_bus結(jié)構(gòu)類型?
該結(jié)構(gòu)定義在include/linux/pci.h頭文件中,如下所示:?
struct?pci_bus?{?
struct?list_head?node;?/*?node?in?list?of?buses?*/?
struct?pci_bus?*parent;?/*?parent?bus?this?bridge?is?on?*/?
struct?list_head?children;?/*?list?of?child?buses?*/?
struct?list_head?devices;?/*?list?of?devices?on?this?bus?*/?
struct?pci_dev?*self;?/*?bridge?device?as?seen?by?parent?*/?
struct?resource?*resource[4];?/*?address?space?routed?to?this?bus?*/?
struct?pci_ops?*ops;?/*?configuration?access?functions?*/?
void?*sysdata;?/*?hook?for?sys-specific?extension?*/?
struct?proc_dir_entry?*procdir;?/*?directory?entry?in?/proc/bus/pci?*/?
unsigned?char?number;?/*?bus?number?*/?
unsigned?char?primary;?/*?number?of?primary?bridge?*/?
unsigned?char?secondary;?/*?number?of?secondary?bridge?*/?
unsigned?char?subordinate;?/*?max?number?of?subordinate?buses?*/?
char?name[48];?
unsigned?short?vendor;?
unsigned?short?device;?
unsigned?int?serial;?/*?serial?number?*/?
unsigned?char?pnpver;?/*?Plug?&?Play?version?*/?
unsigned?char?productver;?/*?product?version?*/?
unsigned?char?checksum;?/*?if?zero?-?checksum?passed?*/?
unsigned?char?pad1;?
};?
各成員的含義如下:?
(1)鏈表元素node:對于PCI根總線而言,其pci_bus結(jié)構(gòu)通過node成員鏈接到本節(jié)一開始所述的根總線鏈表中,根總線鏈表的表頭由一個list_head類型的全局變量pci_root_buses所描述。而對于非根pci總線,其pci_bus結(jié)構(gòu)通過node成員鏈接到其父總線的子總線鏈表children中(見下面)。?
(2)parent指針:指向該pci總線的父總線,即pci橋所在的那條總線。?
(3)children指針:描述了這條PCI總線的子總線鏈表的表頭。這條PCI總線的所有子總線都通過上述的node鏈表元素鏈接成一條子總線鏈表,而該鏈表的表頭就由父總線的children指針所描述。?
(4)devices鏈表頭:描述了這條PCI總線的邏輯設備鏈表的表頭。除了鏈接在全局PCI設備鏈表中之外,每一個PCI邏輯設備也通過其pci_dev結(jié)構(gòu)中的bus_list成員鏈入其所在PCI總線的局部設備鏈表中,而這個局部的總線設備鏈表的表頭就由pci_bus結(jié)構(gòu)中的devices成員所描述。?
(5)指針self:指向引出這條PCI總線的橋設備的pci_dev結(jié)構(gòu)。?
(6)資源指針數(shù)組resource[4]:指向應路由到這條pci總線的地址空間資源,通常是指向?qū)獦蛟O備的pci_dev結(jié)構(gòu)中的資源數(shù)組resource[10:7]。?
(7)指針ops:指向一個pci_ops結(jié)構(gòu),表示這條pci總線所使用的配置空間訪問函數(shù)。下一節(jié)將詳細討論這個數(shù)據(jù)結(jié)構(gòu)。?
(8)無類型指針sysdata:指向系統(tǒng)特定的擴展數(shù)據(jù)。?
(9)指針procdir:指向該PCI總線在/proc文件系統(tǒng)中對應的目錄項。?
(10)number:這條PCI總線的總線編號(bus?number),取值范圍0-255。?
(11)primary:表示引出這條PCI總線的“橋設備的主總線”(也即橋設備所在的PCI總線)編號,取值范圍0-255。?
(12)secondary:表示引出這條PCI總線的橋設備的次總線號,因此secondary成員總是等于number成員的值。取值范圍0-255。?
(13)subordinate:這條PCI總線的下屬PCI總線(Subordinate?pci?bus)的總線編號最大值,它應該等于引出這條PCI總線的橋設備的subordinate值。?
(13)name[48]:這條PCI總線的名字字符串。?
(14)vendor和device:表示引出這條PCI總線的橋設備的廠商ID和設備ID。?
(15)serial:系列號。?
(16)pnpver:Plug&Play版本號。?
(17)productver:產(chǎn)品版本號。?
(18)chechsum:pci_bus結(jié)構(gòu)的校驗和,因改為0。?
(19)pad1:為了使pci_bus結(jié)構(gòu)的大小沿內(nèi)存邊界對齊而設立的成員,無實際意義。?
下面對上述結(jié)構(gòu)中的總線編號進行一下說明。假定這樣一個PCI總線體系結(jié)構(gòu):根總線0上有一個PCI橋,它引出子總線bus?1,bus?1上又有一個PCI橋引出bus?2,如下圖2所示:?
(圖2)?
PCI橋的配置空間頭部中定義橋兩側(cè)的主、次總線編號,以及橋后面的下級總線編號的最大可能值。因此在上圖中,bus?0總線的pci_bus結(jié)構(gòu)中的number、primary、secondary都應該為0,因為它是通過Host/PCI橋引出的根總線;而bus?1總線的pci_bus結(jié)構(gòu)中的number=secondary=1,而bus?1的primary應該為0;而bus?2總線的pci_bus結(jié)構(gòu)中的number=secondary=2,其primary則應該等于1。這三條總線的subordinate值都應該等于2。?
§2.3?PCI設備鏈表?
前面已經(jīng)講過,所有的PCI設備都通過其pci_dev結(jié)構(gòu)中的global_list成員鏈接一條“全局pci設備鏈表”pci_devices,表頭pci_devices定義在drivers/pci/pci.c文件中:?
LIST_HEAD(pci_device);?
另外,同屬一條PCI總線上的所有PCI設備也通過其pci_dev結(jié)構(gòu)中的bus_list成員鏈接成一條局部這條PCI總線的“總線設備鏈表”,表頭則由該PCI總線的pci_bus結(jié)構(gòu)中的devices成員所定義。?
下圖3描述可以很清楚地描述可以很清楚地描述上述這兩重鏈表的關(guān)系?
(圖3)?
■遍歷PCI設備鏈表的輔助宏?
為了更為方便地遍歷上述兩類PCI設備鏈表(全局PCI設備鏈表和局部總線設備鏈表),Linux在頭文件中pci.h定義了幾個輔助宏,從而使得遍歷鏈表的代碼更為簡單、易懂。它們是:?
(1)宏pci_for_each_dev(dev):正向遍歷全局設備鏈表pce_devices中的每一個PCI?設備,參數(shù)dev是一個pci_dev結(jié)構(gòu)類型的指針。?其定義如下:?
#define?pci_for_each_dev(dev)?\?
for(dev=pci_dev_g(pci_devices.next);dev!=pci_dev_g(&pci_devices);dev=pci_dev_g(dev->global_list.next))?
上述定義中的宏pci_dev_g()用于將一個list_head類型的指針轉(zhuǎn)換為一個pci_dev類型的指針(下面談及)。對這個宏的典型使用方法如下所示:?
pci_dev?*dev;?
……?
pci_for_each_dev(dev)?
{?
……/*?對dev所指向的每一個pci設備進行處理?*/?
}?
(2)宏pci_for_each_dev_reverse(dev):逆向遍歷全局設備鏈表pci_devices中的每一個pci設備。參數(shù)dev同樣也是一個pci_dev結(jié)構(gòu)類型的指針。其定義如下:?
#define?pci_for_each_dev_reverse(dev)?\?
for(dev=pci_dev_g(pci_devices.prev);dev!=pci_dev_g(&pci_devices);dev=pci_dev_g(dev->global_list.prev))?
顯然,上述宏將從全局設備鏈表中的最后一個元素(由pci_devices.prev所指向)開始逆向遍歷整個鏈表。該宏的使用與pci_for_each_dev()宏相同,只是遍歷方向相反而已。?
(3)宏pci_dev_g(n)和pci_dev_b(n):宏pci_dev_g()用來將與全局設備鏈表對應的list_head類型指針n轉(zhuǎn)換為pci_dev類型的指針,宏pci_dev_b()用來將與總線局部設備鏈表對應的list_head類型指針n轉(zhuǎn)換為pci_dev類型的指針。實際上,它們都是通過list_entry()宏來實現(xiàn)實際的轉(zhuǎn)換工作,如下所示:?
#define?pci_dev_g(n)?list_entry(n,?struct?pci_dev,?global_list)?
#define?pci_dev_b(n)?list_entry(n,?struct?pci_dev,?bus_list)?
§2.4?PCI總線鏈表?
系統(tǒng)中當前存在的所有根總線都通過其pci_bus結(jié)構(gòu)中的node成員鏈接成一條全局的根總線鏈表,其表頭由list類型的全局變量pci_root_buses來描述。它的定義如下(drivers/pci/pci.c):?
LIST_HEAD(pci_root_buses);?
而根總線下面的所有下級總線則都通過其pci_bus結(jié)構(gòu)中的node成員鏈接到其父總線的children鏈表中。這樣,通過這兩種PCI總線鏈表,linux內(nèi)核就將所有的pci_bus結(jié)構(gòu)以一種倒置樹的方式組織起來。假定對于圖4所示的PCI總線體系結(jié)構(gòu),它所對應的總線鏈表結(jié)構(gòu)如圖5所示。?
(圖4)?
(圖5)?
類似地,宏pci_bus_b(n)則被用來將list_head類型的指針n轉(zhuǎn)換為pci_bus類型的指針。該宏也是定義在include/linux/pci.h頭文件中:?
#define?pci_bus_b(n)?list_entry(n,?struct?pci_bus,?node)
總結(jié)
以上是生活随笔為你收集整理的PCI总线体系结构概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 铂金为什么不保值?列举铂金的几大缺点
- 下一篇: 为什么买首饰千万别买铂金的?原因出乎意料