Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基說明:
《Linux PCI驅(qū)動框架分析(一)》
;話不多說,直接開始。
struct pci_host_bridge
描述;struct pci_dev
描述PCI設(shè)備,以及PCI-to-PCI橋設(shè)備;struct pci_bus
用于描述PCI總線,struct pci_slot
用于描述總線上的物理插槽;來一張更詳細(xì)的結(jié)構(gòu)體組織圖:
pci_host_bridge
,這個結(jié)構(gòu)一般由Host驅(qū)動負(fù)責(zé)來初始化創(chuàng)建;pci_host_bridge
指向root bus,也就是編號為0的總線,在該總線下,可以掛接各種外設(shè)或物理slot,也可以通過PCI橋去擴(kuò)展總線;Linux PCI驅(qū)動框架,基于Linux設(shè)備驅(qū)動模型,因此有必要先簡要介紹一下,實(shí)際上Linux設(shè)備驅(qū)動模型也是一個大的topic,先挖個坑,有空再來填。來張圖吧:
match
函數(shù)),當(dāng)發(fā)現(xiàn)驅(qū)動與設(shè)備能進(jìn)行匹配時,就會執(zhí)行probe函數(shù)的操作;bus_type
會維護(hù)兩個鏈表,分別用于掛接向其注冊的設(shè)備和驅(qū)動,而match
函數(shù)就負(fù)責(zé)匹配檢測;kset/kobject
等內(nèi)容,建議去看看之前的文章《linux設(shè)備模型之kset/kobj/ktype分析》
既然說到了設(shè)備驅(qū)動模型,那么首先我們要做的事情,就是先在內(nèi)核里邊創(chuàng)建一個PCI總線,用于掛接PCI設(shè)備和PCI驅(qū)動,我們的實(shí)現(xiàn)來到了pci_driver_init()
函數(shù):
pci_driver_init()
來創(chuàng)建一個PCI總線結(jié)構(gòu)(全局變量pci_bus_type
),這里描述的PCI總線結(jié)構(gòu),是指驅(qū)動匹配模型中的概念,PCI的設(shè)備和驅(qū)動都會掛在該P(yáng)CI總線上;pci_bus_type
的函數(shù)操作接口也能看出來,pci_bus_match
用來檢查設(shè)備與驅(qū)動是否匹配,一旦匹配了就會調(diào)用pci_device_probe
函數(shù),下邊針對這兩個函數(shù)稍加介紹;pci_bus_match
函數(shù)的調(diào)用,實(shí)際會去比對vendor
和device
等信息,這個都是廠家固化的,在驅(qū)動中設(shè)置成PCI_ANY_ID
就能支持所有設(shè)備;pci_device_probe
的執(zhí)行;枚舉的入口函數(shù):pci_host_probe
pci_scan_root_bus_bridge
開始,首先需要先向系統(tǒng)注冊一個host bridge
,在注冊的過程中需要創(chuàng)建一個root bus
,也就是bus 0
,在pci_register_host_bridge
函數(shù)中,主要是一系列的初始化和注冊工作,此外還為總線分配資源,包括地址空間等;pci_scan_child_bus
開始,從bus 0
向下掃描并添加設(shè)備,這個過程由pci_scan_child_bus_extend
來完成;pci_scan_child_bus_extend
的流程可以看出,主要有兩大塊:
pci_scan_child_bus_extend
的函數(shù)來掃描下一級的總線,從這個過程看,就是一個遞歸過程。Depth First Search
)過程,熟悉數(shù)據(jù)結(jié)構(gòu)與算法的同學(xué)應(yīng)該清楚,這就類似典型的走迷宮的過程;如果你對上述的流程還不清楚,再來一張圖:
暫且寫這么多,細(xì)節(jié)方面不再贅述了,把握大體的框架即可,無法扼住PCI的咽喉,那就扼住它的骨架吧。