亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 55067 | 回復: 66
打印 上一主題 下一主題

網(wǎng)卡驅(qū)動注冊到PCI總線這一過程的分析 [復制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-12-15 22:46 |只看該作者 |倒序瀏覽
大家好,最近在看網(wǎng)絡(luò)部分的代碼,目前看到了網(wǎng)卡的初始化部分。書上講到的內(nèi)容主要是網(wǎng)卡驅(qū)動程序?qū)W(wǎng)卡自身的初始化部分,即網(wǎng)卡驅(qū)動的probe函數(shù)是如何執(zhí)行的,而很少講到網(wǎng)卡是如何注冊到系統(tǒng)中去的這一部分。
    現(xiàn)在的網(wǎng)卡大部分都是連接到PCI總線上的。因此,網(wǎng)卡驅(qū)動是如何連接到PCI總線,又是如何與網(wǎng)卡設(shè)備聯(lián)系起來,網(wǎng)卡在注冊的最后又是如何調(diào)用到該網(wǎng)卡的probe函數(shù)的,這一個過程將在后面的文章中進行描述。整個文章分成兩個部分,第一部分是講解總線、設(shè)備以及驅(qū)動三者的聯(lián)系,為第二部分具體講解PCI總線、網(wǎng)卡設(shè)備和驅(qū)動做一點鋪墊。
    由于我在這方面也是初學,之所以想總結(jié)出來是想到在總結(jié)的過程中對自己的學習也是一個梳理的過程。所以有什么地方寫得不好的,還請各位多多指正,非常感謝!也希望能在這里結(jié)識更多的朋友。

    在總結(jié)的過程中參考了下面一些資料,在此表示感謝:
[1] <Understanding Linux Network Internals>
[2] <Linux那些事兒之我是U盤>
[3] <Essential Linux Device Drivers>
[4] <Linux Device Driver> 3rd Edition.


1. 總線、設(shè)備和驅(qū)動
1.1 簡單介紹
        Linux設(shè)備模型中三個很重要的概念就是總線、設(shè)備和驅(qū)動,即bus,device和driver。它們分別對應的數(shù)據(jù)結(jié)構(gòu)分別為struct bus_type,struct device和struct device_driver。
        總線是處理器與一個或多個設(shè)備之間的通道,在設(shè)備模型中,所有的設(shè)備都通過總線相連。在最底層,Linux系統(tǒng)中的每一個設(shè)備都用device結(jié)構(gòu)的一個實例來表示。而驅(qū)動則是使總線上的設(shè)備能夠完成它應該完成的功能。
        在系統(tǒng)中有多種總線,如PCI總線、SCSI總線等。系統(tǒng)中的多個設(shè)備和驅(qū)動是通過總線讓它們聯(lián)系起來的。在bus_type中兩個很重要的成員就是struct kset drivers和struct kset devices。它分別代表了連接在這個總線上的兩個鏈,一個是設(shè)備鏈表,另一個則是設(shè)備驅(qū)動鏈表。也就是說,通過一個總線描述符,就可以找到掛載到這條總線上的設(shè)備,以及支持該總線的不同的設(shè)備驅(qū)動程序。
1.2 總線、設(shè)備與驅(qū)動的綁定
        在系統(tǒng)啟動時,它會對每種類型的總線創(chuàng)建一個描述符,并將使用該總線的設(shè)備鏈接到該總線描述符的devices鏈上來。也即是說在系統(tǒng)初始化時,它會掃描連接了哪些設(shè)備,并且為每個設(shè)備建立一個struce device變量,然后將該變量鏈接到這個設(shè)備所連接的總線的描述符上去。另一方面,每當加載了一個設(shè)備驅(qū)動,則系統(tǒng)也會準備一個struct device_driver結(jié)構(gòu)的變量,然后再將這個變量也鏈接到它所在總線的描述符的drivers鏈上去。       
        對于設(shè)備來說,在結(jié)構(gòu)體struct device中有兩個重要的成員,一個是struct bus_type *bus,另一個是struct device_driver *driver。bus成員就表示該設(shè)備是鏈接到哪一個總線上的,而driver成員就表示當前設(shè)備是由哪個驅(qū)動程序所驅(qū)動的。對于驅(qū)動程序來說,在結(jié)構(gòu)體struct device_driver中也有兩個成員,struct bus_type *bus和struct list_head devices,這里的bus成員也是指向這個驅(qū)動是鏈接到哪個總線上的,而devices這個鏈表則是表示當前這個驅(qū)動程序可以去進行驅(qū)動的那些設(shè)備。一個驅(qū)動程序可以支持一個或多個設(shè)備,而一個設(shè)備則只會綁定給一個驅(qū)動程序。
        對于device與device_driver之間建立聯(lián)系的方式,主要有兩種方式。第一種,在計算機啟動的時候,總線開始掃描連接在其上的設(shè)備,為每個設(shè)備建立一個struct device變量并鏈接到該總線的devices鏈上,然后開始初始化不同的驅(qū)動程序,驅(qū)動程序到它所在的總線的devices鏈上去遍歷每一個還沒有被綁定給某個驅(qū)動的設(shè)備,然后再查看是否能夠支持這種設(shè)備,如果它能夠支持這種設(shè)備,則將這個設(shè)備與這個驅(qū)動聯(lián)系起來。即,將這個設(shè)備的device變量加到驅(qū)動的devices鏈上,同時讓struct device中的device_driver指向當前這個驅(qū)動。第二種則是熱插拔。也即是在系統(tǒng)運行時插入了設(shè)備,此時內(nèi)核會去查找在該bus鏈上注冊了的device_driver,然后再將設(shè)備與驅(qū)動聯(lián)系起來。設(shè)備與驅(qū)動根據(jù)什么規(guī)則聯(lián)系起來,它們是如何被聯(lián)系起來的代碼我們將在后面的章節(jié)進行詳細的描述。
1.3 PCI總線
        PCI是一種在CPU與I/O設(shè)備之間進行高速數(shù)據(jù)傳輸?shù)囊环N總線。有很多設(shè)備都是使用PCI總線的,網(wǎng)卡就是其中之一。我們在前面講了那些總線、設(shè)備與驅(qū)動方面的知識,原因就在于網(wǎng)卡是連接到PCI總線上,所以PCI總線、網(wǎng)卡設(shè)備以及網(wǎng)卡驅(qū)動就成了我們研究網(wǎng)卡的一個很重要的線索,尤其是在網(wǎng)絡(luò)的鏈路層部分。下圖顯示了在一個系統(tǒng)中PCI設(shè)備的一個框圖:

        PCI子系統(tǒng)聲明了一個bus_type結(jié)構(gòu),為pci_bus_type。它就是PCI總線的描述符。在這個變量上,鏈接了PCI設(shè)備以及支持PCI設(shè)備的驅(qū)動程序。
1.4 PCI設(shè)備與驅(qū)動
        PCI設(shè)備通常由一組參數(shù)唯一地標識,它們被vendorID,deviceID和class nodes所標識,即設(shè)備廠商,型號等,這些參數(shù)保存在pci_device_id結(jié)構(gòu)中。每個PCI設(shè)備都會被分配一個pci_dev變量,內(nèi)核就用這個數(shù)據(jù)結(jié)構(gòu)來表示一個PCI設(shè)備。
        所有的PCI驅(qū)動程序都必須定義一個pci_driver結(jié)構(gòu)變量,在該變量中包含了這個PCI驅(qū)動程序所提供的不同功能的函數(shù),同時,在這個結(jié)構(gòu)中也包含了一個device_driver結(jié)構(gòu),這個結(jié)構(gòu)定義了PCI子系統(tǒng)與PCI設(shè)備之間的接口。在注冊PCI驅(qū)動程序時,這個結(jié)構(gòu)將被初始化,同時這個pci_driver變量會被鏈接到pci_bus_type中的驅(qū)動鏈上去。
        在pci_driver中有一個成員struct pci_device_id *id_table,它列出了這個設(shè)備驅(qū)動程序所能夠處理的所有PCI設(shè)備的ID值。
1.5 PCI設(shè)備與驅(qū)動的綁定過程
        下面描述一下對于PCI設(shè)備與驅(qū)動綁定的過程。首先在系統(tǒng)啟動的時候,PCI總線會去掃描連接到這個總線上的設(shè)備,同時為每一個設(shè)備建立一個pci_dev結(jié)構(gòu),在這個結(jié)構(gòu)中有一個device成員,并將這些pci_dev結(jié)構(gòu)鏈接到PCI總線描述符上的devices鏈。如下圖所示:

        第二步是當PCI驅(qū)動被加載時,pci_driver結(jié)構(gòu)體將被初始化,這一過程在函數(shù)pci_register_driver中:
        drv->driver.bus = &pci_bus_type;
        drv->driver.probe = pci_device_probe;
        最后會調(diào)用driver_register(&drv->driver)將這個PCI驅(qū)動掛載到總線描述符的驅(qū)動鏈上。同時在注冊的過程中,會根據(jù)pci_driver中的id_table中的ID值去查看該驅(qū)動支持哪些設(shè)備,將這些設(shè)備掛載到pci_driver中的devices鏈中來。如下圖所示:

        對于不同的設(shè)備,可能驅(qū)動程序也不一樣,因此,對于上圖中的Dev3,可能就需要另外一個驅(qū)動程序來對其進行驅(qū)動。所以當加載了Dev3的驅(qū)動程序時,其示意圖如下圖所示:

        上面這三個示意圖就描述了總線、設(shè)備以及驅(qū)動在系統(tǒng)中是如何進行相互聯(lián)系的。前面對于驅(qū)動注冊這些函數(shù)的描述較為簡單,因為網(wǎng)卡是一個PCI設(shè)備,因此在后面具體地講到網(wǎng)卡注冊時再來詳細地講解和PCI相關(guān)的注冊等函數(shù)。
1.6 小結(jié)
        本部分主要講解了總線、設(shè)備以及驅(qū)動方面的一些知識,由于網(wǎng)卡是一個PCI設(shè)備,因此具體地講到了一點PCI總線、PCI設(shè)備及相應的PCI驅(qū)動方面的知識,但是由于PCI本身就是很大的一個子系統(tǒng),因此這里不可能對其進行詳細地講解,在后面對網(wǎng)卡的分析中,將對網(wǎng)卡中涉及到的和PCI相關(guān)的部分進行講解。

評分

參與人數(shù) 2可用積分 +21 收起 理由
OraBSD + 6 精彩!
Godbach + 15 原創(chuàng)內(nèi)容

查看全部評分

論壇徽章:
0
2 [報告]
發(fā)表于 2008-12-15 22:47 |只看該作者
2. 網(wǎng)卡在PCI層的注冊
2.1 數(shù)據(jù)結(jié)構(gòu)
        前面第一章講了總線、設(shè)備以及驅(qū)動方面的關(guān)系,也講到了大多數(shù)網(wǎng)卡設(shè)備實際上是一個PCI設(shè)備。因此,本章就講解網(wǎng)卡設(shè)備在注冊時是如何注冊到PCI總線上去的。在這里,以Intel的E100網(wǎng)卡驅(qū)動進行講解。
        前面講到每個PCI設(shè)備都由一組參數(shù)唯一地標識,這些參數(shù)保存在結(jié)構(gòu)體pci_device_id中,如下所示:

  1. struct pci_device_id {
  2.         __u32 vendor, device;                /* Vendor and device ID or PCI_ANY_ID*/
  3.         __u32 subvendor, subdevice;        /* Subsystem ID's or PCI_ANY_ID */
  4.         __u32 class, class_mask;        /* (class,subclass,prog-if) triplet */
  5.         kernel_ulong_t driver_data;        /* Data private to the driver */
  6. };
復制代碼

        每個PCI設(shè)備驅(qū)動都有一個pci_driver變量,它描述了一個PCI驅(qū)動的信息,如下所示:

  1. struct pci_driver {
  2.         struct list_head node;
  3.         char *name;
  4.         const struct pci_device_id *id_table;        /* must be non-NULL for probe to be called */
  5.         int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);        /* New device inserted */
  6.         void (*remove) (struct pci_dev *dev);        /* Device removed (NULL if not a hot-plug capable driver) */
  7.         int  (*suspend) (struct pci_dev *dev, pm_message_t state);        /* Device suspended */
  8.         int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
  9.         int  (*resume_early) (struct pci_dev *dev);
  10.         int  (*resume) (struct pci_dev *dev);                        /* Device woken up */
  11.         int  (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);   /* Enable wake event */
  12.         void (*shutdown) (struct pci_dev *dev);

  13.         struct pci_error_handlers *err_handler;
  14.         struct device_driver        driver;
  15.         struct pci_dynids dynids;

  16.         int multithread_probe;
  17. };
復制代碼

        每個PCI驅(qū)動中都有一個id_table成員變量,記錄了當前這個驅(qū)動所能夠進行驅(qū)動的那些設(shè)備的ID值。
        對于E100網(wǎng)卡驅(qū)動來說,它的pci_driver變量定義為:

  1. static struct pci_driver e100_driver = {
  2.         .name =         DRV_NAME,
  3.         .id_table =     e100_id_table,
  4.         .probe =        e100_probe,
  5.         .remove =       __devexit_p(e100_remove),
  6. #ifdef CONFIG_PM
  7.         /* Power Management hooks */
  8.         .suspend =      e100_suspend,
  9.         .resume =       e100_resume,
  10. #endif
  11.         .shutdown =     e100_shutdown,
  12.         .err_handler = &e100_err_handler,
  13. };
復制代碼

        里面e100_id_table就表示該E100驅(qū)動所能夠支持的PCI設(shè)備的ID號,其定義為:

  1. #define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
  2.         PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
  3.         PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich }
  4. static struct pci_device_id e100_id_table[] = {
  5.         INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
  6.         INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),
  7.         …
  8.         { 0, }
  9. };
復制代碼

        當PCI層檢測到一個PCI設(shè)備能夠被某PCI驅(qū)動所支持時(這是通過函數(shù)pci_match_one_device來進行檢測的),就會調(diào)用這個PCI驅(qū)動上的probe函數(shù),在該函數(shù)中會對該特定的PCI設(shè)備進行一些具體的初始化等操作。比如對于E100設(shè)備驅(qū)動來說,其probe函數(shù)為e100_probe。在這個函數(shù)中,會對網(wǎng)卡設(shè)備進行初始化。
        e100_probe主要就涉及到網(wǎng)卡設(shè)備net_device的初始化,我們現(xiàn)在先來關(guān)注一下從網(wǎng)卡注冊一直到調(diào)用e100_probe這一個過程的整個流程。
2.2 E100初始化
        E100驅(qū)動程序的初始化是在函數(shù)e100_init_module()中的,如下:

  1. static int __init e100_init_module(void)
  2. {
  3.         if(((1 << debug) - 1) & NETIF_MSG_DRV) {
  4.                 printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
  5.                 printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT);
  6.         }
  7.         return pci_register_driver(&e100_driver);
  8. }
復制代碼

        在這個函數(shù)中,調(diào)用了pci_register_driver()函數(shù),對e100_driver這個驅(qū)動進行注冊。
2.3 PCI注冊
        在前面我們已經(jīng)看到,PCI的注冊就是將PCI驅(qū)動程序掛載到其所在的總線的drivers鏈,同時掃描PCI設(shè)備,將它能夠進行驅(qū)動的設(shè)備掛載到driver上的devices鏈表上來,這里,我們將詳細地查看這整個流程的函數(shù)調(diào)用關(guān)系。
        pci_register_driver()->__pci_register_driver()

  1. /**
  2. * __pci_register_driver - register a new pci driver
  3. * @drv: the driver structure to register
  4. * @owner: owner module of drv
  5. * @mod_name: module name string
  6. *
  7. * Adds the driver structure to the list of registered drivers.
  8. * Returns a negative value on error, otherwise 0.
  9. * If no error occurred, the driver remains registered even if
  10. * no device was claimed during registration.
  11. */       
  12. int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name);
  13.         在函數(shù)中有幾個初始化語句:
  14.         drv->driver.name = drv->name;
  15.         drv->driver.bus = &pci_bus_type;
  16.         drv->driver.owner = owner;
  17.         drv->driver.mod_name = mod_name;
復制代碼

        即是將PCI設(shè)備中的driver變量的總線指向pci_bus_type這個總線描述符,同時設(shè)置驅(qū)動的名字等。
        pci_bus_type定義如下:

  1. struct bus_type pci_bus_type = {
  2.         .name                = "pci",
  3.         .match                = pci_bus_match,
  4.         .uevent                = pci_uevent,
  5.         .probe                = pci_device_probe,
  6.         .remove                = pci_device_remove,
  7.         .suspend        = pci_device_suspend,
  8.         .suspend_late        = pci_device_suspend_late,
  9.         .resume_early        = pci_device_resume_early,
  10.         .resume                = pci_device_resume,
  11.         .shutdown        = pci_device_shutdown,
  12.         .dev_attrs        = pci_dev_attrs,
  13. };
復制代碼

        然后再調(diào)用函數(shù)driver_register(&drv->driver);通過這個函數(shù)將這個PCI驅(qū)動中的struct device_driver driver成員變量注冊到系統(tǒng)中去。
        pci_register_driver()->__pci_register_driver()->driver_register()
        driver_register()代碼如下:

  1. /**
  2. *        driver_register - register driver with bus
  3. *        @drv:        driver to register
  4. *
  5. *        We pass off most of the work to the bus_add_driver() call,
  6. *        since most of the things we have to do deal with the bus
  7. *        structures.
  8. *
  9. *        The one interesting aspect is that we setup @drv->unloaded
  10. *        as a completion that gets complete when the driver reference
  11. *        count reaches 0.
  12. */
  13. int driver_register(struct device_driver * drv)
  14. {
  15.         if ((drv->bus->probe && drv->probe) ||
  16.             (drv->bus->remove && drv->remove) ||
  17.             (drv->bus->shutdown && drv->shutdown)) {
  18.                 printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
  19.         }
  20.         klist_init(&drv->klist_devices, NULL, NULL);
  21.         init_completion(&drv->unloaded);
  22.         return bus_add_driver(drv);
  23. }
復制代碼

        klist_init()是為設(shè)備驅(qū)動的klist_devices成員進行初始化,這個klist_devices是一個對鏈表進行操作的包裹結(jié)構(gòu),它會鏈接這個驅(qū)動能夠支持的那些設(shè)備。
        最后就調(diào)用bus_add_driver()函數(shù)。這個函數(shù)的功能就是將這個驅(qū)動加到其所在的總線的驅(qū)動鏈上。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()
        在bus_add_driver()函數(shù)中,最重要的是調(diào)用driver_attach()函數(shù),其定義如下:

  1. /**
  2. *        driver_attach - try to bind driver to devices.
  3. *        @drv:        driver.
  4. *
  5. *        Walk the list of devices that the bus has on it and try to
  6. *        match the driver with each one.  If driver_probe_device()
  7. *        returns 0 and the @dev->driver is set, we've found a
  8. *        compatible pair.
  9. */
  10. int driver_attach(struct device_driver * drv)
  11. {
  12.         return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  13. }
復制代碼

        該函數(shù)遍歷這個驅(qū)動所在的總線上的所有設(shè)備,然后將這些設(shè)備與當前驅(qū)動進行匹配,以檢測這個驅(qū)動是否能夠支持某個設(shè)備,也即是將設(shè)備與驅(qū)動聯(lián)系起來。
        bus_for_each_dev函數(shù)是掃描在drv->bus這個總線上的所有設(shè)備,然后將每個設(shè)備以及當前驅(qū)動這兩個指針傳遞給__driver_attach函數(shù)。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()
        __driver_attach()函數(shù)是將驅(qū)動與設(shè)備聯(lián)系起來的函數(shù)。

  1. static int __driver_attach(struct device * dev, void * data)
  2. {
  3.         struct device_driver * drv = data;

  4.         /*
  5.          * Lock device and try to bind to it. We drop the error
  6.          * here and always return 0, because we need to keep trying
  7.          * to bind to devices and some drivers will return an error
  8.          * simply if it didn't support the device.
  9.          *
  10.          * driver_probe_device() will spit a warning if there
  11.          * is an error.
  12.          */

  13.         if (dev->parent)        /* Needed for USB */
  14.                 down(&dev->parent->sem);
  15.         down(&dev->sem);
  16.         if (!dev->driver)
  17.                 driver_probe_device(drv, dev);
  18.         up(&dev->sem);
  19.         if (dev->parent)
  20.                 up(&dev->parent->sem);

  21.         return 0;
  22. }
復制代碼

        在函數(shù)中有兩條語句:

  1.         if (!dev->driver)
  2.                 driver_probe_device(drv, dev);
復制代碼

        也即是判斷當前設(shè)備是否已經(jīng)注冊了一個驅(qū)動,如果沒有注冊驅(qū)動,則調(diào)用driver_probe_device()函數(shù)。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()
        如下:

  1. /**
  2. * driver_probe_device - attempt to bind device & driver together
  3. * @drv: driver to bind a device to
  4. * @dev: device to try to bind to the driver
  5. *
  6. * First, we call the bus's match function, if one present, which should
  7. * compare the device IDs the driver supports with the device IDs of the
  8. * device. Note we don't do this ourselves because we don't know the
  9. * format of the ID structures, nor what is to be considered a match and
  10. * what is not.
  11. *
  12. * This function returns 1 if a match is found, an error if one occurs
  13. * (that is not -ENODEV or -ENXIO), and 0 otherwise.
  14. *
  15. * This function must be called with @dev->sem held.  When called for a
  16. * USB interface, @dev->parent->sem must be held as well.
  17. */
  18. int driver_probe_device(struct device_driver * drv, struct device * dev)
  19. {
  20.         struct stupid_thread_structure *data;
  21.         struct task_struct *probe_task;
  22.         int ret = 0;

  23.         if (!device_is_registered(dev))
  24.                 return -ENODEV;
  25.         if (drv->bus->match && !drv->bus->match(dev, drv))
  26.                 goto done;

  27.         pr_debug("%s: Matched Device %s with Driver %s\n",
  28.                  drv->bus->name, dev->bus_id, drv->name);

  29.         data = kmalloc(sizeof(*data), GFP_KERNEL);
  30.         if (!data)
  31.                 return -ENOMEM;
  32.         data->drv = drv;
  33.         data->dev = dev;

  34.         if (drv->multithread_probe) {
  35.                 probe_task = kthread_run(really_probe, data,
  36.                                          "probe-%s", dev->bus_id);
  37.                 if (IS_ERR(probe_task))
  38.                         ret = really_probe(data);
  39.         } else
  40.                 ret = really_probe(data);

  41. done:
  42.         return ret;
  43. }       
復制代碼

        該函數(shù)首先會調(diào)用總線上的match函數(shù),以判斷當前的PCI驅(qū)動能否支持該PCI設(shè)備,如果可以,則繼續(xù)往后面執(zhí)行。
        drv->bus->match函數(shù)也即是pci_bus_type中的match成員變量,它為pci_bus_match函數(shù)。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->pci_bus_match()

  1. /**
  2. * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
  3. * @dev: the PCI device structure to match against
  4. * @drv: the device driver to search for matching PCI device id structures
  5. *
  6. * Used by a driver to check whether a PCI device present in the
  7. * system is in its list of supported devices. Returns the matching
  8. * pci_device_id structure or %NULL if there is no match.
  9. */
  10. static int pci_bus_match(struct device *dev, struct device_driver *drv)
  11. {
  12.         struct pci_dev *pci_dev = to_pci_dev(dev);
  13.         struct pci_driver *pci_drv = to_pci_driver(drv);
  14.         const struct pci_device_id *found_id;

  15.         found_id = pci_match_device(pci_drv, pci_dev);
  16.         if (found_id)
  17.                 return 1;

  18.         return 0;
  19. }
復制代碼

        pci_bus_match函數(shù)的作用就是將PCI設(shè)備與PCI驅(qū)動進行比較以檢查該驅(qū)動是否能夠支持這個設(shè)備。在函數(shù)的最前面是兩個宏to_pci_dev和to_pci_driver。因為在函數(shù)執(zhí)行的過程中,雖然最開始傳進來的是pci_driver結(jié)構(gòu)與pci_dev結(jié)構(gòu),但是在執(zhí)行的時候卻取了這兩個結(jié)構(gòu)體中的device_driver和device成員變量,所以現(xiàn)在就要通過這兩個成員變量找到之前對應的pci_driver和pci_dev結(jié)構(gòu)的地址。
#define        to_pci_dev(n) container_of(n, struct pci_dev, dev)
#define        to_pci_driver(drv) container_of(drv,struct pci_driver, driver)
        這兩個宏在<Linux Device Driver> 3rd書上有相應的講解,這里也就是找到E100的pci_driver:e100_driver以及該網(wǎng)卡設(shè)備的pci_dev結(jié)構(gòu),F(xiàn)在就要對它們進行比較以看它們之間是否能夠聯(lián)系起來。這是通過函數(shù)pci_match_device實現(xiàn)的。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->pci_bus_match()->pci_match_device()

  1. /**
  2. * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
  3. * @drv: the PCI driver to match against
  4. * @dev: the PCI device structure to match against
  5. *
  6. * Used by a driver to check whether a PCI device present in the
  7. * system is in its list of supported devices.  Returns the matching
  8. * pci_device_id structure or %NULL if there is no match.
  9. */
  10. const struct pci_device_id *pci_match_device(struct pci_driver *drv,
  11.                                              struct pci_dev *dev)
  12. {
  13.         struct pci_dynid *dynid;

  14.         /* Look at the dynamic ids first, before the static ones */
  15.         spin_lock(&drv->dynids.lock);
  16.         list_for_each_entry(dynid, &drv->dynids.list, node) {
  17.                 if (pci_match_one_device(&dynid->id, dev)) {
  18.                         spin_unlock(&drv->dynids.lock);
  19.                         return &dynid->id;
  20.                 }
  21.         }
  22.         spin_unlock(&drv->dynids.lock);

  23.         return pci_match_id(drv->id_table, dev);
  24. }
復制代碼

        pci_match_one_driver函數(shù)的作用是將一個PCI設(shè)備與PCI驅(qū)動進行比較,以查看它們是否相匹配。如果相匹配,則返回匹配的pci_device_id結(jié)構(gòu)體指針。
        此時,如果該PCI驅(qū)動已經(jīng)找到了一個可以想符的PCI設(shè)備,則返回,然后再退回到之前的driver_probe_device函數(shù)中。在該函數(shù)最后將調(diào)用really_probe函數(shù)。將device_driver與device結(jié)構(gòu)體指針作為參數(shù)傳遞到這個函數(shù)中。下面幾行是調(diào)用驅(qū)動或者總線的probe函數(shù)來掃描設(shè)備。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()
        在函數(shù)really_probe()中:

  1.         if (dev->bus->probe) {
  2.                 ret = dev->bus->probe(dev);
  3.                 if (ret)
  4.                         goto probe_failed;
  5.         } else if (drv->probe) {
  6.                 ret = drv->probe(dev);
  7.                 if (ret)
  8.                         goto probe_failed;
  9.         }
復制代碼

        此時的dev->bus為pci_bus_type,其probe函數(shù)則對應為:pci_device_probe。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()
        同樣,在該函數(shù)中會獲得當前的PCI設(shè)備的pci_dev結(jié)構(gòu)體指針以及PCI驅(qū)動程序的pci_driver結(jié)構(gòu)體指針。分別使用宏to_pci_dev和to_pci_driver。最后則調(diào)用函數(shù)__pci_device_probe。在該函數(shù)中還會調(diào)用函數(shù)pci_call_probe,這是最后的函數(shù)
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci_device_probe()->pci_call_probe()
        在函數(shù)pci_call_probe里有一條語句:

  1. static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
  2.                           const struct pci_device_id *id)
  3. {
  4.         int error;
  5. /*  省略  */
  6.         error = drv->probe(dev, id);
復制代碼

        在此處就調(diào)用了pci_driver的probe函數(shù),對于這里的E100驅(qū)動來說,它的probe函數(shù)是最開始注冊的e100_probe函數(shù),在該函數(shù)中會完成對網(wǎng)卡設(shè)備net_device的初始化等操作。

  1.         pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->pci_device_probe()->__pci_device_probe()->pci_call_probe()->e100_probe()
復制代碼

        到這里,我們對網(wǎng)卡驅(qū)動的PCI層的初始化分析就告一個段落了,剩下的部分就是網(wǎng)卡驅(qū)動對網(wǎng)卡設(shè)備本身的初始化等操作。

論壇徽章:
0
3 [報告]
發(fā)表于 2008-12-15 22:48 |只看該作者
2.4 函數(shù)調(diào)用流程圖
        在這里,為網(wǎng)卡在PCI層的注冊畫了一個函數(shù)調(diào)用的流程圖,能夠更直觀地展現(xiàn)網(wǎng)卡從注冊到調(diào)用其自身的網(wǎng)卡初始化的這一個函數(shù)調(diào)用過程。

論壇徽章:
0
4 [報告]
發(fā)表于 2008-12-15 22:52 |只看該作者
文筆實在不行,寫了一個下午加晚上,才寫這么點。希望能夠?qū)Τ鯇W者有一點用吧。因為我最開始在看網(wǎng)卡驅(qū)動的時候,就是迷惑加載了網(wǎng)卡之后是如何調(diào)用到該網(wǎng)卡的probe函數(shù)的。所以就仔細地看了一下里面的源碼。

這里主要還是起一個梳理的作用,很多代碼也沒有進一步地深入分析。不過對于網(wǎng)絡(luò)架構(gòu)來說,首先將整個調(diào)用的流程掌握了,對后面的理解也就更加方便了。

論壇徽章:
36
IT運維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
5 [報告]
發(fā)表于 2008-12-15 22:54 |只看該作者
scutan兄又有好文章了,明天仔細拜讀啊。

論壇徽章:
0
6 [報告]
發(fā)表于 2008-12-15 23:10 |只看該作者
補充一下,貼一下open函數(shù)的調(diào)用的筆記:

86, ifconfig eth0 up 會導致 net_device->open被調(diào)用,內(nèi)幕!
       
            # strace ifconfig eth0 up 2>&1 |less -N
   
    可以看到,它是先用sockfd = socket(AF_INET, SOCK_DGRAM, 0)生成一個sockfd文件描述符,
    再ioctl(sockfd, SIOCSIFFLAGS, 加上IFF_UP標志)。 這樣就導致了open方法的調(diào)用。

    socket文件描述符都是用socket(2)系統(tǒng)調(diào)用生成的:
           
        sys_socket() > sock_map_fd() > sock_attach_fd() :

                dentry->d_op = &sockfs_dentry_operations;
                ...

                init_file(file, sock_mnt, dentry, FMODE_READ|FMODE_WRITE, &socket_file_ops);
                SOCK_INODE(sock)->i_fop = &socket_file_ops;
       
        (回憶一下,這個是不是就類似于ext3_iget()里頭對inode->i_fop的賦值?)


                static const struct file_operations socket_file_ops = {
                        .owner =        THIS_MODULE,
                        .llseek =        no_llseek,
                        .aio_read =        sock_aio_read,
                        .aio_write =        sock_aio_write,
                        .poll =                sock_poll,
                        .unlocked_ioctl = sock_ioctl,
                #ifdef CONFIG_COMPAT
                        .compat_ioctl = compat_sock_ioctl,
                #endif
                        .mmap =                sock_mmap,
                        .open =                sock_no_open,        /* special open code to disallow open via /proc */
                        .release =        sock_close,
                        .fasync =        sock_fasync,
                        .sendpage =        sock_sendpage,
                        .splice_write = generic_splice_sendpage,
                        .splice_read =        sock_splice_read,
                }

        其unlocked_ioctl = sock_ioctl,那么我們沿著sock_ioctl走下去:
               
                sock_ioctl() --switch到了default--> dev_ioctl() > --SIOCSIFFLAGS--> dev_ifsioc()
                > dev_change_flags() :
                       
                        if ((old_flags ^ flags) & IFF_UP) {IFF_UP/* Bit is different  ? */
                                ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);

        如果是設(shè)置了IFF_UP,就調(diào)用dev_open;如果是清除了IFF_UP,就調(diào)用dev_close。 看dev_open里的:
               
                ret = dev->open(dev);
       
        就在此時,struct net_device的open方法被調(diào)用。

論壇徽章:
0
7 [報告]
發(fā)表于 2008-12-15 23:11 |只看該作者
2). remove函數(shù)何時被調(diào)用?
            
            當pci_dev消失時(設(shè)備被拔出),或者module被rmmod時。

            pci_unregister_driver() > driver_unregister() > driver_detach() > __device_release_driver():

                if (dev->bus && dev->bus->remove)
                        dev->bus->remove(dev);
                else if (drv->remove)
                        drv->remove(dev);
          
            對pci設(shè)備來說,這里的dev->bus就是&pci_bus_type,參考1)中的定義,我們知道其remove函數(shù)是
            pci_device_remove():

                        struct pci_dev * pci_dev = to_pci_dev(dev);
                        struct pci_driver * drv = pci_dev->driver;

                        if (drv) {
                                if (drv->remove)
                                        drv->remove(pci_dev);
                                pci_dev->driver = NULL;
                        }

論壇徽章:
0
8 [報告]
發(fā)表于 2008-12-15 23:11 |只看該作者
[FYI] 增加/刪除一個PCI device時的情景。
              (只有boot時的enumeration和hotplug兩種情況可能導致設(shè)備出現(xiàn)與消失)


              pci device的發(fā)現(xiàn):

                      [ pci_scan_slot() > pci_scan_single_device() > pci_scan_device()
                                                             > pci_device_add() ]


                pci_bus_add_devices() > pci_bus_add_device() > device_add() > bus_attach_device() :
                       
                        int device_attach(struct device *dev)
                        {
                                int ret = 0;

                                down(&dev->sem);
                                if (dev->driver) {
                                        ret = device_bind_driver(dev);
                                        if (ret == 0)
                                                ret = 1;
                                        else {
                                                dev->driver = NULL;
                                                ret = 0;
                                        }
                                } else {
                                        ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
                                }
                                up(&dev->sem);
                                return ret;
                        }

                也就是說,如果已經(jīng)有了dev->driver這個值,那么就直接bind上去;如果沒有,那么:
                       
                        bus_for_each_drv() > __device_attach() > driver_probe_device() > really_probe()

                此后發(fā)生的情形就和從pci_register_driver()一直調(diào)用到really_probe()的一樣了。


        remove:
        =======
                pci_remove_bus_device() > pci_destroy_dev() > pci_stop_dev() > device_unregister() >
                device_del() > bus_remove_device() > device_release_driver() > __device_release_driver() :

                        static void __device_release_driver(struct device *dev)
                        {
                                struct device_driver *drv;

                                drv = dev->driver;
                                if (drv) {
                                        driver_sysfs_remove(dev);
                                        sysfs_remove_link(&dev->kobj, "driver");

                                        if (dev->bus)
                                                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                                                             BUS_NOTIFY_UNBIND_DRIVER,
                                                                             dev);

                                        if (dev->bus && dev->bus->remove)
                                                dev->bus->remove(dev);
                                        else if (drv->remove)
                                                drv->remove(dev);
                                        devres_release_all(dev);
                                        dev->driver = NULL;
                                        klist_remove(&dev->knode_driver);
                                }
                        }

                注意,如果可以,首先嘗試調(diào)用pci_bus_type的remove方法(a.k.a pci_device_remove),否則調(diào)用
                device_driver的remove方法。

論壇徽章:
0
9 [報告]
發(fā)表于 2008-12-15 23:27 |只看該作者

謝謝albcamus版主精彩的補充。

論壇徽章:
0
10 [報告]
發(fā)表于 2008-12-16 08:48 |只看該作者

回復 #1 scutan 的帖子

實踐的結(jié)論是最有說服力的
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP