PCI設(shè)備驅(qū)動(二)

Linux PCI設(shè)備驅(qū)動代碼必須掃描系統(tǒng)中所有的PCI總線,尋找系統(tǒng)中所有的PCI設(shè)備(包括PCI-PCI橋設(shè)備)。系統(tǒng)中的每條PCI總線都有個編號number,根PCI總線的編號為0。

一、總線鏈表

1、根總線鏈表( pci_root_buses )

  • 系統(tǒng)當(dāng)前存在的所有根總線(因為可能存在不止一個Host/PCI橋,那么就可能存在多條根總線) 都通過其pci_bus結(jié)構(gòu)體中的node成員鏈接成一個全局的根總線鏈表,其表頭由struct list_head類型的全局變量pci_root_buses描述。

我們在/linux-2.4.18/linux/drivers/pci/pci.c的38行可以看到如下定義:LIST_HEAD(pci_root_buses);

2、子總線鏈表(children)

  • 根總線下面的所有下級總線則都通過其pci_bus結(jié)構(gòu)體中的node成員鏈接到其父總線的children鏈表中。

這樣,通過這兩種PCI總線鏈表,Linux內(nèi)核就將所有的pci_bus結(jié)構(gòu)體以一種倒置樹的方式組織起來。

二、PCI設(shè)備鏈表

每個PCI設(shè)備都由一個pci_dev結(jié)構(gòu)體表示,每個pci_dev結(jié)構(gòu)體都同時連入兩個隊列:

  • 一方面通過其成員global_list掛入一個總的pci_dev結(jié)構(gòu)隊列(隊列頭是pci_devices);
  • 另一方面又通過成員bus_list掛入其所在總線的pci_dev結(jié)構(gòu)隊列devices(隊列頭是pci_bus.devices,即該pci設(shè)備所在的pci總線的devices隊列),并且使指針bus(指pci_dev結(jié)構(gòu)體里的bus成員)指向代表著其所在總線的pci_bus結(jié)構(gòu)。
  • 如果設(shè)備是PCI-PCI橋,則還要使其指針subordinate指向代表著另一條PCI總線的pci_bus結(jié)構(gòu)。同樣我們在/linux-2.4.18/linux/drivers/pci/pci.c的39行可以看到如下定義:LIST_HEAD(pci_devices);

對于PCI設(shè)備鏈表,我們可以對照在"PCI設(shè)備驅(qū)動(二)"中貼出的PCI系統(tǒng)結(jié)構(gòu)示意圖,具體理解Linux內(nèi)核中對應(yīng)的數(shù)據(jù)結(jié)構(gòu)。
圖片發(fā)自簡書App

三、PCI驅(qū)動程序初始化

1、深度優(yōu)先算法構(gòu)建設(shè)備樹

Linux PCI初始化代碼從PCI總線0開始掃描,它通過讀取"Vendor ID"和"Device ID"來試圖發(fā)現(xiàn)每一個插槽上的設(shè)備。

  • 如果發(fā)現(xiàn)了一個PCI-PCI橋,則創(chuàng)建一個pci_bus數(shù)據(jù)結(jié)構(gòu)并且連入到由pci_root_buses指向的pci_bus和pci_dev數(shù)據(jù)結(jié)構(gòu)組成的樹中。PCI初始化代碼通過設(shè)備類代碼0x060400來判斷一個PCI設(shè)備是否是PCI-PCI橋。

  • 然后Linux核心開始構(gòu)造這個橋設(shè)備另一端的PCI總線和其上的設(shè)備。如果還發(fā)現(xiàn)了橋設(shè)備,就以同樣的步驟來進行構(gòu)建。
    這個處理過程稱之為深度優(yōu)先算法。

PCI-PCI橋橫跨在兩條總線之間,寄存器PCI_PRIMARY_BUS和PCI_SECONDARY_BUS的內(nèi)容就說明了其上下兩端的總線號:

  • PCI_SECONDARY_BUS就是該PCI-PCI橋所連接和控制的總線,
  • PCI_SUBORDINATE_BUS則說明自此以下、在以此為根的子樹中最大的總線號是什么。
    我們可以在/linux-2.4.18/linux/include/linux/pci.h看到如下定義:
112:  /* Header type 1 (PCI-to-PCI bridges) */

113:  #define PCI_PRIMARY_BUS           0x18       /* Primary bus number */

114:  #define PCI_SECONDARY_BUS     0x19       /* Secondary bus number */

115:  #define PCI_SUBORDINATE_BUS  0x1a       /* Highest bus number behind the bridge */

2、內(nèi)核對設(shè)備的尋址方式

  • 由于在枚舉階段做的是深度優(yōu)先掃描,所以子樹中的總線號總是連續(xù)遞增的。當(dāng)CPU往I/O寄存器0xCF8中寫入一個綜合地址以后,從0號總線開始,每個PCI-PCI橋會把綜合地址中的總線號與自身的總線號相比,如果相符就用邏輯設(shè)備號在本總線上尋訪目標(biāo)設(shè)備;

  • 如果綜合地址中的總線號與自己不相符,就進一步把這個總線號與PCI_SUBORDINATE_BUS中的內(nèi)容相比,如果目標(biāo)總線號落在當(dāng)前子樹范圍中,就把綜合地址傳遞給其下的各個次層PCI-PCI橋,要不然就不予理睬。
    這樣,最終就會找到目標(biāo)設(shè)備。

當(dāng)然,這個過程只是在PCI設(shè)備的配置階段需要這樣做,一旦配置完成,CPU就直接通過有關(guān)的總線地址訪問目標(biāo)設(shè)備了。

3、PCI橋的窗口

PCI-PCI橋要想正確傳遞對PCI I/O,PCI Memory或PCI Configuration地址空間的讀和寫請求,必須知道下列信息:

  1. Primary Bus Number(主總線號)
    該PCI-PCI橋所處的PCI總線稱為主總線。
  2. Secondary Bus Number(子總線號)
    該PCI-PCI橋所連接的PCI總線稱為子總線/次總線號。
  3. Subordinate Bus Number
    PCI總線的下屬PCI總線的總線編號最大值。

(1)什么是PCI橋窗口

PCI橋的配置寄存器與一般的PCI設(shè)備不同。

  • 一般PCI設(shè)備可以有6個地址區(qū)間,外加一個ROM區(qū)間,代表著設(shè)備上實際存在的存儲器或寄存器區(qū)間。
  • PCI橋設(shè)備,本身并不一定有存儲器或寄存器區(qū)間,但是卻有三個用于地址過濾的區(qū)間。每個地址過濾區(qū)間決定了一個地址窗口,從CPU一側(cè)發(fā)出的地址,如果落在PCI橋的某個窗口內(nèi),就可以穿過PCI橋而到達其所連接的總線上。

(2)PCI橋窗口的開關(guān)

  • PCI橋的命令寄存器中有”memory access enable”和”I/O access enable”的兩個控制位,當(dāng)這兩個控制位為0時,這些窗口就全都關(guān)上了。

(3)PCI橋窗口的作用

在未完成對PCI總線的初始化之前,還沒有為PCI設(shè)備上的各個區(qū)間分配合適的總線地址時,正是因為這兩個控制位為0,才不會對CPU一側(cè)造成干擾。

  • 對于PCI設(shè)備驅(qū)動(一)中的 PCI系統(tǒng)示意圖 ,僅當(dāng)讀和寫請求中的PCI I/O或PCI memory地址屬于SCSI或Ethernet設(shè)備時,PCI-PCI橋才將這些總線上的請求從PCI總線0傳遞到PCI總線1。這種過濾機制可以避免地址在系統(tǒng)中沒必要的繁衍。
  • 為了做到這點,每個PCI-PCI橋必須正確地被設(shè)置好它所負責(zé)的PCI I/O或PCI memory的起始地址和大小。當(dāng)一個讀或?qū)懻埱蟮刂仿湓谄湄撠?zé)的范圍之內(nèi),這個請求將被映射到次級的PCI總線上。系統(tǒng)中的PCI-PCI橋一旦設(shè)置完畢,如果Linux中的設(shè)備驅(qū)動程序存取的PCI I/O和PCI memory地址落在在這些窗口之內(nèi),那么這些PCI-PCI橋就是透明的。
  • 這是個很重要的特性,使得Linux PCI設(shè)備驅(qū)動程序開發(fā)者的工作容易些。

4、PCI橋的窗口的配置

問題:配置一個PCI-PCI橋的時候,并不知道這個PCI-PCI橋的subordinate bus number。那么就不知道該PCI橋下面是否還有其他的PCI-PCI橋。即使你知道,也不清楚如何對它們賦值。

方法:利用上述的深度掃描算法來掃描每個總線。每當(dāng)發(fā)現(xiàn)PCI-PCI橋就對它進行賦值。當(dāng)發(fā)現(xiàn)一個PCI-PCI橋時,可以確定它的secondary bus number。然后我們暫時先將其subordinate bus number賦值為0xFF。緊接著,開始掃描該PCI-PCI橋的downstream橋。

過程:這個過程看起來有點復(fù)雜,下面的例子將給出清晰的解釋:

(1)第一步

以下圖的拓撲結(jié)構(gòu)為例:

  • 掃描時首先發(fā)現(xiàn)的橋是Bridge1。Bridge 1的downstream PCI總線號碼被賦值1。自然該橋的secondary bus number也是1。其subordinate bus number暫時賦值為0xFF。上述賦值的含義是所有類型1的含有PCI總線1或更高(<255)的號碼的PCI配置地址將被Bridge 1傳遞到PCI總線1上。
  • 如果PCI總線號是1,Bridge 1 還負責(zé)將配置地址的類型轉(zhuǎn)換成類型0(對于這里說的類型0和類型1,請參考PCI設(shè)備驅(qū)動(一))。否則,就不做轉(zhuǎn)換。上述動作就是開始掃描總線1時Linux PCI初始化代碼所完成的對總線0的配置工作。
    配置PCI系統(tǒng) 第一步

(2)第二步

  • 由于Linux PCI設(shè)備驅(qū)動使用深度優(yōu)先算法進行掃描,所以初始化代碼開始掃描總線1。從而Bridge 2被發(fā)現(xiàn)。
  • 因為在Bridge 2下面發(fā)現(xiàn)不再有PCI-PCI橋,所以Bridge 2的subordinate bus number是2,等于它的secondary bus number。下圖顯示了在這個時刻總線和PCI-PCI橋的賦值情況。
    配置PCI系統(tǒng) 第二步

(3)第三步

  • Linux PCI設(shè)備驅(qū)動代碼從總線2的掃描中回來接著進行掃描總線1,發(fā)現(xiàn)Bridge 3。
  • 它的primary bus number被賦值為1,secondary bus number為3。因為總線3上還發(fā)現(xiàn)了PCI-PCI橋,所以Bridge 3的subordinate bus number暫時賦值0xFF。
  • 下圖顯示了這個時刻系統(tǒng)配置的狀態(tài)。到目前為止,含有總線號1,2,3的類型1的PCI配置都可以正確地傳送到相應(yīng)的總線上。
    配置PCI系統(tǒng) 第三步

(4)第四步

  • 現(xiàn)在Linux開始掃描PCI總線3,Bridge 3的downstream。PCI總線3上有另外一個PCI-PCI橋,Bridge 4。因此Bridge 4的primary bus number的值為3,secondary bus number為4。
  • 由于Bridge 4下面沒有別的橋設(shè)備,所以Bridge 4的subordinate bus number為4。
  • 然后回到PCI-PCI Bridge 3。這時就將Bridge 3的subordinate bus number從0xFF改為4,表示總線4是從Bridge 3往下走的最遠的PCI-PCI橋。
  • 最后,Linux PCI設(shè)備驅(qū)動代碼將4以同樣的道理賦值給Bridge 1的subordinate bus number。下圖反映了系統(tǒng)最后的狀態(tài)。
    配置PCI系統(tǒng) 第四步
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 首先要明確兩個概念:Linux內(nèi)核 PCI設(shè)備驅(qū)動和設(shè)備本身驅(qū)動兩部分。工作中所謂的編寫設(shè)備驅(qū)動,其實就是編寫設(shè)備...
    Leon_Geo閱讀 3,583評論 0 6
  • 總線 計算機的各個功能部件通過總線連接在一起構(gòu)成完整的計算機系統(tǒng),總線是多個系統(tǒng)功能部件之間進行數(shù)據(jù)傳送的公共通路...
    羅蓁蓁閱讀 5,335評論 0 10
  • (轉(zhuǎn))從PC總線到ARM的內(nèi)部總線轉(zhuǎn)自:http://blog.chinaunix.net/u1/34474/sh...
    spfanlost閱讀 1,486評論 0 2
  • 昨晚,在大學(xué)班級微信群里發(fā)現(xiàn)一個噩耗,一大學(xué)同學(xué)被酒駕的司機撞到,因肇事司機逃逸沒有及時報警,同學(xué)不治身亡。他的愛...
    黃家小妞閱讀 153評論 0 0
  • 01 與鄧先生的相識回憶起來極其漫長,在一起之前是一個素未謀面的人。或許是我喜歡有一點小腱子肉的男生的原因,看到他...
    一只小桃子閱讀 497評論 5 2

友情鏈接更多精彩內(nèi)容