一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

網(wǎng)卡驅(qū)動(dòng)注冊(cè)到PCI總線這一過程的分析...

 黃浦江中的一條魚 2009-04-16
大家好,最近在看網(wǎng)絡(luò)部分的代碼,目前看到了網(wǎng)卡的初始化部分。書上講到的內(nèi)容主要是網(wǎng)卡驅(qū)動(dòng)程序?qū)W(wǎng)卡自身的初始化部分,即網(wǎng)卡驅(qū)動(dòng)的probe函數(shù)是如何執(zhí)行的,而很少講到網(wǎng)卡是如何注冊(cè)到系統(tǒng)中去的這一部分。
    現(xiàn)在的網(wǎng)卡大部分都是連接到PCI總線上的。因此,網(wǎng)卡驅(qū)動(dòng)是如何連接到PCI總線,又是如何與網(wǎng)卡設(shè)備聯(lián)系起來,網(wǎng)卡在注冊(cè)的最后又是
如何調(diào)用到該網(wǎng)卡的probe函數(shù)的,這一個(gè)過程將在后面的文章中進(jìn)行描述。整個(gè)文章分成兩個(gè)部分,第一部分是講解總線、設(shè)備以及驅(qū)動(dòng)三者的聯(lián)系,為第二
部分具體講解PCI總線、網(wǎng)卡設(shè)備和驅(qū)動(dòng)做一點(diǎn)鋪墊。
    由于我在這方面也是初學(xué),之所以想總結(jié)出來是想到在總結(jié)的過程中對(duì)自己的學(xué)習(xí)也是一個(gè)梳理的過程。所以有什么地方寫得不好的,還請(qǐng)各位多多指正,非常感謝!也希望能在這里結(jié)識(shí)更多的朋友。
    在總結(jié)的過程中參考了下面一些資料,在此表示感謝:
[1]
[2]
[3]
[4]  3rd Edition.
1. 總線、設(shè)備和驅(qū)動(dòng)
1.1 簡單介紹
        Linux設(shè)備模型中三個(gè)很重要的概念就是總線、設(shè)備和驅(qū)動(dòng),即bus,device和driver。它們分別對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)分別為struct bus_type,struct device和struct device_driver。
        總線是處理器與一個(gè)或多個(gè)設(shè)備之間的通道,在設(shè)備模型中,所有的設(shè)備都通過總線相連。在最底層,Linux系統(tǒng)中的每一個(gè)設(shè)備都用device結(jié)構(gòu)的一個(gè)實(shí)例來表示。而驅(qū)動(dòng)則是使總線上的設(shè)備能夠完成它應(yīng)該完成的功能。
在系統(tǒng)中有多種總線,如PCI總線、SCSI總線等。系統(tǒng)中的多個(gè)設(shè)備和驅(qū)動(dòng)是通過總線讓它們聯(lián)系起來的。在bus_type中兩個(gè)很重要的成員就是
struct kset drivers和struct kset devices。它分別代表了連接在這個(gè)總線上的兩個(gè)鏈,一個(gè)是設(shè)備鏈表,另一個(gè)則是
設(shè)備驅(qū)動(dòng)鏈表。也就是說,通過一個(gè)總線描述符,就可以找到掛載到這條總線上的設(shè)備,以及支持該總線的不同的設(shè)備驅(qū)動(dòng)程序。
1.2 總線、設(shè)備與驅(qū)動(dòng)的綁定
在系統(tǒng)啟動(dòng)時(shí),它會(huì)對(duì)每種類型的總線創(chuàng)建一個(gè)描述符,并將使用該總線的設(shè)備鏈接到該總線描述符的devices鏈上來。也即是說在系統(tǒng)初始化時(shí),它會(huì)掃描
連接了哪些設(shè)備,并且為每個(gè)設(shè)備建立一個(gè)struce device變量,然后將該變量鏈接到這個(gè)設(shè)備所連接的總線的描述符上去。另一方面,每當(dāng)加載了一
個(gè)設(shè)備驅(qū)動(dòng),則系統(tǒng)也會(huì)準(zhǔn)備一個(gè)struct device_driver結(jié)構(gòu)的變量,然后再將這個(gè)變量也鏈接到它所在總線的描述符的drivers鏈上
去。
對(duì)于設(shè)備來說,在結(jié)構(gòu)體struct device中有兩個(gè)重要的成員,一個(gè)是struct bus_type *bus,另一個(gè)是
struct device_driver *driver。bus成員就表示該設(shè)備是鏈接到哪一個(gè)總線上的,而driver成員就表示當(dāng)前設(shè)備是由哪個(gè)
驅(qū)動(dòng)程序所驅(qū)動(dòng)的。對(duì)于驅(qū)動(dòng)程序來說,在結(jié)構(gòu)體struct device_driver中也有兩個(gè)成員,struct bus_type *bus和
struct list_head devices,這里的bus成員也是指向這個(gè)驅(qū)動(dòng)是鏈接到哪個(gè)總線上的,而devices這個(gè)鏈表則是表示當(dāng)前這個(gè)
驅(qū)動(dòng)程序可以去進(jìn)行驅(qū)動(dòng)的那些設(shè)備。一個(gè)驅(qū)動(dòng)程序可以支持一個(gè)或多個(gè)設(shè)備,而一個(gè)設(shè)備則只會(huì)綁定給一個(gè)驅(qū)動(dòng)程序。
對(duì)于device與device_driver之間建立聯(lián)系的方式,主要有兩種方式。第一種,在計(jì)算機(jī)啟動(dòng)的時(shí)候,總線開始掃描連接在其上的設(shè)備,為每個(gè)
設(shè)備建立一個(gè)struct device變量并鏈接到該總線的devices鏈上,然后開始初始化不同的驅(qū)動(dòng)程序,驅(qū)動(dòng)程序到它所在的總線的
devices鏈上去遍歷每一個(gè)還沒有被綁定給某個(gè)驅(qū)動(dòng)的設(shè)備,然后再查看是否能夠支持這種設(shè)備,如果它能夠支持這種設(shè)備,則將這個(gè)設(shè)備與這個(gè)驅(qū)動(dòng)聯(lián)系起
來。即,將這個(gè)設(shè)備的device變量加到驅(qū)動(dòng)的devices鏈上,同時(shí)讓struct device中的device_driver指向當(dāng)前這個(gè)驅(qū)
動(dòng)。第二種則是熱插拔。也即是在系統(tǒng)運(yùn)行時(shí)插入了設(shè)備,此時(shí)內(nèi)核會(huì)去查找在該bus鏈上注冊(cè)了的device_driver,然后再將設(shè)備與驅(qū)動(dòng)聯(lián)系起
來。設(shè)備與驅(qū)動(dòng)根據(jù)什么規(guī)則聯(lián)系起來,它們是如何被聯(lián)系起來的代碼我們將在后面的章節(jié)進(jìn)行詳細(xì)的描述。
1.3 PCI總線
PCI是一種在CPU與I/O設(shè)備之間進(jìn)行高速數(shù)據(jù)傳輸?shù)囊环N總線。有很多設(shè)備都是使用PCI總線的,網(wǎng)卡就是其中之一。我們?cè)谇懊嬷v了那些總線、設(shè)備與
驅(qū)動(dòng)方面的知識(shí),原因就在于網(wǎng)卡是連接到PCI總線上,所以PCI總線、網(wǎng)卡設(shè)備以及網(wǎng)卡驅(qū)動(dòng)就成了我們研究網(wǎng)卡的一個(gè)很重要的線索,尤其是在網(wǎng)絡(luò)的鏈路
層部分。下圖顯示了在一個(gè)系統(tǒng)中PCI設(shè)備的一個(gè)框圖:
[attach]226528[/attach]
        PCI子系統(tǒng)聲明了一個(gè)bus_type結(jié)構(gòu),為pci_bus_type。它就是PCI總線的描述符。在這個(gè)變量上,鏈接了PCI設(shè)備以及支持PCI設(shè)備的驅(qū)動(dòng)程序。
1.4 PCI設(shè)備與驅(qū)動(dòng)
PCI設(shè)備通常由一組參數(shù)唯一地標(biāo)識(shí),它們被vendorID,deviceID和class nodes所標(biāo)識(shí),即設(shè)備廠商,型號(hào)等,這些參數(shù)保存在
pci_device_id結(jié)構(gòu)中。每個(gè)PCI設(shè)備都會(huì)被分配一個(gè)pci_dev變量,內(nèi)核就用這個(gè)數(shù)據(jù)結(jié)構(gòu)來表示一個(gè)PCI設(shè)備。
所有的PCI驅(qū)動(dòng)程序都必須定義一個(gè)pci_driver結(jié)構(gòu)變量,在該變量中包含了這個(gè)PCI驅(qū)動(dòng)程序所提供的不同功能的函數(shù),同時(shí),在這個(gè)結(jié)構(gòu)中也包
含了一個(gè)device_driver結(jié)構(gòu),這個(gè)結(jié)構(gòu)定義了PCI子系統(tǒng)與PCI設(shè)備之間的接口。在注冊(cè)PCI驅(qū)動(dòng)程序時(shí),這個(gè)結(jié)構(gòu)將被初始化,同時(shí)這個(gè)
pci_driver變量會(huì)被鏈接到pci_bus_type中的驅(qū)動(dòng)鏈上去。
        在pci_driver中有一個(gè)成員struct pci_device_id *id_table,它列出了這個(gè)設(shè)備驅(qū)動(dòng)程序所能夠處理的所有PCI設(shè)備的ID值。
1.5 PCI設(shè)備與驅(qū)動(dòng)的綁定過程
下面描述一下對(duì)于PCI設(shè)備與驅(qū)動(dòng)綁定的過程。首先在系統(tǒng)啟動(dòng)的時(shí)候,PCI總線會(huì)去掃描連接到這個(gè)總線上的設(shè)備,同時(shí)為每一個(gè)設(shè)備建立一個(gè)
pci_dev結(jié)構(gòu),在這個(gè)結(jié)構(gòu)中有一個(gè)device成員,并將這些pci_dev結(jié)構(gòu)鏈接到PCI總線描述符上的devices鏈。如下圖所示:
[attach]226529[/attach]
        第二步是當(dāng)PCI驅(qū)動(dòng)被加載時(shí),pci_driver結(jié)構(gòu)體將被初始化,這一過程在函數(shù)pci_register_driver中:
        drv->driver.bus = &pci_bus_type;
        drv->driver.probe = pci_device_probe;
最后會(huì)調(diào)用driver_register(&drv->driver)將這個(gè)PCI驅(qū)動(dòng)掛載到總線描述符的驅(qū)動(dòng)鏈上。同時(shí)在注冊(cè)的過程
中,會(huì)根據(jù)pci_driver中的id_table中的ID值去查看該驅(qū)動(dòng)支持哪些設(shè)備,將這些設(shè)備掛載到pci_driver中的devices鏈中
來。如下圖所示:
[attach]226530[/attach]
        對(duì)于不同的設(shè)備,可能驅(qū)動(dòng)程序也不一樣,因此,對(duì)于上圖中的Dev3,可能就需要另外一個(gè)驅(qū)動(dòng)程序來對(duì)其進(jìn)行驅(qū)動(dòng)。所以當(dāng)加載了Dev3的驅(qū)動(dòng)程序時(shí),其示意圖如下圖所示:
[attach]226531[/attach]
        上面這三個(gè)示意圖就描述了總線、設(shè)備以及驅(qū)動(dòng)在系統(tǒng)中是如何進(jìn)行相互聯(lián)系的。前面對(duì)于驅(qū)動(dòng)注冊(cè)這些函數(shù)的描述較為簡單,因?yàn)榫W(wǎng)卡是一個(gè)PCI設(shè)備,因此在后面具體地講到網(wǎng)卡注冊(cè)時(shí)再來詳細(xì)地講解和PCI相關(guān)的注冊(cè)等函數(shù)。
1.6 小結(jié)
本部分主要講解了總線、設(shè)備以及驅(qū)動(dòng)方面的一些知識(shí),由于網(wǎng)卡是一個(gè)PCI設(shè)備,因此具體地講到了一點(diǎn)PCI總線、PCI設(shè)備及相應(yīng)的PCI驅(qū)動(dòng)方面的知
識(shí),但是由于PCI本身就是很大的一個(gè)子系統(tǒng),因此這里不可能對(duì)其進(jìn)行詳細(xì)地講解,在后面對(duì)網(wǎng)卡的分析中,將對(duì)網(wǎng)卡中涉及到的和PCI相關(guān)的部分進(jìn)行講
解。

PCI

init1

init2

init3

scutan
回復(fù)于:2008-12-15 22:47:47

2. 網(wǎng)卡在PCI層的注冊(cè)
2.1 數(shù)據(jù)結(jié)構(gòu)
        前面第一章講了總線、設(shè)備以及驅(qū)動(dòng)方面的關(guān)系,也講到了大多數(shù)網(wǎng)卡設(shè)備實(shí)際上是一個(gè)PCI設(shè)備。因此,本章就講解網(wǎng)卡設(shè)備在注冊(cè)時(shí)是如何注冊(cè)到PCI總線上去的。在這里,以Intel的E100網(wǎng)卡驅(qū)動(dòng)進(jìn)行講解。
        前面講到每個(gè)PCI設(shè)備都由一組參數(shù)唯一地標(biāo)識(shí),這些參數(shù)保存在結(jié)構(gòu)體pci_device_id中,如下所示:
struct pci_device_id {
        __u32 vendor, device;                /* Vendor and device ID or PCI_ANY_ID*/
        __u32 subvendor, subdevice;        /* Subsystem ID's or PCI_ANY_ID */
        __u32 class, class_mask;        /* (class,subclass,prog-if) triplet */
        kernel_ulong_t driver_data;        /* Data private to the driver */
};
        每個(gè)PCI設(shè)備驅(qū)動(dòng)都有一個(gè)pci_driver變量,它描述了一個(gè)PCI驅(qū)動(dòng)的信息,如下所示:
struct pci_driver {
        struct list_head node;
        char *name;
        const struct pci_device_id *id_table;        /* must be non-NULL for probe to be called */
        int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);        /* New device inserted */
        void (*remove) (struct pci_dev *dev);        /* Device removed (NULL if not a hot-plug capable driver) */
        int  (*suspend) (struct pci_dev *dev, pm_message_t state);        /* Device suspended */
        int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
        int  (*resume_early) (struct pci_dev *dev);
        int  (*resume) (struct pci_dev *dev);                        /* Device woken up */
        int  (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);   /* Enable wake event */
        void (*shutdown) (struct pci_dev *dev);
        struct pci_error_handlers *err_handler;
        struct device_driver        driver;
        struct pci_dynids dynids;
        int multithread_probe;
};
        每個(gè)PCI驅(qū)動(dòng)中都有一個(gè)id_table成員變量,記錄了當(dāng)前這個(gè)驅(qū)動(dòng)所能夠進(jìn)行驅(qū)動(dòng)的那些設(shè)備的ID值。
        對(duì)于E100網(wǎng)卡驅(qū)動(dòng)來說,它的pci_driver變量定義為:
static struct pci_driver e100_driver = {
        .name =         DRV_NAME,
        .id_table =     e100_id_table,
        .probe =        e100_probe,
        .remove =       __devexit_p(e100_remove),
#ifdef CONFIG_PM
        /* Power Management hooks */
        .suspend =      e100_suspend,
        .resume =       e100_resume,
#endif
        .shutdown =     e100_shutdown,
        .err_handler = &e100_err_handler,
};
        里面e100_id_table就表示該E100驅(qū)動(dòng)所能夠支持的PCI設(shè)備的ID號(hào),其定義為:
#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
        PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
        PCI_CLASS_NETWORK_ETHERNET
當(dāng)PCI層檢測(cè)到一個(gè)PCI設(shè)備能夠被某PCI驅(qū)動(dòng)所支持時(shí)(這是通過函數(shù)pci_match_one_device來進(jìn)行檢測(cè)的),就會(huì)調(diào)用這個(gè)PCI
驅(qū)動(dòng)上的probe函數(shù),在該函數(shù)中會(huì)對(duì)該特定的PCI設(shè)備進(jìn)行一些具體的初始化等操作。比如對(duì)于E100設(shè)備驅(qū)動(dòng)來說,其probe函數(shù)為
e100_probe。在這個(gè)函數(shù)中,會(huì)對(duì)網(wǎng)卡設(shè)備進(jìn)行初始化。
        e100_probe主要就涉及到網(wǎng)卡設(shè)備net_device的初始化,我們現(xiàn)在先來關(guān)注一下從網(wǎng)卡注冊(cè)一直到調(diào)用e100_probe這一個(gè)過程的整個(gè)流程。
2.2 E100初始化
        E100驅(qū)動(dòng)程序的初始化是在函數(shù)e100_init_module()中的,如下:
static int __init e100_init_module(void)
{
        if(((1
        在這個(gè)函數(shù)中,調(diào)用了pci_register_driver()函數(shù),對(duì)e100_driver這個(gè)驅(qū)動(dòng)進(jìn)行注冊(cè)。
2.3 PCI注冊(cè)
        在前面我們已經(jīng)看到,PCI的注冊(cè)就是將PCI驅(qū)動(dòng)程序掛載到其所在的總線的drivers鏈,同時(shí)掃描PCI設(shè)備,將它能夠進(jìn)行驅(qū)動(dòng)的設(shè)備掛載到driver上的devices鏈表上來,這里,我們將詳細(xì)地查看這整個(gè)流程的函數(shù)調(diào)用關(guān)系。
        pci_register_driver()->__pci_register_driver()
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
* @owner: owner module of drv
* @mod_name: module name string
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/       
int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name);
        在函數(shù)中有幾個(gè)初始化語句:
        drv->driver.name = drv->name;
        drv->driver.bus = &pci_bus_type;
        drv->driver.owner = owner;
        drv->driver.mod_name = mod_name;
        即是將PCI設(shè)備中的driver變量的總線指向pci_bus_type這個(gè)總線描述符,同時(shí)設(shè)置驅(qū)動(dòng)的名字等。
        pci_bus_type定義如下:
struct bus_type pci_bus_type = {
        .name                = "pci",
        .match                = pci_bus_match,
        .uevent                = pci_uevent,
        .probe                = pci_device_probe,
        .remove                = pci_device_remove,
        .suspend        = pci_device_suspend,
        .suspend_late        = pci_device_suspend_late,
        .resume_early        = pci_device_resume_early,
        .resume                = pci_device_resume,
        .shutdown        = pci_device_shutdown,
        .dev_attrs        = pci_dev_attrs,
};
        然后再調(diào)用函數(shù)driver_register(&drv->driver);通過這個(gè)函數(shù)將這個(gè)PCI驅(qū)動(dòng)中的struct device_driver driver成員變量注冊(cè)到系統(tǒng)中去。
        pci_register_driver()->__pci_register_driver()->driver_register()
        driver_register()代碼如下:
/**
*        driver_register - register driver with bus
*        @drv:        driver to register
*
*        We pass off most of the work to the bus_add_driver() call,
*        since most of the things we have to do deal with the bus
*        structures.
*
*        The one interesting aspect is that we setup @drv->unloaded
*        as a completion that gets complete when the driver reference
*        count reaches 0.
*/
int driver_register(struct device_driver * drv)
{
        if ((drv->bus->probe && drv->probe) ||
            (drv->bus->remove && drv->remove) ||
            (drv->bus->shutdown && drv->shutdown)) {
                printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
        }
        klist_init(&drv->klist_devices, NULL, NULL);
        init_completion(&drv->unloaded);
        return bus_add_driver(drv);
}
        klist_init()是為設(shè)備驅(qū)動(dòng)的klist_devices成員進(jìn)行初始化,這個(gè)klist_devices是一個(gè)對(duì)鏈表進(jìn)行操作的包裹結(jié)構(gòu),它會(huì)鏈接這個(gè)驅(qū)動(dòng)能夠支持的那些設(shè)備。
        最后就調(diào)用bus_add_driver()函數(shù)。這個(gè)函數(shù)的功能就是將這個(gè)驅(qū)動(dòng)加到其所在的總線的驅(qū)動(dòng)鏈上。
        pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()
        在bus_add_driver()函數(shù)中,最重要的是調(diào)用driver_attach()函數(shù),其定義如下:
/**
*        driver_attach - try to bind driver to devices.
*        @drv:        driver.
*
*        Walk the list of devices that the bus has on it and try to
*        match the driver with each one.  If driver_probe_device()
*        returns 0 and the @dev->driver is set, we've found a
*        compatible pair.
*/
int driver_attach(struct device_driver * drv)
{
        return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
        該函數(shù)遍歷這個(gè)驅(qū)動(dòng)所在的總線上的所有設(shè)備,然后將這些設(shè)備與當(dāng)前驅(qū)動(dòng)進(jìn)行匹配,以檢測(cè)這個(gè)驅(qū)動(dòng)是否能夠支持某個(gè)設(shè)備,也即是將設(shè)備與驅(qū)動(dòng)聯(lián)系起來。
        bus_for_each_dev函數(shù)是掃描在drv->bus這個(gè)總線上的所有設(shè)備,然后將每個(gè)設(shè)備以及當(dāng)前驅(qū)動(dòng)這兩個(gè)指針傳遞給__driver_attach函數(shù)。
pci_register_driver()->__pci_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()
        __driver_attach()函數(shù)是將驅(qū)動(dòng)與設(shè)備聯(lián)系起來的函數(shù)。
static int __driver_attach(struct device * dev, void * data)
{
        struct device_driver * drv = data;
        /*
         * Lock device and try to bind to it. We drop the error
         * here and always return 0, because we need to keep trying
         * to bind to devices and some drivers will return an error
         * simply if it didn't support the device.
         *
         * driver_probe_device() will spit a warning if there
         * is an error.
         */
        if (dev->parent)        /* Needed for USB */
                down(&dev->parent->sem);
        down(&dev->sem);
        if (!dev->driver)
                driver_probe_device(drv, dev);
        up(&dev->sem);
        if (dev->parent)
                up(&dev->parent->sem);
        return 0;
}
        在函數(shù)中有兩條語句:
        if (!dev->driver)
                driver_probe_device(drv, dev);
        也即是判斷當(dāng)前設(shè)備是否已經(jīng)注冊(cè)了一個(gè)驅(qū)動(dòng),如果沒有注冊(cè)驅(qū)動(dò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()
        如下:
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, an error if one occurs
* (that is not -ENODEV or -ENXIO), and 0 otherwise.
*
* This function must be called with @dev->sem held.  When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
        struct stupid_thread_structure *data;
        struct task_struct *probe_task;
        int ret = 0;
        if (!device_is_registered(dev))
                return -ENODEV;
        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);
        data = kmalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
        data->drv = drv;
        data->dev = dev;
        if (drv->multithread_probe) {
                probe_task = kthread_run(really_probe, data,
                                         "probe-%s", dev->bus_id);
                if (IS_ERR(probe_task))
                        ret = really_probe(data);
        } else
                ret = really_probe(data);
done:
        return ret;
}       
        該函數(shù)首先會(huì)調(diào)用總線上的match函數(shù),以判斷當(dāng)前的PCI驅(qū)動(dòng)能否支持該P(yáng)CI設(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()
/**
* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
* @dev: the PCI device structure to match against
* @drv: the device driver to search for matching PCI device id structures
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct pci_driver *pci_drv = to_pci_driver(drv);
        const struct pci_device_id *found_id;
        found_id = pci_match_device(pci_drv, pci_dev);
        if (found_id)
                return 1;
        return 0;
}
pci_bus_match函數(shù)的作用就是將PCI設(shè)備與PCI驅(qū)動(dòng)進(jìn)行比較以檢查該驅(qū)動(dòng)是否能夠支持這個(gè)設(shè)備。在函數(shù)的最前面是兩個(gè)宏
to_pci_dev和to_pci_driver。因?yàn)樵诤瘮?shù)執(zhí)行的過程中,雖然最開始傳進(jìn)來的是pci_driver結(jié)構(gòu)與pci_dev結(jié)構(gòu),但是
在執(zhí)行的時(shí)候卻取了這兩個(gè)結(jié)構(gòu)體中的device_driver和device成員變量,所以現(xiàn)在就要通過這兩個(gè)成員變量找到之前對(duì)應(yīng)的
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)
這兩個(gè)宏在 3rd書上有相應(yīng)的講解,這里也就是找到E100的pci_driver:
e100_driver以及該網(wǎng)卡設(shè)備的pci_dev結(jié)構(gòu)?,F(xiàn)在就要對(duì)它們進(jìn)行比較以看它們之間是否能夠聯(lián)系起來。這是通過函數(shù)
pci_match_device實(shí)現(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()
/**
* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
* @drv: the PCI driver to match against
* @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices.  Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                                             struct pci_dev *dev)
{
        struct pci_dynid *dynid;
        /* Look at the dynamic ids first, before the static ones */
        spin_lock(&drv->dynids.lock);
        list_for_each_entry(dynid, &drv->dynids.list, node) {
                if (pci_match_one_device(&dynid->id, dev)) {
                        spin_unlock(&drv->dynids.lock);
                        return &dynid->id;
                }
        }
        spin_unlock(&drv->dynids.lock);
        return pci_match_id(drv->id_table, dev);
}
        pci_match_one_driver函數(shù)的作用是將一個(gè)PCI設(shè)備與PCI驅(qū)動(dòng)進(jìn)行比較,以查看它們是否相匹配。如果相匹配,則返回匹配的pci_device_id結(jié)構(gòu)體指針。
此時(shí),如果該P(yáng)CI驅(qū)動(dòng)已經(jīng)找到了一個(gè)可以想符的PCI設(shè)備,則返回,然后再退回到之前的driver_probe_device函數(shù)中。在該函數(shù)最后將
調(diào)用really_probe函數(shù)。將device_driver與device結(jié)構(gòu)體指針作為參數(shù)傳遞到這個(gè)函數(shù)中。下面幾行是調(diào)用驅(qū)動(dòng)或者總線的
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()中:
        if (dev->bus->probe) {
                ret = dev->bus->probe(dev);
                if (ret)
                        goto probe_failed;
        } else if (drv->probe) {
                ret = drv->probe(dev);
                if (ret)
                        goto probe_failed;
        }
        此時(shí)的dev->bus為pci_bus_type,其probe函數(shù)則對(duì)應(yīng)為: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ù)中會(huì)獲得當(dāng)前的PCI設(shè)備的pci_dev結(jié)構(gòu)體指針以及PCI驅(qū)動(dòng)程序的pci_driver結(jié)構(gòu)體指針。分別使用宏
to_pci_dev和to_pci_driver。最后則調(diào)用函數(shù)__pci_device_probe。在該函數(shù)中還會(huì)調(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里有一條語句:
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);
        在此處就調(diào)用了pci_driver的probe函數(shù),對(duì)于這里的E100驅(qū)動(dòng)來說,它的probe函數(shù)是最開始注冊(cè)的e100_probe函數(shù),在該函數(shù)中會(huì)完成對(duì)網(wǎng)卡設(shè)備net_device的初始化等操作。
        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()
        到這里,我們對(duì)網(wǎng)卡驅(qū)動(dòng)的PCI層的初始化分析就告一個(gè)段落了,剩下的部分就是網(wǎng)卡驅(qū)動(dòng)對(duì)網(wǎng)卡設(shè)備本身的初始化等操作。

scutan
回復(fù)于:2008-12-15 22:48:52

2.4 函數(shù)調(diào)用流程圖
        在這里,為網(wǎng)卡在PCI層的注冊(cè)畫了一個(gè)函數(shù)調(diào)用的流程圖,能夠更直觀地展現(xiàn)網(wǎng)卡從注冊(cè)到調(diào)用其自身的網(wǎng)卡初始化的這一個(gè)函數(shù)調(diào)用過程。
[attach]226537[/attach]
[attach]226538[/attach]

1

2

scutan
回復(fù)于:2008-12-15 22:52:22

文筆實(shí)在不行,寫了一個(gè)下午加晚上,才寫這么點(diǎn)。希望能夠?qū)Τ鯇W(xué)者有一點(diǎn)用吧。因?yàn)槲易铋_始在看網(wǎng)卡驅(qū)動(dòng)的時(shí)候,就是迷惑加載了網(wǎng)卡之后是如何調(diào)用到該網(wǎng)卡的probe函數(shù)的。所以就仔細(xì)地看了一下里面的源碼。
這里主要還是起一個(gè)梳理的作用,很多代碼也沒有進(jìn)一步地深入分析。不過對(duì)于網(wǎng)絡(luò)架構(gòu)來說,首先將整個(gè)調(diào)用的流程掌握了,對(duì)后面的理解也就更加方便了。
:lol:

Godbach
回復(fù)于:2008-12-15 22:54:50

scutan兄又有好文章了,明天仔細(xì)拜讀啊。

albcamus
回復(fù)于:2008-12-15 23:10:29

補(bǔ)充一下,貼一下open函數(shù)的調(diào)用的筆記:
86, ifconfig eth0 up 會(huì)導(dǎo)致 net_device->open被調(diào)用,內(nèi)幕!
       
            # strace ifconfig eth0 up 2>&1 |less -N
   
    可以看到,它是先用sockfd = socket(AF_INET, SOCK_DGRAM, 0)生成一個(gè)sockfd文件描述符,
    再ioctl(sockfd, SIOCSIFFLAGS, 加上IFF_UP標(biāo)志)。 這樣就導(dǎo)致了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;
       
        (回憶一下,這個(gè)是不是就類似于ext3_iget()里頭對(duì)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);
       
        就在此時(shí),struct net_device的open方法被調(diào)用。

albcamus
回復(fù)于:2008-12-15 23:11:23

2). remove函數(shù)何時(shí)被調(diào)用?
            
            當(dāng)pci_dev消失時(shí)(設(shè)備被拔出),或者module被rmmod時(shí)。
            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);
          
            對(duì)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;
                        }

albcamus
回復(fù)于:2008-12-15 23:11:50

[FYI] 增加/刪除一個(gè)PCI device時(shí)的情景。
              (只有boot時(shí)的enumeration和hotplug兩種情況可能導(dǎo)致設(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這個(gè)值,那么就直接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
回復(fù)于:2008-12-15 23:27:56

:em02:
謝謝albcamus版主精彩的補(bǔ)充。

kns1024wh
回復(fù)于:2008-12-16 08:48:27

實(shí)踐的結(jié)論是最有說服力的

dreamice
回復(fù)于:2008-12-16 09:51:09

這貼牛,高手匯集。受益匪淺:lol:

Godbach
回復(fù)于:2008-12-16 10:04:16

是啊。先讀一遍,然后再向scutan和albcamus兄請(qǐng)教。

Godbach
回復(fù)于:2008-12-16 10:56:10

引用:補(bǔ)充一下,貼一下open函數(shù)的調(diào)用的筆記:
大家要學(xué)習(xí)albcamus版主看書做筆記的好習(xí)慣啊。偶以前就沒這個(gè)習(xí)慣,理解的東西沒過多久就又忘了。

Arthur_
回復(fù)于:2008-12-16 13:25:12

五體投地:shock:

scutan
回復(fù)于:2008-12-16 13:38:57

謝謝各位。:em02:

Godbach
回復(fù)于:2008-12-16 14:09:43

通讀一遍,寫的很好。流程比較明確。
整個(gè)流程應(yīng)該是使用了兩個(gè)probe函數(shù)。一個(gè)是pci_bus_type的probe函數(shù)指針,指向pci_device_probe。
另一個(gè)就是驅(qū)動(dòng)程序自己的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
回復(fù)于:2008-12-16 14:16:41

不同的總線注冊(cè)不同的struct bus_type結(jié)構(gòu)體。隨后在同一總線上注冊(cè)多個(gè)設(shè)備驅(qū)動(dòng)的時(shí)候,它們對(duì)應(yīng)同一個(gè)struct bus_type。

Godbach
回復(fù)于:2008-12-16 14:33:49

一個(gè)小問題:
從內(nèi)核代碼中看到USB設(shè)備時(shí)使用usb_register注冊(cè)USB設(shè)備,同時(shí)使用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類的設(shè)備。這個(gè)該怎么理解。偶的硬件學(xué)得不好,熟悉的朋友能否幫忙解釋一下?

Godbach
回復(fù)于:2008-12-16 16:44:36

LZ說明一下內(nèi)核版本號(hào)吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:

scutan
回復(fù)于:2008-12-16 16:55:24

引用:原帖由 Godbach 于 2008-12-16 16:44 發(fā)表

LZ說明一下內(nèi)核版本號(hào)吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:

我的是2.6.21

[url=http://linux./bbs/viewpro.php?uid=551201]scutan

回復(fù)于:2008-12-16 16:56:34

引用:原帖由 Godbach 于 2008-12-16 16:44 發(fā)表

LZ說明一下內(nèi)核版本號(hào)吧。我這2.6.18.3,dd.c中沒有really_probe函數(shù)。:wink:

driver_probe_device 函數(shù)
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
        struct stupid_thread_structure *data;
        struct task_struct *probe_task;
        int ret = 0;
        if (!device_is_registered(dev))
                return -ENODEV;
        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);
        data = kmalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
        data->drv = drv;
        data->dev = dev;
        if (drv->multithread_probe) {
                probe_task = kthread_run(really_probe, data,
                                         "probe-%s", dev->bus_id);
                if (IS_ERR(probe_task))
                        ret = really_probe(data);
        } else
                ret = really_probe(data);
done:
        return ret;
}

[url=http://linux./bbs/viewpro.php?uid=534931]Godbach

回復(fù)于:2008-12-16 17:00:51

2.6.18.3中dd.c.
函數(shù)driver_probe_device代碼中dev->bus->probe對(duì)應(yīng)的函數(shù)為pci_device_probe,隨后調(diào)用關(guān)系為:->__pci_device_probe->pci_call_probe
該函數(shù)里面執(zhí)行了 drv->probe,即e100驅(qū)動(dòng)注冊(cè)的e100_probe()
driver_probe_device函數(shù)的實(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ù)的實(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
回復(fù)于:2008-12-16 17:07:01

代碼是變化了。應(yīng)該是2.6.21的代碼里面把driver_probe_device()函數(shù)的實(shí)現(xiàn)進(jìn)行了封裝。
[ 本帖最后由 Godbach 于 2008-12-16 17:09 編輯 ]

qinjiana0786
回復(fù)于:2008-12-16 19:16:57

分析的過程是正確的,但是在內(nèi)核的PCI的過程上要是展開下就很好,不要只是用圖片或者函數(shù)的調(diào)用路線來形容,畢竟內(nèi)核的部分不是能用只
言片語就能一筆帶過的,核心部分還是圍繞著PCI的過程詳細(xì)一下就太精彩了,如果LZ沒有時(shí)間將來我會(huì)把這部分完成。只是現(xiàn)在還在進(jìn)行TCP協(xié)議內(nèi)容,而
且我看了一下其他人對(duì)這些內(nèi)容的補(bǔ)充,實(shí)話說,在點(diǎn)但不在譜,就是說太零散,不利用于群內(nèi)大多數(shù)朋友的學(xué)習(xí),有點(diǎn)嘩眾取寵的感覺,這又不是問題貼沒有必要
展開某個(gè)細(xì)節(jié)來說明,況且展開的也不是難點(diǎn)所在,沒有主線,就象一個(gè)樂譜,每個(gè)人都做做自己的曲子插進(jìn)來,這樣的音樂能聽嗎?希望各位發(fā)揚(yáng)自主寫作的精
神,而不是靠討論貼來完成一篇精華的文章。


Godbach
回復(fù)于:2008-12-16 20:51:39

非常歡迎qinjiana0786兄來完成更詳細(xì)的分析啊。
LZ在這篇文章的開篇也說了,這里整理出來一個(gè)流程,提供一個(gè)主線,幫著梳理一下。這樣可以幫助希望自己熟悉和分析代碼,但又不知道如何下手的朋友。

eexplorer
回復(fù)于:2008-12-17 09:19:49

引用:原帖由 Godbach 于 2008-12-16 14:33 發(fā)表

一個(gè)小問題:
從內(nèi)核代碼中看到USB設(shè)備時(shí)使用usb_register注冊(cè)USB設(shè)備,同時(shí)使用usb自身的bus_type。那這意味著USB總線是和PCI總線屬于兩種不同的總線。
但為什么在pci_ids.h中,有這樣的定義:
MS在PCI ...

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

[url=http://linux./bbs/viewpro.php?uid=534931]Godbach

回復(fù)于:2008-12-17 09:42:01

引用:原帖由 eexplorer 于 2008-12-17 09:19 發(fā)表

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

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

[url=http://linux./bbs/viewpro.php?uid=686144]bbaqq222

回復(fù)于:2008-12-17 09:58:09

感謝高人的講解,認(rèn)真琢磨一下

scutan
回復(fù)于:2008-12-17 13:40:13

引用:原帖由 qinjiana0786 于 2008-12-16 19:16 發(fā)表

分析的過程是正確的,但是在內(nèi)核的PCI的過程上要是展開下就很好,不要只是用圖片或者函數(shù)的調(diào)用路線來形容,畢竟內(nèi)核的部分不是能用只言片語就能一筆帶過的,核心部分還是圍繞著PCI的過程詳細(xì)一下就太精彩了,如 ...

小弟在硬件這塊目前還沒有精力去深入下去,所以希望qinjiana0786兄能夠多多指點(diǎn)一下。
謝謝。
BTW: 我從您的TCP/IP核心過程分析中學(xué)到了很多東西,表示感謝!

[url=http://linux./bbs/viewpro.php?uid=256598]eexplorer

回復(fù)于:2008-12-17 17:33:05

引用:原帖由 Godbach 于 2008-12-17 09:42 發(fā)表

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

不太明白你的問題,USB device應(yīng)該只有通過USB host controller才能和CPU交互,

[url=http://linux./bbs/viewpro.php?uid=534931]Godbach

回復(fù)于:2008-12-17 17:49:10

引用:和usb host controller 通信的usb devices才是掛在usb bus上的
對(duì)這句話我的理解是,可能還有一些不通過usb host controller 通信的usb devices。
看來我的理解能力很弱

ller
回復(fù)于:2008-12-18 22:55:42

網(wǎng)卡驅(qū)動(dòng)模塊是什么時(shí)候加載的,是根據(jù)udev規(guī)則嗎,我沒有找到相應(yīng)規(guī)則,還是起網(wǎng)絡(luò)服務(wù)時(shí)加載的,我在運(yùn)行級(jí)別1上仍然可以lsmod到我的網(wǎng)卡驅(qū)動(dòng)模塊

ller
回復(fù)于:2008-12-18 23:38:30

在網(wǎng)終的啟動(dòng)腳本上看到了對(duì)ethN的modprobe操作,然后根據(jù)modprobe.conf中的alias找到相應(yīng)的驅(qū)動(dòng)

albcamus
回復(fù)于:2008-12-19 12:56:53

引用:原帖由 qinjiana0786 于 2008-12-16 19:16 發(fā)表

分析的過程是正確的,但是在內(nèi)核的PCI的過程上要是展開下就很好,不要只是用圖片或者函數(shù)的調(diào)用路線來形容,畢竟內(nèi)核的部分不是能用只言片語就能一筆帶過的,核心部分還是圍繞著PCI的過程詳細(xì)一下就太精彩了,如 ...

linux有的看、有的用嗎? 每個(gè)人就寫了那么一點(diǎn), 拼湊起來的:outu: :outu:

[url=http://linux./bbs/viewpro.php?uid=784896]OraBSD

回復(fù)于:2008-12-20 23:59:05

:shock:

emmoblin
回復(fù)于:2008-12-21 21:19:04

好貼啊,我也是不清楚怎么調(diào)用到probe的。
努力以后我也能寫幾篇加精的文章。努力。。。。

Godbach
回復(fù)于:2008-12-21 22:22:16

引用:原帖由 emmoblin 于 2008-12-21 21:19 發(fā)表

好貼啊,我也是不清楚怎么調(diào)用到probe的。
努力以后我也能寫幾篇加精的文章。努力。。。。

期待emmoblin兄多發(fā)幾篇精品文章上來啊。

[url=http://linux./bbs/viewpro.php?uid=182323]hb12112

回復(fù)于:2008-12-23 15:46:59

精彩!

efr6
回復(fù)于:2008-12-23 18:42:42

thank you!

WFCJZ
回復(fù)于:2008-12-24 01:01:56

搞懂了,整死電信,叫它給人玩網(wǎng)卡綁定這招,最煩人!:lol:
[flash]http://eblog./UploadFiles/2007-5/59797326.swf [/flash]

ops
回復(fù)于:2008-12-24 12:44:41

好東西

wylhistory
回復(fù)于:2009-01-14 20:09:13

學(xué)習(xí)了!

原文鏈接:
http://linux./bbs/viewthread.php?tid=1052717
轉(zhuǎn)載請(qǐng)注明作者名及原文出處
               
               
               

本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog./u2/83200/showart_1799736.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产成人精品午夜福利| 真实偷拍一区二区免费视频| 99热九九热这里只有精品| 久久精品偷拍视频观看| 中文字幕乱码亚洲三区| 精品久久少妇激情视频| 青青操视频在线播放免费| 亚洲熟妇熟女久久精品| 麻豆视传媒短视频在线看| 国产精品丝袜美腿一区二区| 日本办公室三级在线观看| 色哟哟精品一区二区三区| 日韩国产亚洲欧美另类| 黑丝国产精品一区二区| 国产免费黄片一区二区| 国产精品免费精品一区二区| 亚洲av专区在线观看| 天堂av一区一区一区| 国产亚洲欧美一区二区| 中文字幕日韩无套内射| 日韩18一区二区三区| 亚洲一区二区三区av高清| 国产对白老熟女正在播放| 亚洲精品日韩欧美精品| 欧美日韩一级aa大片| 国产一级内射麻豆91| 国产精品人妻熟女毛片av久| 久一视频这里只有精品| 国产一区国产二区在线视频| 亚洲国产成人久久99精品| 国产传媒免费观看视频| 太香蕉久久国产精品视频| 亚洲乱码av中文一区二区三区| 我的性感妹妹在线观看| 太香蕉久久国产精品视频| 蜜桃传媒在线正在播放| 国产午夜免费在线视频| 99国产精品国产精品九九 | 免费观看日韩一级黄色大片| 免费在线播放不卡视频| 日韩欧美中文字幕人妻|