Pci設備驅動1:pci設備驅動實例(realtek8168)


Realtek8168網卡時pci接口的網卡,其驅動程序就是一個PCI設備的驅動程序實例,我們一起看看其流程。

1.  首先,初始化模塊調用static inline int pci_register_driver(struct pci_driver *driver)函數來注冊設備驅動,這個函數的參數是struct pci_driver *driver,對應於r8168,就是

static struct pci_driver rtl8168_pci_driver = {

       .name             = MODULENAME,

       .id_table  = rtl8168_pci_tbl,

       .probe            = rtl8168_init_one,

       .remove          = __devexit_p(rtl8168_remove_one),

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)

       .shutdown       = rtl8168_shutdown,

#endif

#ifdef CONFIG_PM

       .suspend  = rtl8168_suspend,

       .resume          = rtl8168_resume,

#endif

};

這個結構體把這個設備驅動所支持的設備(rtl8168_pci_tbl),探測函數(rtl8168_init_one)等都定義好,后面我們將需要用到rtl8168_pci_tbl,rtl8168_init_one兩部分內容來匹配,是否系統中的設備,看是否有設備可以跟這個驅動匹配

2.  pci_register_driver 函數調用__pci_register_driver來完成任務,而__pci_register_driver則重新封裝了要注冊的驅動為PCI總線的,即

int __pci_register_driver(struct pci_driver *drv, struct module *owner)

{

       ……

       drv->driver.bus = &pci_bus_type;

    ……

       drv->driver.kobj.ktype = &pci_driver_kobj_type;

    ……

}

接下來就是調用設備驅動模型的函數,把我們要注冊的驅動掛載到PCI總線的設備隊列上,並掃描PCI總線的設備隊列,查看是否有設備可以匹配這個驅動,這跟usb設備驅動的掛載是一致的,只是這里掛載的是PCI總線,usb掛載的是USB總線,大致的流程是

driver_register()---àbus_add_driver()----àdriver_attach()--à__driver_attach()--àdriver_probe_device()---àdev->bus->probe(),即最后還是調用了2.中的pci_bus_type結構體中的probe成員函數,即static int pci_device_probe(struct device * dev)

3.  static int pci_device_probe(struct device * dev)函數的參數dev就是遍歷了PCI總線上的設備鏈表,一一進行匹配來完成的,因為我們調用__driver_attach()的方式是bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

4.  static int pci_device_probe(struct device * dev)通過兩個宏轉換to_pci_driver,to_pci_dev,獲得需要匹配的設備和驅動,調用static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)函數進行匹配

5.  __pci_device_probe函數首先通過設備驅動中的rtl8168_pci_tbl表,跟從設備獲得vendorID,productID進行比較,看是否一致,如果一致,就返回這個表的地址;如果沒有一致的,就表明,這個設備跟這個驅動不匹配,就不需要繼續進行下面的操作了,直接退出

6.  如果第5步發現了一致的設備表,就表明有設備ID一致,需要進一步探測,接下來就要調用我們設備驅動程序中的探測函數,進行更具體的探測了,即pci_call_probe(drv, pci_dev, id)---à drv->probe(dev, id),到這里,就開始調用我們的設備驅動中的探測函數了。

7.  static int __devinit rtl8168_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)函數是r8168的探測函數,其調用rtl8168_init_board(pdev, &dev, &ioaddr)來完成跟PCI設備驅動相關的探測。

8.  static int __devinit rtl8168_init_board(struct pci_dev *pdev, struct net_device **dev_out, void __iomem **ioaddr_out)函數調用pci_enable_device函數來使能PCI設備,只有使能成功的PCI設備,才能正常使用。

9.  調用pci_set_mwi函數判斷設備是否支持memory-write-invalidate 功能

10.              調用pci_find_capability函數來判斷設備是否有電源管理功能.

11.              調用pci_resource_flags函數來判斷PCI是內存映射模式,還是IO模式

12.              調用pci_resource_len函數來判斷內存空間是否小於設備所需要的內存空間,如果小於,明顯出錯

13.              調用pci_request_regions函數通知內核,當前PCI將使用這些內存地址,其他設備不能再使用了

14.              調用pci_set_master(pdev)函數,設置設備具有獲得總線的能力,即調用這個函數,使設備具備申請使用PCI總線的能力。

15.              調用ioremap函數把剛剛申請的物理內存,映射成虛擬內存,因為進程使用的都是虛擬內存地址,而不是物理內存地址。

16.              把ioremap映射的虛擬內存返回給調用函數。

17.              到此,跟PCI相關的初始化都完成了,設備即可正常工作了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM