日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

PCI驱动框架简单分析

發(fā)布時(shí)間:2023/12/18 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PCI驱动框架简单分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、PCI 概念介紹

? ??PCI是CPU和外圍設(shè)備通信的高速傳輸總線。PCI規(guī)范能夠?qū)崿F(xiàn)32位并行數(shù)據(jù)傳輸,工作頻率為 33MHz 或 66MHz ,最大吞吐率高達(dá)266MB/s,PCI的衍生物包括 CardBus、mini-PCI、PCI-Express、cPCI等。

? ? PCI總線體系結(jié)構(gòu)是一種層次式的體系結(jié)構(gòu)。在這種層次體系結(jié)構(gòu)中,PCI橋設(shè)備占據(jù)著重要的地位,它將父總線與子總線連接在一起,從而使整個(gè)系統(tǒng)看起來像一個(gè)倒置的樹狀結(jié)構(gòu),樹的頂端是CPU,它通過一個(gè)較為特殊的CPI橋設(shè)備-Host/PCI橋設(shè)備與根PCI總線連接起來。

? ? 作為特殊的PCI設(shè)備,PCI橋包括以下幾種:

? ? HOST/PCI橋,用于連接CPU與PCI根總線,第一個(gè)根總線的編號(hào)為0。在PC中,內(nèi)存控制器也通常被集成到Host/PCI橋設(shè)備芯片中,因此,Host/PCI橋也通常被稱為“北橋”芯片組。

? ? PCI/ISA橋,用作連接舊的ISA總線,通常,PCI中的類似的i8359A中斷控制器這樣的設(shè)備也會(huì)被集成到PCI/ISA橋設(shè)備中,因此,PCI/ISA橋通常也被稱作“南橋”芯片組。

? ? PCI-to-PCI橋,用于連接PCI主總線與次總線,PCI橋所處的總線被稱作“主總線”(父總線),PCI橋設(shè)備所連接的總線為“次總線”(子總線)。

? ??

二、PCI設(shè)備與配置空間

? ? 在i386系統(tǒng)結(jié)構(gòu)中,對(duì)內(nèi)存的訪問和對(duì)輸入/輸出寄存器的訪問通過兩套不同的指令完成,所有的存儲(chǔ)器和IO兩個(gè)不同的地址空間。一般而言,內(nèi)存的物理地址以及輸入/輸出寄存器的地址是由硬件決定的,不過對(duì)于內(nèi)存的物理地址還可以通過地址映射機(jī)制來一次轉(zhuǎn)換(I/O也可以映射)。可是,怎樣處理外設(shè)的存儲(chǔ)空間呢?理想的辦法是系統(tǒng)軟件自動(dòng)設(shè)置,思路是:

? ? 1、外設(shè)通過某種途徑告訴系統(tǒng),它有幾個(gè)存儲(chǔ)區(qū)間以及I/O地址空間,每個(gè)區(qū)間是多大,以及各自在本地的地址,顯然這些地址都是局部的內(nèi)部的,都從0開始算起。

? ? 2、系統(tǒng)軟件在知道了一共有多少外設(shè),各自又有什么樣的存儲(chǔ)區(qū)間以后,就可以為這些區(qū)間分配“物理地址”,并且建立起這些區(qū)間與總線之間的連接,以后就可以通過這些地址來訪問。顯然,這里所謂的“物理地址”與真正的物理地址還是有些區(qū)別的,它實(shí)際上也是一種邏輯地址,所以常成為“總線地址”,因?yàn)檫@是CPU在總線上所看到的地址。可想而知,外設(shè)上一定有著某種地址映射機(jī)制。所謂的“為外設(shè)分配地址”,就是為其分配總線地址,并建立起映射。

? ? PCI設(shè)備上存在許多完成上述工作的寄存器(配置空間),那么系統(tǒng)初始化的時(shí)候如何訪問這些寄存器該何如?對(duì)于i386結(jié)構(gòu)的處理器,PCI總線的設(shè)計(jì)者在I/O地址空間保留了8個(gè)字節(jié)用于這個(gè)目的,那就是0xCF8~0xCFF,這8個(gè)字節(jié)的地址空間構(gòu)成了兩個(gè)32位的寄存器,第一個(gè)是“地址寄存器”0xCF8,第二個(gè)是“數(shù)據(jù)寄存器”0xCFC,要訪問配置空間的寄存器時(shí),CPU先向地址寄存器寫入目標(biāo)地址,然后通過數(shù)據(jù)寄存器進(jìn)行讀寫數(shù)據(jù)不過,寫入地址寄存器的目標(biāo)地址是一種包括總線號(hào)、設(shè)備號(hào)、功能號(hào)以及配置寄存器地址的綜合地址。每個(gè)PCI設(shè)備最多有8個(gè)功能,所以設(shè)備號(hào)和功能號(hào)組合在一起又被稱作“邏輯設(shè)備”號(hào)。


? ? 如上圖所示,PCI標(biāo)準(zhǔn)規(guī)定每個(gè)設(shè)備的配置寄存器組最多可以有256字節(jié)的連續(xù)空間,其中開頭的64字節(jié)的用途和格式是標(biāo)準(zhǔn)的,成為配置寄存器組的“頭部”,這樣的頭部又有兩種,“0型”頭部用于一般的PCI設(shè)備,“1型”頭部用于PCI橋無論是“0型”還是“1型”,其開頭的16個(gè)字節(jié)的用途和格式是共同的


三、PCI驅(qū)動(dòng)框架分析

? ? 在內(nèi)核中與PCI相關(guān)的結(jié)構(gòu)體大概有pci_driver 、pci_bus_type 、pci_dev 、pci_bus ,我們前邊所說的所有的PCI總線都是指的 pci_bus

? 3.1 pci_bus

struct pci_bus {struct list_head node; /* node in list of buses */struct pci_bus *parent; /* parent bus this bridge is on */struct list_head children; /* list of child buses */struct list_head devices; /* list of devices on this bus */struct pci_dev *self; /* bridge device as seen by parent */struct list_head slots; /* list of slots on this bus */struct resource *resource[PCI_BUS_NUM_RESOURCES];/* address space routed to this bus */struct pci_ops *ops; /* configuration access functions */void *sysdata; /* hook for sys-specific extension */struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */unsigned char number; /* bus number */unsigned char primary; /* number of primary bridge */unsigned char secondary; /* number of secondary bridge */unsigned char subordinate; /* max number of subordinate buses */char name[48];unsigned short bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */pci_bus_flags_t bus_flags; /* Inherited by child busses */struct device *bridge;struct device dev;struct bin_attribute *legacy_io; /* legacy I/O for this bus */struct bin_attribute *legacy_mem; /* legacy mem */unsigned int is_added:1; }; ? ? 幾個(gè)重要的成員:

? ? children: ?PCI橋可以使當(dāng)前總線得到擴(kuò)展,當(dāng)前總線上有幾個(gè)PCI橋,那么當(dāng)前總線就會(huì)擁有幾個(gè)子總線,子總線會(huì)連接到父總線的children鏈表中。

? ? device: 連接在這條總線上的設(shè)備鏈表。

? ? ops: 當(dāng)前總線訪問總線上設(shè)備配置空間的 read、write 方法。

? ? 在內(nèi)核啟動(dòng)的過程中,首先會(huì)創(chuàng)建0級(jí)總線,然后枚舉探測(cè)0級(jí)總線上的設(shè)備,如果是PCI橋,那么還要進(jìn)入下一級(jí)子總線,最終所有的連接的PCI設(shè)備都將被探測(cè)到,詳細(xì)的探測(cè)過程,我們?cè)诤筮叿治觥?/span>


? 3.2 pci_bus_type

? ? 看到 bus_type 顯然這是個(gè)設(shè)備總線驅(qū)動(dòng)模型里的“總線”,與前邊提到的 pci_bus ,完全是兩碼事,那么pci_driver 和 pci_dev 就是注冊(cè)到 pci_bus_type 的驅(qū)動(dòng)和設(shè)備。分析總線設(shè)備驅(qū)動(dòng)模型的時(shí)候,總要分析一下它的 match 函數(shù)(匹配規(guī)則)。

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; } static 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); } static inline const struct pci_device_id * pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) {if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&(id->device == PCI_ANY_ID || id->device == dev->device) &&(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&!((id->class ^ dev->class) & id->class_mask))return id;return NULL; } const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,struct pci_dev *dev) {if (ids) {while (ids->vendor || ids->subvendor || ids->class_mask) {if (pci_match_one_device(ids, dev))return ids;ids++;}}return NULL; } ? ? 通過分析代碼,PCI設(shè)備與驅(qū)動(dòng)的匹配方式有兩種,一種是通過 pci_driver->dynids ,另一種是通過 pci_driver->idtable 。使用idtable 是總線設(shè)備驅(qū)動(dòng)模型中常用的匹配方法,一般都是通過設(shè)備名來匹配,但是PCI比較特殊,它是通過設(shè)備的 vendor 、subvendor 、device 、subdevice 來匹配(這些都是在配置空間里可以讀取到的)。

? ? 至于 pci_driver->dynids ,它是通過用戶空間給驅(qū)動(dòng)增加匹配條件的一種方法(還記得I2C可以在用戶空間創(chuàng)建設(shè)備嗎,一樣的)。

error = pci_create_newid_file(drv); static int pci_create_newid_file(struct pci_driver *drv) {int error = 0;if (drv->probe != NULL)error = driver_create_file(&drv->driver, &driver_attr_new_id);return error; } ? ? 在 pci_register_driver 函數(shù)中會(huì)調(diào)用到一個(gè) pci_create_newid_file 函數(shù),它在 sysfs 文件系統(tǒng)中會(huì)創(chuàng)建一個(gè)?new_id 的屬性文件,通過這個(gè)屬性文件,我們就可以來為該驅(qū)動(dòng)增加匹配條件。

? ? 內(nèi)核幫助文檔有說明:

? ? New PCI IDs may be added to a device driver pci_ids table at runtime?as shown below:
? ? echo "vendor device subvendor subdevice class class_mask driver_data" > \
/sys/bus/pci/drivers/{driver}/new_id

? ? 對(duì)于這種方法不在詳細(xì)分析。

? ? 分析完設(shè)備總線驅(qū)動(dòng)模型,我想整個(gè)PCI驅(qū)動(dòng)的框架就非常清楚了,內(nèi)核啟動(dòng)時(shí),通過pci_bus之間的關(guān)系枚舉出所有的 PCI 設(shè)備,并為每一個(gè) PCI 設(shè)備創(chuàng)建一個(gè) pci_dev ,根據(jù)配置空間的信息填充 pci_dev 之后,注冊(cè)到pci_bus_type 。而,我們寫的 pci_driver 在 idtable 里指定它所支持的設(shè)備信息,同樣也注冊(cè)到 pci_bus_type中去,信息一致匹配成功則調(diào)用 driver->probe 函數(shù),然后你可以注冊(cè)字符設(shè)備、塊設(shè)備等等。


四、PCI設(shè)備的枚舉探測(cè)過程

? ? 在內(nèi)核啟動(dòng)過程中,PCI設(shè)備的探測(cè)過程是完全自動(dòng)的,內(nèi)核已經(jīng)集成好了方法,我們無需更改,在這里還是分析一邊代碼作為了解。

? ? 分析之前,先看一下全部的函數(shù)調(diào)用關(guān)系,大致了解一下

<span style="font-size:10px;">pci_arch_init /* 判斷host/pci橋的類型 */pci_direct_probepci_check_type1pci_sanity_checkpci_direct_initraw_pci_ops = &pci_direct_conf1;raw_pci_ext_ops = &pci_direct_conf1; /* 第二個(gè)過程,枚舉各級(jí)總線上的設(shè)備 */ pci_subsys_initpci_legacy_initpcibios_scan_rootpci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); pci_create_bus(parent, bus, ops, sysdata); // 創(chuàng)建 0 級(jí)總線pci_scan_child_bus(b); // 探測(cè)當(dāng)前總線設(shè)備以及子總線、子總線設(shè)備pci_scan_slot(bus, devfn); // 探測(cè)當(dāng)前總線的設(shè)備pci_scan_single_device(bus, devfn); // 探測(cè)單功能設(shè)備pci_scan_single_device(bus, devfn + fn); //探測(cè)多功能設(shè)備pci_scan_device(bus, devfn); //通過配置空間 枚舉設(shè)備pci_setup_device //根據(jù)配置空間信息,設(shè)置pci_devpci_device_add(dev, bus); list_add_tail(&dev->bus_list, &bus->devices); // 將探測(cè)到的設(shè)備加入到當(dāng)前總線的設(shè)備鏈表 pci_scan_bridge //此時(shí)已經(jīng)完成當(dāng)前總線設(shè)備的探測(cè),如果這些設(shè)備里有PCI橋,那么進(jìn)入下一級(jí),探測(cè)橋下的設(shè)備child = pci_add_new_bus(bus, dev, busnr);pci_scan_child_bus(child); // 進(jìn)入下一級(jí)探測(cè)pci_bus_add_devices // 全部設(shè)備探測(cè)完畢,注冊(cè)設(shè)備。pci_bus_add_device(dev); device_add // 將設(shè)備注冊(cè)到 pci_bus_typepci_bus_add_devices(child); //它最終也會(huì)調(diào)用到 device_add 將各個(gè)子總線上的設(shè)備注冊(cè)到 pci_bus_type ? ? 下面來看具體的探測(cè)過程。

static __init int pci_arch_init(void) { #ifdef CONFIG_PCI_DIRECTint type = 0;type = pci_direct_probe(); #endif#ifdef CONFIG_PCI_BIOSpci_pcbios_init(); #endif#ifdef CONFIG_PCI_DIRECTpci_direct_init(type); #endifdmi_check_pciprobe();dmi_check_skip_isa_align();return 0; } arch_initcall(pci_arch_init); ? ? 這個(gè)函數(shù)是放在 init 段中,內(nèi)核啟動(dòng)時(shí)會(huì)調(diào)用。
int __init pci_direct_probe(void) {struct resource *region, *region2;/* 申請(qǐng)IO資源 */region = request_region(0xCF8, 8, "PCI conf1");/* 探測(cè)那種類型 ,0型(PCI設(shè)備)和1型(PCI橋) */if (pci_check_type1()) {raw_pci_ops = &pci_direct_conf1;port_cf9_safe = true;return 1;}release_resource(region);return 0; } ? ? 這里,我們以“1型”也就是PCI橋?yàn)槔?#xff0c;看看是如何判斷類型的。

static int __init pci_check_type1(void) {unsigned long flags;unsigned int tmp;int works = 0;local_irq_save(flags);/* i386 pci地址寄存器 0xcfb 寫 0x01 */outb(0x01, 0xCFB);tmp = inl(0xCF8);outl(0x80000000, 0xCF8);/* 判斷設(shè)備類型 */if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {works = 1;}outl(tmp, 0xCF8);local_irq_restore(flags);return works; } static int __init pci_sanity_check(struct pci_raw_ops *o) {u32 x = 0;int year, devfn;/* Assume Type 1 works for newer systems.This handles machines that don't have anything on PCI Bus 0. */dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);if (year >= 2001)return 1;for (devfn = 0; devfn < 0x100; devfn++) {/* 讀 CLASS_DEVICE ,PCI_CLASS_DEVICE 是片內(nèi)偏移地址 */if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))continue;/* 如果 CLASS_DEVICE 為 HOST-PCI橋(北橋),PCI-PCI橋,PCI-ISA橋(南橋)正確返回 */if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)return 1;/* 讀 VENDOR_ID 制造商ID */if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))continue;/* 如果 VENDOR_ID 為 INTEL 或 COMPAQ 正常返回 */if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)return 1;}DBG(KERN_WARNING "PCI: Sanity check failed\n");return 0; } ? ? 檢測(cè)完是“0型”還是“1型”設(shè)備之后,在 raw_pci_ops 中指定對(duì)應(yīng)的讀寫配置空間的方法。

/* 地址是由 總線編號(hào)、設(shè)備號(hào)、片內(nèi)地址 組成 */ #define PCI_CONF1_ADDRESS(bus, devfn, reg) \(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \| (devfn << 8) | (reg & 0xFC))static int pci_conf1_read(unsigned int seg, unsigned int bus,unsigned int devfn, int reg, int len, u32 *value) {unsigned long flags;/* 最多256個(gè)總線 ,256個(gè)設(shè)備 片內(nèi)寄存器范圍 0~4095 */if ((bus > 255) || (devfn > 255) || (reg > 4095)) {*value = -1;return -EINVAL;}spin_lock_irqsave(&pci_config_lock, flags);/* 向地址寄存器 寫要讀取的地址 */outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);/* 從數(shù)據(jù)寄存器讀取數(shù)據(jù) */switch (len) {case 1:*value = inb(0xCFC + (reg & 3));break;case 2:*value = inw(0xCFC + (reg & 2));break;case 4:*value = inl(0xCFC);break;}spin_unlock_irqrestore(&pci_config_lock, flags);return 0; }struct pci_raw_ops {int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,int reg, int len, u32 *val);int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,int reg, int len, u32 val); }; struct pci_raw_ops *raw_pci_ops; /* 設(shè)置全局的 配置空間讀寫函數(shù) */ void __init pci_direct_init(int type) {if (type == 1) {raw_pci_ops = &pci_direct_conf1;raw_pci_ext_ops = &pci_direct_conf1;return;} } ? ? 在內(nèi)核啟動(dòng)過程中,還有一個(gè)PCI相關(guān)的函數(shù)會(huì)被調(diào)用 int __init pci_subsys_init(void) { #ifdef CONFIG_X86_NUMAQpci_numaq_init(); #endif #ifdef CONFIG_ACPIpci_acpi_init(); #endif #ifdef CONFIG_X86_VISWSpci_visws_init(); #endifpci_legacy_init();pcibios_fixup_peer_bridges();pcibios_irq_init();pcibios_init();return 0; } subsys_initcall(pci_subsys_init); struct pci_bus *pci_root_bus; static int __init pci_legacy_init(void) {pci_root_bus = pcibios_scan_root(0);//創(chuàng)建0級(jí)總線if (pci_root_bus)pci_bus_add_devices(pci_root_bus);return 0; } extern struct list_head pci_root_buses; /* list of all known PCI buses */ struct pci_bus * __devinit pcibios_scan_root(int busnum) {struct pci_bus *bus = NULL;struct pci_sysdata *sd;/* 在全局 pci_root_buses 鏈表尋找 總線編號(hào)為 busnum 的總線 */while ((bus = pci_find_next_bus(bus)) != NULL) {if (bus->number == busnum) {/* 如果已經(jīng)存在,返回它 */return bus;}}/* 如果這個(gè)總線編號(hào)不存在, 那么創(chuàng)建這個(gè)Bus */sd = kzalloc(sizeof(*sd), GFP_KERNEL);sd->node = get_mp_bus_to_node(busnum);bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);return bus; } struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,int bus, struct pci_ops *ops, void *sysdata) {struct pci_bus *b;/* 創(chuàng)建 Bus */b = pci_create_bus(parent, bus, ops, sysdata);if (b)b->subordinate = pci_scan_child_bus(b);return b; } unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) {unsigned int devfn, pass, max = bus->secondary;struct pci_dev *dev;/* 探測(cè)總線上的設(shè)備 */for (devfn = 0; devfn < 0x100; devfn += 8)pci_scan_slot(bus, devfn);/* Reserve buses for SR-IOV capability. */max += pci_iov_bus_range(bus);/** After performing arch-dependent fixup of the bus, look behind* all PCI-to-PCI bridges on this bus.*/if (!bus->is_added) {pr_debug("PCI: Fixups for bus %04x:%02x\n",pci_domain_nr(bus), bus->number);pcibios_fixup_bus(bus);if (pci_is_root_bus(bus))bus->is_added = 1;}/* 探測(cè) pci 橋上的設(shè)備,創(chuàng)建子Bus,掛到父 bus->child */for (pass=0; pass < 2; pass++)list_for_each_entry(dev, &bus->devices, bus_list) {if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)max = pci_scan_bridge(bus, dev, max, pass);}/** We've scanned the bus and so we know all about what's on* the other side of any bridges that may be on this bus plus* any devices.** Return how far we've got finding sub-buses.*/pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",pci_domain_nr(bus), bus->number, max);return max; } int pci_scan_slot(struct pci_bus *bus, int devfn) {int fn, nr = 0;struct pci_dev *dev;dev = pci_scan_single_device(bus, devfn);/* 如果是多功能設(shè)備 */if (dev && dev->multifunction) {for (fn = 1; fn < 8; fn++) {dev = pci_scan_single_device(bus, devfn + fn);if (dev) {if (!dev->is_added)nr++;dev->multifunction = 1;}}}return nr; } struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) {struct pci_dev *dev;/* 遍歷 bus->devices 設(shè)備鏈表,查找是否有 devfn 號(hào)設(shè)備存在 */dev = pci_get_slot(bus, devfn);/* 如果已經(jīng)存在,返回它 */if (dev) {pci_dev_put(dev);return dev;}/* 通過訪問配置空間,探測(cè)設(shè)備 */dev = pci_scan_device(bus, devfn);/* 探測(cè)失敗 返回Null */if (!dev)return NULL;/* 探測(cè)成功 */pci_device_add(dev, bus);return dev; } static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) {struct pci_dev *dev;u32 l;int delay = 1;/* 讀 PCI_VENDOR_ID 制造商ID */if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))return NULL;/* id 等于這些值,認(rèn)為探測(cè)失敗 ,返回 */if (l == 0xffffffff || l == 0x00000000 ||l == 0x0000ffff || l == 0xffff0000)return NULL;..../* 探測(cè)成功,分配一個(gè) pci_dev 結(jié)構(gòu) */dev = alloc_pci_dev();dev->bus = bus;dev->devfn = devfn;dev->vendor = l & 0xffff;dev->device = (l >> 16) & 0xffff;/* 讀取配置空間,更詳細(xì)的設(shè)置,指定 dev->bus 等 */if (pci_setup_device(dev)) {kfree(dev);return NULL;}return dev; } int pci_setup_device(struct pci_dev *dev) {u32 class;u8 hdr_type;struct pci_slot *slot;dev->sysdata = dev->bus->sysdata;dev->dev.parent = dev->bus->bridge;/* 設(shè)置 dev 所屬的總線 */dev->dev.bus = &pci_bus_type;dev->hdr_type = hdr_type & 0x7f;dev->multifunction = !!(hdr_type & 0x80);dev->error_state = pci_channel_io_normal;set_pcie_port_type(dev);list_for_each_entry(slot, &dev->bus->slots, list)if (PCI_SLOT(dev->devfn) == slot->number)dev->slot = slot;dev->dma_mask = 0xffffffff;/* 設(shè)備名 */dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),dev->bus->number, PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));/* 設(shè)備類型 */pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);dev->revision = class & 0xff;class >>= 8; /* upper 3 bytes */dev->class = class;class >>= 8;/* need to have dev->class ready */dev->cfg_size = pci_cfg_space_size(dev);/* "Unknown power state" */dev->current_state = PCI_UNKNOWN;/* Early fixups, before probing the BARs */pci_fixup_device(pci_fixup_early, dev);/* device class may be changed after fixup */class = dev->class >> 8;switch (dev->hdr_type) { /* header type */case PCI_HEADER_TYPE_NORMAL: /* standard header */...case PCI_HEADER_TYPE_BRIDGE: /* bridge header *//* 設(shè)置 dev->irq */pci_read_irq(dev);dev->transparent = ((dev->class & 0xff) == 1);/* 設(shè)置 dev->rom_base_reg */pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);set_pcie_hotplug_bridge(dev);break;case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */...break;return 0; } void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) {device_initialize(&dev->dev);dev->dev.release = pci_release_dev;pci_dev_get(dev);dev->dev.dma_mask = &dev->dma_mask;dev->dev.dma_parms = &dev->dma_parms;dev->dev.coherent_dma_mask = 0xffffffffull;pci_set_dma_max_seg_size(dev, 65536);pci_set_dma_seg_boundary(dev, 0xffffffff);/* Fix up broken headers */pci_fixup_device(pci_fixup_header, dev);/* Clear the state_saved flag. */dev->state_saved = false;/* Initialize various capabilities */pci_init_capabilities(dev);/* 將設(shè)備掛入 bus->devices鏈表 */down_write(&pci_bus_sem);list_add_tail(&dev->bus_list, &bus->devices);up_write(&pci_bus_sem); } void pci_bus_add_devices(const struct pci_bus *bus) {struct pci_dev *dev;struct pci_bus *child;int retval;/* 遍歷當(dāng)前總線的 dev ,注冊(cè)設(shè)備 */list_for_each_entry(dev, &bus->devices, bus_list) {/* Skip already-added devices */if (dev->is_added)continue;retval = pci_bus_add_device(dev);if (retval)dev_err(&dev->dev, "Error adding device, continuing\n");}/* 遍歷子總線的dev,注冊(cè)設(shè)備 */list_for_each_entry(dev, &bus->devices, bus_list) {BUG_ON(!dev->is_added);child = dev->subordinate;/** If there is an unattached subordinate bus, attach* it and then scan for unattached PCI devices.*/if (!child)continue;if (list_empty(&child->node)) {down_write(&pci_bus_sem);list_add_tail(&child->node, &dev->bus->children);up_write(&pci_bus_sem);}pci_bus_add_devices(child);/** register the bus with sysfs as the parent is now* properly registered.*/if (child->is_added)continue;retval = pci_bus_add_child(child);if (retval)dev_err(&dev->dev, "Error adding bus, continuing\n");} } int pci_bus_add_device(struct pci_dev *dev) {int retval;/* 將設(shè)備注冊(cè)到 pci_bus_type */retval = device_add(&dev->dev);if (retval)return retval;dev->is_added = 1;pci_proc_attach_device(dev);pci_create_sysfs_dev_files(dev);return 0; }

















?






總結(jié)

以上是生活随笔為你收集整理的PCI驱动框架简单分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。