2008年7月30日 星期三

關於PCI的probing

.

PCI已經是一個被廣泛使用的bus了,很多的硬體都是PCI介面的,當然,現在更流行的是USB介面,如果我們要撰寫PCI bus上的硬體的driver,第一個要面對的問題是,怎麼確定我們的硬體是否存在PCI bus,以及如何去存取它

之前在看比較傳統的OS的driver,在PCI階段就遇到pci probing和configuration的問題,然而現在雖然linux都幫我們做掉了(是的,pci的部份linux都處理掉了,不需自己probe and get configuration,linux傳給你的struct pci_dev中已經有了它幫我們probe好的informatin),不過因為有看過,還是把它記下來吧

PCI的spec目前到2.2,可以抓下來參考,很多pci存取的細節都被定義在PCI的spec中

我著重在PCI的存取部份的介紹,所以若有看不懂的地方,那就必需去查詢PCI的spec了,PCI有define 64 bit的extension,但是在這裡我們只討論32bit的pci

PCI有自己的space,這個space一般稱為pci configuration space,這個space的address是32bit,每個PCI device佔有一塊固定大小64 dwords(256 byte)的空間,而在每個PCI device自己的空間中,其前面16 dwords(64 byte)的欄位都是一樣的,而存取這16個dwords的位址,其實就等於存取PCI device上對應的16個register,PCI device的資訊都存在這些register中

PCI的configuration register layout如下圖所示:



那麼我們就會面臨到兩個問題:
1.如何存取PCI configuration space?
2.每個device的位址為何? 不知道device的位址,我們就沒法存取

要存取PCI有兩種方式,一是透過IO port,另外一個是在得到memory mapped後的address後,就可以直接存取記憶體位址,利用memory mapped access了
但是在還沒獲得memory mapped的address之前,初始我們還是必需透過IO port存取
IO port的存取是間接存取,其必需透過c語言的in和out指令,讀取的時候,將我們要訪問的PCI configuration space中的address寫入(也就是in)0x0cf8這個port,然後就可以從port 0x0c00讀出(也就是out)結果,這種利用IO port的讀取我們先將其命名為IO port read

而在讀出BAR (base address register)這個register中的值後,我們就知道這個PCI device的configuration space被mapping到記憶體空間的哪裡,於是就可以像存取記憶體一樣去存取PCI device了


在知道如何存取PCI configuration space後,接下來的問題是,我們要如何從PCI configuration space中找到我們driver要support的device?

首先,PCI spec中提供了兩種configuration transaction,分別為Type 0和Type 1,Type 0和Type 1的差別可以看spec,而我們主要用到的是Type 1 configuration transaction

Type1 configuration時,其address layout如下圖所示:

所以我們要怎麼利用configuration transaction找出電腦上插了哪些PCI device,是否有我們driver support的PCI device?
很簡單,我們不可能知道該卡的bus number和device number,所以我們只能probe,也就是一個個掃過去,換句話說,就是數學上的窮舉
從bus number = 1 ~ 16 (一般電腦上通常只有一個bus),device number = 1 ~ 32,一個一個利用IO port read去讀出每個device空間上的第一組register,也就是PCI device的Vendor ID和Device ID

若該位址不存在PCI device,則IO port read讀出的值為0xffffffff
反之,該位址存在PCI device,則由於IO port read讀出的值包含Vendor ID和Device ID,如Intel的Vendor ID為0x8086,藉由比會Vendor ID、Device ID、Revision ID等值,很輕易的就能比對出probe到的這個PCI device是否為我們driver想要support的對像了
然後,讀出其BAR register的值,我們就能很輕易的存取該PCI device了

而在舊的OS中(如MS-DOS或pso中),這個動作要我們自己來做,而在linux中,很幸運的,linux都幫你做好了,於是你就可以將心力放在自己的driver上

.

沒有留言: