一、PCI 概念介紹
PCI是CPU和外圍設備通信的高速傳輸總線。PCI規(guī)范能夠實現32位并行數據傳輸,工作頻率為 33MHz 或 66MHz ,最大吞吐率高達266MB/s,PCI的衍生物包括 CardBus、mini-PCI、PCI-Express、cPCI等。
PCI總線體系結構是一種層次式的體系結構。在這種層次體系結構中,PCI橋設備占據著重要的地位,它將父總線與子總線連接在一起,從而使整個系統看起來像一個倒置的樹狀結構,樹的頂端是CPU,它通過一個較為特殊的CPI橋設備-Host/PCI橋設備與根PCI總線連接起來。
作為特殊的PCI設備,PCI橋包括以下幾種:
HOST/PCI橋,用于連接CPU與PCI根總線,第一個根總線的編號為0。在PC中,內存控制器也通常被集成到Host/PCI橋設備芯片中,因此,Host/PCI橋也通常被稱為“北橋”芯片組。
PCI/ISA橋,用作連接舊的ISA總線,通常,PCI中的類似的i8359A中斷控制器這樣的設備也會被集成到PCI/ISA橋設備中,因此,PCI/ISA橋通常也被稱作“南橋”芯片組。
PCI-to-PCI橋,用于連接PCI主總線與次總線,PCI橋所處的總線被稱作“主總線”(父總線),PCI橋設備所連接的總線為“次總線”(子總線)。

二、PCI設備與配置空間
在i386系統結構中,對內存的訪問和對輸入/輸出寄存器的訪問通過兩套不同的指令完成,所有的存儲器和IO兩個不同的地址空間。一般而言,內存的物理地址以及輸入/輸出寄存器的地址是由硬件決定的,不過對于內存的物理地址還可以通過地址映射機制來一次轉換(I/O也可以映射)??墒牵鯓犹幚硗庠O的存儲空間呢?理想的辦法是系統軟件自動設置,思路是:
1、外設通過某種途徑告訴系統,它有幾個存儲區(qū)間以及I/O地址空間,每個區(qū)間是多大,以及各自在本地的地址,顯然這些地址都是局部的內部的,都從0開始算起。
2、系統軟件在知道了一共有多少外設,各自又有什么樣的存儲區(qū)間以后,就可以為這些區(qū)間分配“物理地址”,并且建立起這些區(qū)間與總線之間的連接,以后就可以通過這些地址來訪問。顯然,這里所謂的“物理地址”與真正的物理地址還是有些區(qū)別的,它實際上也是一種邏輯地址,所以常成為“總線地址”,因為這是CPU在總線上所看到的地址??上攵?,外設上一定有著某種地址映射機制。所謂的“為外設分配地址”,就是為其分配總線地址,并建立起映射。
PCI設備上存在許多完成上述工作的寄存器(配置空間),那么系統初始化的時候如何訪問這些寄存器該何如?對于i386結構的處理器,PCI總線的設計者在I/O地址空間保留了8個字節(jié)用于這個目的,那就是0xCF8~0xCFF,這8個字節(jié)的地址空間構成了兩個32位的寄存器,第一個是“地址寄存器”0xCF8,第二個是“數據寄存器”0xCFC,要訪問配置空間的寄存器時,CPU先向地址寄存器寫入目標地址,然后通過數據寄存器進行讀寫數據。不過,寫入地址寄存器的目標地址是一種包括總線號、設備號、功能號以及配置寄存器地址的綜合地址。每個PCI設備最多有8個功能,所以設備號和功能號組合在一起又被稱作“邏輯設備”號。

如上圖所示,PCI標準規(guī)定每個設備的配置寄存器組最多可以有256字節(jié)的連續(xù)空間,其中開頭的64字節(jié)的用途和格式是標準的,成為配置寄存器組的“頭部”,這樣的頭部又有兩種,“0型”頭部用于一般的PCI設備,“1型”頭部用于PCI橋,無論是“0型”還是“1型”,其開頭的16個字節(jié)的用途和格式是共同的。
三、PCI驅動框架分析
在內核中與PCI相關的結構體大概有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;
- };
幾個重要的成員:
children: PCI橋可以使當前總線得到擴展,當前總線上有幾個PCI橋,那么當前總線就會擁有幾個子總線,子總線會連接到父總線的children鏈表中。
device: 連接在這條總線上的設備鏈表。
ops: 當前總線訪問總線上設備配置空間的 read、write 方法。
在內核啟動的過程中,首先會創(chuàng)建0級總線,然后枚舉探測0級總線上的設備,如果是PCI橋,那么還要進入下一級子總線,最終所有的連接的PCI設備都將被探測到,詳細的探測過程,我們在后邊分析。
3.2 pci_bus_type
看到 bus_type 顯然這是個設備總線驅動模型里的“總線”,與前邊提到的 pci_bus ,完全是兩碼事,那么pci_driver 和 pci_dev 就是注冊到 pci_bus_type 的驅動和設備。分析總線設備驅動模型的時候,總要分析一下它的 match 函數(匹配規(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設備與驅動的匹配方式有兩種,一種是通過 pci_driver->dynids ,另一種是通過 pci_driver->idtable 。使用idtable 是總線設備驅動模型中常用的匹配方法,一般都是通過設備名來匹配,但是PCI比較特殊,它是通過設備的 vendor
、subvendor 、device 、subdevice 來匹配(這些都是在配置空間里可以讀取到的)。
至于 pci_driver->dynids ,它是通過用戶空間給驅動增加匹配條件的一種方法(還記得I2C可以在用戶空間創(chuàng)建設備嗎,一樣的)。
- 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 函數中會調用到一個 pci_create_newid_file 函數,它在 sysfs 文件系統中會創(chuàng)建一個 new_id 的屬性文件,通過這個屬性文件,我們就可以來為該驅動增加匹配條件。
內核幫助文檔有說明:
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
對于這種方法不在詳細分析。
分析完設備總線驅動模型,我想整個PCI驅動的框架就非常清楚了,內核啟動時,通過pci_bus之間的關系枚舉出所有的 PCI 設備,并為每一個 PCI 設備創(chuàng)建一個 pci_dev ,根據配置空間的信息填充 pci_dev 之后,注冊到pci_bus_type 。而,我們寫的 pci_driver 在 idtable 里指定它所支持的設備信息,同樣也注冊到 pci_bus_type中去,信息一致匹配成功則調用 driver->probe 函數,然后你可以注冊字符設備、塊設備等等。
四、PCI設備的枚舉探測過程
在內核啟動過程中,PCI設備的探測過程是完全自動的,內核已經集成好了方法,我們無需更改,在這里還是分析一邊代碼作為了解。
分析之前,先看一下全部的函數調用關系,大致了解一下
- <span style="font-size:10px;">pci_arch_init /* 判斷host/pci橋的類型 */
- pci_direct_probe
- pci_check_type1
- pci_sanity_check
-
- pci_direct_init
- raw_pci_ops = &pci_direct_conf1;
- raw_pci_ext_ops = &pci_direct_conf1;
-
- /* 第二個過程,枚舉各級總線上的設備 */
- pci_subsys_init
- pci_legacy_init
- pcibios_scan_root
- pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);
- pci_create_bus(parent, bus, ops, sysdata); // 創(chuàng)建 0 級總線
- pci_scan_child_bus(b); // 探測當前總線設備以及子總線、子總線設備
- pci_scan_slot(bus, devfn); // 探測當前總線的設備
- pci_scan_single_device(bus, devfn); // 探測單功能設備
- pci_scan_single_device(bus, devfn + fn); //探測多功能設備
- pci_scan_device(bus, devfn); //通過配置空間 枚舉設備
- pci_setup_device //根據配置空間信息,設置pci_dev
- pci_device_add(dev, bus);
- list_add_tail(&dev->bus_list, &bus->devices); // 將探測到的設備加入到當前總線的設備鏈表
- pci_scan_bridge //此時已經完成當前總線設備的探測,如果這些設備里有PCI橋,那么進入下一級,探測橋下的設備
- child = pci_add_new_bus(bus, dev, busnr);
- pci_scan_child_bus(child); // 進入下一級探測
- pci_bus_add_devices // 全部設備探測完畢,注冊設備。
- pci_bus_add_device(dev);
- device_add // 將設備注冊到 pci_bus_type
- pci_bus_add_devices(child); //它最終也會調用到 device_add 將各個子總線上的設備注冊到 pci_bus_type
下面來看具體的探測過程。
- static __init int pci_arch_init(void)
- {
- #ifdef CONFIG_PCI_DIRECT
- int type = 0;
- type = pci_direct_probe();
- #endif
-
- #ifdef CONFIG_PCI_BIOS
- pci_pcbios_init();
- #endif
-
- #ifdef CONFIG_PCI_DIRECT
- pci_direct_init(type);
- #endif
-
- dmi_check_pciprobe();
-
- dmi_check_skip_isa_align();
-
- return 0;
- }
- arch_initcall(pci_arch_init);
這個函數是放在 init 段中,內核啟動時會調用。
- int __init pci_direct_probe(void)
- {
- struct resource *region, *region2;
- /* 申請IO資源 */
- region = request_region(0xCF8, 8, "PCI conf1");
-
- /* 探測那種類型 ,0型(PCI設備)和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橋為例,看看是如何判斷類型的。
- 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);
- /* 判斷設備類型 */
- 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 是片內偏移地址 */
- 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;
- }
檢測完是“0型”還是“1型”設備之后,在 raw_pci_ops 中指定對應的讀寫配置空間的方法。
- /* 地址是由 總線編號、設備號、片內陸址 組成 */
- #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個總線 ,256個設備 片內寄存器范圍 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);
-
- /* 從數據寄存器讀取數據 */
- 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;
- /* 設置全局的 配置空間讀寫函數 */
- void __init pci_direct_init(int type)
- {
- if (type == 1) {
- raw_pci_ops = &pci_direct_conf1;
-
- raw_pci_ext_ops = &pci_direct_conf1;
- return;
- }
- }
在內核啟動過程中,還有一個PCI相關的函數會被調用- int __init pci_subsys_init(void)
- {
- #ifdef CONFIG_X86_NUMAQ
- pci_numaq_init();
- #endif
- #ifdef CONFIG_ACPI
- pci_acpi_init();
- #endif
- #ifdef CONFIG_X86_VISWS
- pci_visws_init();
- #endif
- pci_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級總線
- 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 鏈表尋找 總線編號為 busnum 的總線 */
- while ((bus = pci_find_next_bus(bus)) != NULL) {
- if (bus->number == busnum) {
- /* 如果已經存在,返回它 */
- return bus;
- }
- }
-
- /* 如果這個總線編號不存在, 那么創(chuàng)建這個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;
-
- /* 探測總線上的設備 */
- 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;
- }
- /* 探測 pci 橋上的設備,創(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);
-
- /* 如果是多功能設備 */
- 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 設備鏈表,查找是否有 devfn 號設備存在 */
- dev = pci_get_slot(bus, devfn);
- /* 如果已經存在,返回它 */
- if (dev) {
- pci_dev_put(dev);
- return dev;
- }
- /* 通過訪問配置空間,探測設備 */
- dev = pci_scan_device(bus, devfn);
- /* 探測失敗 返回Null */
- if (!dev)
- return NULL;
- /* 探測成功 */
- 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 等于這些值,認為探測失敗 ,返回 */
- if (l == 0xffffffff || l == 0x00000000 ||
- l == 0x0000ffff || l == 0xffff0000)
- return NULL;
- ....
-
- /* 探測成功,分配一個 pci_dev 結構 */
- dev = alloc_pci_dev();
-
- dev->bus = bus;
- dev->devfn = devfn;
- dev->vendor = l & 0xffff;
- dev->device = (l >> 16) & 0xffff;
- /* 讀取配置空間,更詳細的設置,指定 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;
-
- /* 設置 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;
- /* 設備名 */
- 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));
- /* 設備類型 */
- 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 */
- /* 設置 dev->irq */
- pci_read_irq(dev);
- dev->transparent = ((dev->class & 0xff) == 1);
- /* 設置 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);
-
- /* 將設備掛入 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;
- /* 遍歷當前總線的 dev ,注冊設備 */
- 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,注冊設備 */
- 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;
-
- /* 將設備注冊到 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;
- }
|