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

Chinaunix

標題: 網(wǎng)卡驅動注冊到PCI總線這一過程的分析 [打印本頁]

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

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


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

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

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

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

        上面這三個示意圖就描述了總線、設備以及驅動在系統(tǒng)中是如何進行相互聯(lián)系的。前面對于驅動注冊這些函數(shù)的描述較為簡單,因為網(wǎng)卡是一個PCI設備,因此在后面具體地講到網(wǎng)卡注冊時再來詳細地講解和PCI相關的注冊等函數(shù)。
1.6 小結
        本部分主要講解了總線、設備以及驅動方面的一些知識,由于網(wǎng)卡是一個PCI設備,因此具體地講到了一點PCI總線、PCI設備及相應的PCI驅動方面的知識,但是由于PCI本身就是很大的一個子系統(tǒng),因此這里不可能對其進行詳細地講解,在后面對網(wǎng)卡的分析中,將對網(wǎng)卡中涉及到的和PCI相關的部分進行講解。
作者: scutan    時間: 2008-12-15 22:47
2. 網(wǎng)卡在PCI層的注冊
2.1 數(shù)據(jù)結構
        前面第一章講了總線、設備以及驅動方面的關系,也講到了大多數(shù)網(wǎng)卡設備實際上是一個PCI設備。因此,本章就講解網(wǎng)卡設備在注冊時是如何注冊到PCI總線上去的。在這里,以Intel的E100網(wǎng)卡驅動進行講解。
        前面講到每個PCI設備都由一組參數(shù)唯一地標識,這些參數(shù)保存在結構體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設備驅動都有一個pci_driver變量,它描述了一個PCI驅動的信息,如下所示:

  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驅動中都有一個id_table成員變量,記錄了當前這個驅動所能夠進行驅動的那些設備的ID值。
        對于E100網(wǎng)卡驅動來說,它的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驅動所能夠支持的PCI設備的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設備能夠被某PCI驅動所支持時(這是通過函數(shù)pci_match_one_device來進行檢測的),就會調(diào)用這個PCI驅動上的probe函數(shù),在該函數(shù)中會對該特定的PCI設備進行一些具體的初始化等操作。比如對于E100設備驅動來說,其probe函數(shù)為e100_probe。在這個函數(shù)中,會對網(wǎng)卡設備進行初始化。
        e100_probe主要就涉及到網(wǎng)卡設備net_device的初始化,我們現(xiàn)在先來關注一下從網(wǎng)卡注冊一直到調(diào)用e100_probe這一個過程的整個流程。
2.2 E100初始化
        E100驅動程序的初始化是在函數(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這個驅動進行注冊。
2.3 PCI注冊
        在前面我們已經(jīng)看到,PCI的注冊就是將PCI驅動程序掛載到其所在的總線的drivers鏈,同時掃描PCI設備,將它能夠進行驅動的設備掛載到driver上的devices鏈表上來,這里,我們將詳細地查看這整個流程的函數(shù)調(diào)用關系。
        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設備中的driver變量的總線指向pci_bus_type這個總線描述符,同時設置驅動的名字等。
        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驅動中的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()是為設備驅動的klist_devices成員進行初始化,這個klist_devices是一個對鏈表進行操作的包裹結構,它會鏈接這個驅動能夠支持的那些設備。
        最后就調(diào)用bus_add_driver()函數(shù)。這個函數(shù)的功能就是將這個驅動加到其所在的總線的驅動鏈上。
        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ù)遍歷這個驅動所在的總線上的所有設備,然后將這些設備與當前驅動進行匹配,以檢測這個驅動是否能夠支持某個設備,也即是將設備與驅動聯(lián)系起來。
        bus_for_each_dev函數(shù)是掃描在drv->bus這個總線上的所有設備,然后將每個設備以及當前驅動這兩個指針傳遞給__driver_attach函數(shù)。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()
        __driver_attach()函數(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);
復制代碼

        也即是判斷當前設備是否已經(jīng)注冊了一個驅動,如果沒有注冊驅動,則調(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驅動能否支持該PCI設備,如果可以,則繼續(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設備與PCI驅動進行比較以檢查該驅動是否能夠支持這個設備。在函數(shù)的最前面是兩個宏to_pci_dev和to_pci_driver。因為在函數(shù)執(zhí)行的過程中,雖然最開始傳進來的是pci_driver結構與pci_dev結構,但是在執(zhí)行的時候卻取了這兩個結構體中的device_driver和device成員變量,所以現(xiàn)在就要通過這兩個成員變量找到之前對應的pci_driver和pci_dev結構的地址。
#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)卡設備的pci_dev結構。現(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設備與PCI驅動進行比較,以查看它們是否相匹配。如果相匹配,則返回匹配的pci_device_id結構體指針。
        此時,如果該PCI驅動已經(jīng)找到了一個可以想符的PCI設備,則返回,然后再退回到之前的driver_probe_device函數(shù)中。在該函數(shù)最后將調(diào)用really_probe函數(shù)。將device_driver與device結構體指針作為參數(shù)傳遞到這個函數(shù)中。下面幾行是調(diào)用驅動或者總線的probe函數(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設備的pci_dev結構體指針以及PCI驅動程序的pci_driver結構體指針。分別使用宏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驅動來說,它的probe函數(shù)是最開始注冊的e100_probe函數(shù),在該函數(shù)中會完成對網(wǎng)卡設備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)卡驅動的PCI層的初始化分析就告一個段落了,剩下的部分就是網(wǎng)卡驅動對網(wǎng)卡設備本身的初始化等操作。
作者: scutan    時間: 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)用過程。


作者: scutan    時間: 2008-12-15 22:52
文筆實在不行,寫了一個下午加晚上,才寫這么點。希望能夠對初學者有一點用吧。因為我最開始在看網(wǎng)卡驅動的時候,就是迷惑加載了網(wǎng)卡之后是如何調(diào)用到該網(wǎng)卡的probe函數(shù)的。所以就仔細地看了一下里面的源碼。

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


作者: Godbach    時間: 2008-12-15 22:54
scutan兄又有好文章了,明天仔細拜讀啊。
作者: albcamus    時間: 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);

        如果是設置了IFF_UP,就調(diào)用dev_open;如果是清除了IFF_UP,就調(diào)用dev_close。 看dev_open里的:
               
                ret = dev->open(dev);
       
        就在此時,struct net_device的open方法被調(diào)用。
作者: albcamus    時間: 2008-12-15 23:11
2). remove函數(shù)何時被調(diào)用?
            
            當pci_dev消失時(設備被拔出),或者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設備來說,這里的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;
                        }
作者: albcamus    時間: 2008-12-15 23:11
[FYI] 增加/刪除一個PCI device時的情景。
              (只有boot時的enumeration和hotplug兩種情況可能導致設備出現(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方法。
作者: scutan    時間: 2008-12-15 23:27

謝謝albcamus版主精彩的補充。
作者: kns1024wh    時間: 2008-12-16 08:48
標題: 回復 #1 scutan 的帖子
實踐的結論是最有說服力的
作者: dreamice    時間: 2008-12-16 09:51
這貼牛,高手匯集。受益匪淺
作者: Godbach    時間: 2008-12-16 10:04
標題: 回復 #11 dreamice 的帖子
是啊。先讀一遍,然后再向scutan和albcamus兄請教。
作者: Godbach    時間: 2008-12-16 10:56
補充一下,貼一下open函數(shù)的調(diào)用的筆記:


大家要學習albcamus版主看書做筆記的好習慣啊。偶以前就沒這個習慣,理解的東西沒過多久就又忘了。
作者: Arthur_    時間: 2008-12-16 13:25
五體投地
作者: scutan    時間: 2008-12-16 13:38
謝謝各位。
作者: Godbach    時間: 2008-12-16 14:09
通讀一遍,寫的很好。流程比較明確。

整個流程應該是使用了兩個probe函數(shù)。一個是pci_bus_type的probe函數(shù)指針,指向pci_device_probe。
另一個就是驅動程序自己的probe函數(shù),指向e100_probe,該函數(shù)在一下代碼中調(diào)用。完成網(wǎng)卡的具體初始化工作。

static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
                          const struct pci_device_id *id)
{
        int error;
/*  省略  */
        error = drv->probe(dev, id);

作者: Godbach    時間: 2008-12-16 14:16
不同的總線注冊不同的struct bus_type結構體。隨后在同一總線上注冊多個設備驅動的時候,它們對應同一個struct bus_type。
作者: Godbach    時間: 2008-12-16 14:33
一個小問題:
從內(nèi)核代碼中看到USB設備時使用usb_register注冊USB設備,同時使用usb自身的bus_type。那這意味著USB總線是和PCI總線屬于兩種不同的總線。
但為什么在pci_ids.h中,有這樣的定義:
#define PCI_BASE_CLASS_SERIAL                0x0c
#define PCI_CLASS_SERIAL_FIREWIRE        0x0c00
#define PCI_CLASS_SERIAL_ACCESS                0x0c01
#define PCI_CLASS_SERIAL_SSA                0x0c02
#define PCI_CLASS_SERIAL_USB                0x0c03
#define PCI_CLASS_SERIAL_USB_UHCI        0x0c0300
#define PCI_CLASS_SERIAL_USB_OHCI        0x0c0310
#define PCI_CLASS_SERIAL_USB_EHCI        0x0c0320
#define PCI_CLASS_SERIAL_FIBER                0x0c04
#define PCI_CLASS_SERIAL_SMBUS                0x0c05


MS在PCI中有USB類的設備。這個該怎么理解。偶的硬件學得不好,熟悉的朋友能否幫忙解釋一下?
作者: Godbach    時間: 2008-12-16 16:44
LZ說明一下內(nèi)核版本號吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:
作者: scutan    時間: 2008-12-16 16:55
原帖由 Godbach 于 2008-12-16 16:44 發(fā)表
LZ說明一下內(nèi)核版本號吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:


我的是2.6.21
作者: scutan    時間: 2008-12-16 16:56
原帖由 Godbach 于 2008-12-16 16:44 發(fā)表
LZ說明一下內(nèi)核版本號吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:


driver_probe_device 函數(shù)

  1. int driver_probe_device(struct device_driver * drv, struct device * dev)
  2. {
  3.         struct stupid_thread_structure *data;
  4.         struct task_struct *probe_task;
  5.         int ret = 0;

  6.         if (!device_is_registered(dev))
  7.                 return -ENODEV;
  8.         if (drv->bus->match && !drv->bus->match(dev, drv))
  9.                 goto done;

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

  12.         data = kmalloc(sizeof(*data), GFP_KERNEL);
  13.         if (!data)
  14.                 return -ENOMEM;
  15.         data->drv = drv;
  16.         data->dev = dev;

  17.         if (drv->multithread_probe) {
  18.                 probe_task = kthread_run(really_probe, data,
  19.                                          "probe-%s", dev->bus_id);
  20.                 if (IS_ERR(probe_task))
  21.                         ret = really_probe(data);
  22.         } else
  23.                 ret = really_probe(data);

  24. done:
  25.         return ret;
  26. }

復制代碼

作者: Godbach    時間: 2008-12-16 17:00
2.6.18.3中dd.c.
函數(shù)driver_probe_device代碼中dev->bus->probe對應的函數(shù)為pci_device_probe,隨后調(diào)用關系為:->__pci_device_probe->pci_call_probe
該函數(shù)里面執(zhí)行了 drv->probe,即e100驅動注冊的e100_probe()
driver_probe_device函數(shù)的實現(xiàn):
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
        int ret = 0;

        if (drv->bus->match && !drv->bus->match(dev, drv))
                goto Done;

        pr_debug("%s: Matched Device %s with Driver %s\n",
                 drv->bus->name, dev->bus_id, drv->name);
        dev->driver = drv;
        if (dev->bus->probe) {
                ret = dev->bus->probe(dev);
                if (ret) {
                        dev->driver = NULL;
                        goto ProbeFailed;
                }
        } else if (drv->probe) {
                ret = drv->probe(dev);
                if (ret) {
                        dev->driver = NULL;
                        goto ProbeFailed;
                }
        }
        device_bind_driver(dev);
        ret = 1;
        pr_debug("%s: Bound Device %s to Driver %s\n",
                 drv->bus->name, dev->bus_id, drv->name);
        goto Done;

ProbeFailed:
        if (ret == -ENODEV || ret == -ENXIO) {
                /* Driver matched, but didn't support device
                 * or device not found.
                 * Not an error; keep going.
                 */
                ret = 0;
        } else {
                /* driver matched but the probe failed */
                printk(KERN_WARNING
                       "%s: probe of %s failed with error %d\n",
                       drv->name, dev->bus_id, ret);
        }
Done:
        return ret;
}


pci_call_probe函數(shù)的實現(xiàn)
static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
                          const struct pci_device_id *id)
{
        int error;
#ifdef CONFIG_NUMA
        /* Execute driver initialization on node where the
           device's bus is attached to.  This way the driver likely
           allocates its local memory on the right node without
           any need to change it. */
        struct mempolicy *oldpol;
        cpumask_t oldmask = current->cpus_allowed;
        int node = pcibus_to_node(dev->bus);
        if (node >= 0 && node_online(node))
            set_cpus_allowed(current, node_to_cpumask(node));
        /* And set default memory allocation policy */
        oldpol = current->mempolicy;
        current->mempolicy = &default_policy;
        mpol_get(current->mempolicy);
#endif
        error = drv->probe(dev, id);
#ifdef CONFIG_NUMA
        set_cpus_allowed(current, oldmask);
        mpol_free(current->mempolicy);
        current->mempolicy = oldpol;
#endif
        return error;
}

[ 本帖最后由 Godbach 于 2008-12-16 17:04 編輯 ]
作者: Godbach    時間: 2008-12-16 17:07
標題: 回復 #21 scutan 的帖子
代碼是變化了。應該是2.6.21的代碼里面把driver_probe_device()函數(shù)的實現(xiàn)進行了封裝。

[ 本帖最后由 Godbach 于 2008-12-16 17:09 編輯 ]
作者: qinjiana0786    時間: 2008-12-16 19:16
分析的過程很精彩,lz辛苦了。

[ 本帖最后由 qinjiana0786 于 2009-3-6 17:02 編輯 ]
作者: Godbach    時間: 2008-12-16 20:51
標題: 回復 #24 qinjiana0786 的帖子
非常歡迎qinjiana0786兄來完成更詳細的分析啊。

LZ在這篇文章的開篇也說了,這里整理出來一個流程,提供一個主線,幫著梳理一下。這樣可以幫助希望自己熟悉和分析代碼,但又不知道如何下手的朋友。
作者: eexplorer    時間: 2008-12-17 09:19
原帖由 Godbach 于 2008-12-16 14:33 發(fā)表
一個小問題:
從內(nèi)核代碼中看到USB設備時使用usb_register注冊USB設備,同時使用usb自身的bus_type。那這意味著USB總線是和PCI總線屬于兩種不同的總線。
但為什么在pci_ids.h中,有這樣的定義:


MS在PCI ...


這些是usb host controller,是一個pci device(可通過lspci找到), 和usb host controller 通信的usb devices才是掛在usb bus上的
作者: Godbach    時間: 2008-12-17 09:42
原帖由 eexplorer 于 2008-12-17 09:19 發(fā)表


這些是usb host controller,是一個pci device(可通過lspci找到), 和usb host controller 通信的usb devices才是掛在usb bus上的


多謝LS。那應該還有一些USB Device是不同USB host controller通信的,這樣的設備是怎么工作的呢?
作者: bbaqq222    時間: 2008-12-17 09:58
感謝高人的講解,認真琢磨一下
作者: scutan    時間: 2008-12-17 13:40
原帖由 qinjiana0786 于 2008-12-16 19:16 發(fā)表
分析的過程是正確的,但是在內(nèi)核的PCI的過程上要是展開下就很好,不要只是用圖片或者函數(shù)的調(diào)用路線來形容,畢竟內(nèi)核的部分不是能用只言片語就能一筆帶過的,核心部分還是圍繞著PCI的過程詳細一下就太精彩了,如 ...


小弟在硬件這塊目前還沒有精力去深入下去,所以希望qinjiana0786兄能夠多多指點一下。
謝謝。

BTW: 我從您的TCP/IP核心過程分析中學到了很多東西,表示感謝!
作者: eexplorer    時間: 2008-12-17 17:33
原帖由 Godbach 于 2008-12-17 09:42 發(fā)表


多謝LS。那應該還有一些USB Device是不同USB host controller通信的,這樣的設備是怎么工作的呢?


不太明白你的問題,USB device應該只有通過USB host controller才能和CPU交互,
作者: Godbach    時間: 2008-12-17 17:49
和usb host controller 通信的usb devices才是掛在usb bus上的


對這句話我的理解是,可能還有一些不通過usb host controller 通信的usb devices。

看來我的理解能力很弱
作者: ller    時間: 2008-12-18 22:55
標題: 回復 #3 scutan 的帖子
網(wǎng)卡驅動模塊是什么時候加載的,是根據(jù)udev規(guī)則嗎,我沒有找到相應規(guī)則,還是起網(wǎng)絡服務時加載的,我在運行級別1上仍然可以lsmod到我的網(wǎng)卡驅動模塊
作者: ller    時間: 2008-12-18 23:38
標題: 回復 #32 ller 的帖子
在網(wǎng)終的啟動腳本上看到了對ethN的modprobe操作,然后根據(jù)modprobe.conf中的alias找到相應的驅動
作者: albcamus    時間: 2008-12-19 12:56
原帖由 qinjiana0786 于 2008-12-16 19:16 發(fā)表
分析的過程是正確的,但是在內(nèi)核的PCI的過程上要是展開下就很好,不要只是用圖片或者函數(shù)的調(diào)用路線來形容,畢竟內(nèi)核的部分不是能用只言片語就能一筆帶過的,核心部分還是圍繞著PCI的過程詳細一下就太精彩了,如 ...


linux有的看、有的用嗎? 每個人就寫了那么一點, 拼湊起來的
作者: OraBSD    時間: 2008-12-20 23:59
標題: 回復 #34 albcamus 的帖子

作者: emmoblin    時間: 2008-12-21 21:19
好貼啊,我也是不清楚怎么調(diào)用到probe的。
努力以后我也能寫幾篇加精的文章。努力。。。。
作者: Godbach    時間: 2008-12-21 22:22
原帖由 emmoblin 于 2008-12-21 21:19 發(fā)表
好貼啊,我也是不清楚怎么調(diào)用到probe的。
努力以后我也能寫幾篇加精的文章。努力。。。。


期待emmoblin兄多發(fā)幾篇精品文章上來啊。
作者: hb12112    時間: 2008-12-23 15:46
精彩!
作者: efr6    時間: 2008-12-23 18:42
thank you!
作者: WFCJZ    時間: 2008-12-24 01:01
搞懂了,整死電信,叫它給人玩網(wǎng)卡綁定這招,最煩人!



http://eblog.cersp.com/UploadFiles/2007-5/59797326.swf
作者: ops    時間: 2008-12-24 12:44
好東西
作者: wylhistory    時間: 2009-01-14 20:09
學習了!
作者: fly6    時間: 2009-02-12 11:15
終于弄懂了內(nèi)核何時調(diào)用驅動程序的probe函數(shù)
哈哈,謝謝樓主
作者: fly6    時間: 2009-04-08 11:33
PCI模塊已加載了, 但在/prco/devices中看不到此模塊的信息
這樣我就沒辦法mknod /dev/mypci 建立設備節(jié)點了,用戶空間程序也就沒辦法訪問了
而模塊的信息如下
/sys/bus/pci/devices/0000:01:02.0/
|-- bus -> ../../../../bus/pci
|-- class
|-- config
|-- device
|-- driver -> ../../../../bus/pci/drivers/hifn_kma_drv
|-- irq
|-- local_cpus
|-- modalias
|-- power
|   |-- state
|   `-- wakeup
|-- resource
|-- resource0
|-- resource1
|-- resource2
|-- subsystem_device
|-- subsystem_vendor
|-- uevent
`-- vendor

[root@localhost 0000:01:02.0]# cat device
0x001d

====================
請問如何在/dev/下面建立起與此PCI設備相關的設備節(jié)點
作者: Godbach    時間: 2009-04-08 11:37
原帖由 fly6 于 2009-4-8 11:33 發(fā)表
PCI模塊已加載了, 但在/prco/devices中看不到此模塊的信息
這樣我就沒辦法mknod /dev/mypci 建立設備節(jié)點了,用戶空間程序也就沒辦法訪問了
而模塊的信息如下
/sys/bus/pci/devices/0000:01:02.0/
|-- bus - ...


PCI設備還需要mknod嗎。LZ了解一下網(wǎng)卡采用什么方式?
作者: fly6    時間: 2009-04-08 12:46
呵呵,PCI設備不用mknod嗎?我憐不清楚
是一塊加速卡,不是網(wǎng)卡
作者: Godbach    時間: 2009-04-08 12:55
原帖由 fly6 于 2009-4-8 12:46 發(fā)表
呵呵,PCI設備不用mknod嗎?我憐不清楚
是一塊加速卡,不是網(wǎng)卡

網(wǎng)卡就是標準的PCI設備。它不適用mknod的方式。PCI設別通常是采用probe的方式探測,然后分配一個結構體。以后操作這個結構體就可以對設備進行操作了。
作者: fly6    時間: 2009-04-08 14:35
但用戶程序怎么使用這個結構體呢?
作者: Godbach    時間: 2009-04-08 16:13
建議看一下LDD3中PCI和網(wǎng)絡設備這兩章內(nèi)容
作者: duanius    時間: 2009-04-19 15:13
最近在看設備模型   這篇文章寫的好  把ldd第14章添加設備一節(jié)給捋了一遍   感謝燕姿

[ 本帖最后由 duanius 于 2009-4-19 15:14 編輯 ]
作者: accessory    時間: 2009-04-24 23:21
不錯. 學習下.

PS: 建議LZ或者版主把第一樓編輯下,加上內(nèi)核版本號 2.6.21 . 這樣清楚些.

[ 本帖最后由 accessory 于 2009-4-24 23:27 編輯 ]
作者: thelittlefox    時間: 2009-05-26 09:40
寫的太精彩了~~
謝謝版主的精彩分析~
ChinaUnix上大牛果然很多
作者: crifan    時間: 2009-05-29 01:39
感謝LZ寫的這么詳細,很有幫助,所以抽空弄了個pdf版本的,方便大家收藏哈:
網(wǎng)卡驅動注冊到PCI總線這一過程的分析.pdf (475.15 KB, 下載次數(shù): 252)
作者: duanius    時間: 2009-06-02 15:58
讀到這篇文章 發(fā)現(xiàn)挺不錯的 可以作為這篇的usb姐妹篇  
http://blog.csdn.net/aaronychen/archive/2008/03/17/2192147.aspx
作者: Godbach    時間: 2009-06-02 16:04
眾人拾柴火焰高啊。多謝大家的分享。
作者: wlp555ren    時間: 2009-07-21 11:35
有個問題,device結構中的關于設備的ID信息,必要要求與驅動一致,才能與之對應,那這個設備的ID信息是怎么得到的????
作者: Godbach    時間: 2009-07-21 16:44
原帖由 wlp555ren 于 2009-7-21 11:35 發(fā)表
有個問題,device結構中的關于設備的ID信息,必要要求與驅動一致,才能與之對應,那這個設備的ID信息是怎么得到的????


PCI設置中是有配置空間的,可以再掃描設置是讀取config空間,獲取到吧。
作者: cskyrain    時間: 2009-08-01 22:32
精彩,不過能在詳細點就更好了,呵呵,
作者: anhongkui    時間: 2009-08-02 20:10
下載看了很精彩
作者: jyhhappyjyh    時間: 2011-06-09 17:49
回復 3# scutan


    版主寫得很辛苦,收益匪淺!有利于全局把握PCI設備驅動的注冊過程!謝謝!
作者: lxzjw123    時間: 2012-04-28 18:05
多謝,學習了
作者: kvmautotest    時間: 2012-04-29 11:41
回復 1# scutan

不錯哦。
不過建議最好寫一下內(nèi)核代碼的版本。
   
作者: zjfreeubuntu    時間: 2013-04-22 21:47
很感動,分享快樂
作者: firocu    時間: 2013-04-26 22:24
還沒讀,先MARK。。。
作者: 雷鋒不謝    時間: 2014-08-27 14:20
期待 后面網(wǎng)驅對網(wǎng)卡的初始化。。;
作者: l289123557    時間: 2015-09-07 23:26
保存網(wǎng)頁上班看
作者: netdyk    時間: 2016-02-26 16:22
回復 53# crifan

好人啊


   




歡迎光臨 Chinaunix (http://72891.cn/) Powered by Discuz! X3.2