linux PCI設備初始化過程 start_kernel->rest_init 這個函數會啓動一個核心線程0, 核心線程而後調用init -> do_basic_setup. 而後咱們開始看下面的過程 void __init driver_init(void) { devices_init(); buses_init(); classes_init(); ...... platform_bus_init(); system_bus_init(); ...... } //在drivers/base/core.c int __init devices_init(void) { return subsystem_register(&devices_subsys); } 咱們還看到 decl_subsys(devices, &ktype_device, &device_uevent_ops); //在kobject.h中 #define decl_subsys(_name,_type,_uevent_ops) \ struct subsystem _name##_subsys = { \ .kset = { \ .kobj = { .name = __stringify(_name) }, \ .ktype = _type, \ .uevent_ops =_uevent_ops, \ } \ } 下面咱們看 int subsystem_register(struct subsystem * s) { ...... subsystem_init(s); ...... if (!(error = kset_add(&s->kset))) { if (!s->kset.subsys) s->kset.subsys = s; //指向本身 } ...... } 在subsystem_init中會調用kset_init(&s->kset)在這函數中又主要調用kobject_init(&k->kobj) 這中又kobj->kset = kset_get(kobj->kset); static inline struct kset * kset_get(struct kset * k) { return k ? to_kset(kobject_get(&k->kobj)) : NULL; } 那麼kobj->kset 應該是NULL. 接着 int kset_add(struct kset * k) { //如今k->subsys 也是NULL if (!k->kobj.parent && !k->kobj.kset && k->subsys) k->kobj.parent = &k->subsys->kset.kobj; return kobject_add(&k->kobj); } 在接着 int kobject_add(struct kobject * kobj) { ...... // 這時是 NULL parent = kobject_get(kobj->parent); ...... if (kobj->kset) { ...... if (!parent) parent = kobject_get(&kobj->kset->kobj); //父節點是這個kobject屬於的那個kset list_add_tail(&kobj->entry, &kobj->kset->list); //添加到kset中 ...... } kobj->parent = parent; //指向他屬於的那個kset中的kobject, 若是這個kobject在一個kset中 //在sysfs中建立目錄 error = create_dir(kobj); ...... } 如今咱們瞭解了子系統註冊過程,其餘的就容易多了. int __init buses_init(void) { return subsystem_register(&bus_subsys); } int __init classes_init(void) { ...... retval = subsystem_register(&class_subsys); ...... //class_obj_subsys沒有在sysfs中顯示 subsystem_init(&class_obj_subsys); if (!class_obj_subsys.kset.subsys) class_obj_subsys.kset.subsys = &class_obj_subsys; return 0; } 這個有些不一樣,咱們來看看 int __init platform_bus_init(void) { device_register(&platform_bus); return bus_register(&platform_bus_type); } 定義 struct device platform_bus = { .bus_id = "platform", }; struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .suspend = platform_suspend, .resume = platform_resume, }; 上面的函數之後若是用到咱們會在看.接下來咱們繼續 int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } void device_initialize(struct device *dev) { //定義在kobject.h中 //#define kobj_set_kset_s(obj,subsys) (obj)->kobj.kset = &(subsys).kset //那麼這個設備是在devices_subsys中 kobj_set_kset_s(dev, devices_subsys); //初始化設備中的kobject kobject_init(&dev->kobj); klist_init(&dev->klist_children, klist_children_get, klist_children_put); } int device_add(struct device *dev) { ...... //增長引用計數 dev = get_device(dev); ...... //copy 名字到 kobject 的 name 中 kobject_set_name(&dev->kobj, "%s", dev->bus_id); ...... if ((error = kobject_add(&dev->kobj))) goto Error; ...... } 下面咱們看總線註冊. struct bus_type { const char * name; struct subsystem subsys; ...... }; int bus_register(struct bus_type * bus) { ...... //設置名字 retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); ...... //在kobject.h中定義了 // #define subsys_set_kset(obj,_subsys) (obj)->subsys.kset.kobj.kset = &(_subsys).kset subsys_set_kset(bus, bus_subsys); //註冊這個總線類型中的子系統 retval = subsystem_register(&bus->subsys); ...... //bus中的devices是個kset kobject_set_name(&bus->devices.kobj, "devices"); bus->devices.subsys = &bus->subsys; //指向總線類型的子系統 retval = kset_register(&bus->devices); //註冊kset ...... kobject_set_name(&bus->drivers.kobj, "drivers"); bus->drivers.subsys = &bus->subsys; bus->drivers.ktype = &ktype_driver; //類型初始化 retval = kset_register(&bus->drivers); ...... //添加屬性,屬性在sysfs中被做爲文件描述 bus_add_attrs(bus); ...... } 咱們繼續 int __init system_bus_init(void) { //這個subsys的parent是devices_subsys的kobj,因此他應該在devices目錄下面 system_subsys.kset.kobj.parent = &devices_subsys.kset.kobj; return subsystem_register(&system_subsys); } 如今咱們看完了的driver_init();中的大多數初始化過程,下面會調用do_initcalls();這個函數會調用module_init等編譯的函數,若是不明白看module_init的說明。 下面咱們看pci的註冊。 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, .shutdown = pci_device_shutdown, .resume = pci_device_resume, .dev_attrs = pci_dev_attrs, }; static int __init pci_driver_init(void) { //pci總線類型也會被添加到bus目錄下,上面有爲何. return bus_register(&pci_bus_type); } postcore_initcall(pci_driver_init); //調用優先級高 下一個優先級就是 static __init int pci_access_init(void) { ...... //會打印 "PCI: PCI BIOS revision ..." pci_pcbios_init(); ...... //會打印 "PCI: Using configuration ..." pci_direct_init(); ...... } arch_initcall(pci_access_init);//比上面那個還要高 void __init pci_direct_init(void) { ...... //在 kernel/resource.c /*struct resource ioport_resource = { .name = "PCI IO", .start = 0, .end = IO_SPACE_LIMIT, .flags = IORESOURCE_IO, }; 中獲取io資源範圍,若是成功會把新的範圍添加到上面的數據結構中*/ region = request_region(0xCF8, 8, "PCI conf1"); ...... //做一些具體的檢測,向io端口查詢相關信息 if (pci_check_type1()) { ...... raw_pci_ops = &pci_direct_conf1; return; //ok } ...... } 下面到了 //很簡單 會打印 "Setting up standard PCI resources" arch/i386/kernel/setup.c static int __init request_standard_resources(void); 繼續 static int __init pci_legacy_init(void) { ...... pci_root_bus = pcibios_scan_root(0); //pci 枚舉已經完成 if (pci_root_bus) pci_bus_add_devices(pci_root_bus); //尋找剩餘的總線,萬一有一個主機橋總線 pcibios_fixup_peer_bridges(); //本身看吧,很間單 return 0; } 首先 struct pci_bus * __devinit pcibios_scan_root(int busnum) { ...... //根據總線號查找總線 while ((bus = pci_find_next_bus(bus)) != NULL) { if (bus->number == busnum) { ...... } } ...... return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, NULL); } //arch/i386/pci/common.c struct pci_ops pci_root_ops = { .read = pci_read, .write = pci_write, }; struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata) { ...... //建立一個pci總線 b = pci_create_bus(parent, bus, ops, sysdata); if (b) b->subordinate = pci_scan_child_bus(b); //最大總線號 return b; } struct pci_bus * __devinit pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata) { ...... struct pci_bus *b; struct device *dev; 分配pci總線數據結構 b = pci_alloc_bus(); ...... //分配設備文件 dev = kmalloc(sizeof(*dev), GFP_KERNEL); ...... b->sysdata = sysdata; b->ops = ops; //總線操做 //查找是否存在同一總線 if (pci_find_bus(pci_domain_nr(b), bus)) { ...... } ...... //添加這個總線到全局隊列中 list_add_tail(&b->node, &pci_root_buses); ...... //bus_id中應該是pci0000:00 sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); error = device_register(dev); ...... //是一個橋設備 b->bridge = get_device(dev); //pcibus_class 在 driver/pci/probe.c 中已經註冊 b->class_dev.class = &pcibus_class; //0000:00 sprintf(b->class_dev.class_id, "%04x:%02x", pci_domain_nr(b), bus); //添加這個class device到 error = class_device_register(&b->class_dev); ...... //在這個目錄下 0000:00建立一個屬性文件 cpuaffinity error = class_device_create_file(&b->class_dev, &class_device_attr_cpuaffinity); ...... //建立一個軟鏈接到pci0000:00 上面咱們已經看到 error = sysfs_create_link(&b->class_dev.kobj, &b->bridge->kobj, "bridge"); ...... b->number = b->secondary = bus; // 0 b->resource[0] = &ioport_resource; b->resource[1] = &iomem_resource; return b; ...... } 下面咱們看掃描 unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) { //max = 0 unsigned int devfn, pass, max = bus->secondary; ...... for (devfn = 0; devfn < 0x100; devfn += 8) pci_scan_slot(bus, devfn); ...... //讀取橋的資源 pcibios_fixup_bus(bus); //掃描完成根總線的全部設備了 //下面進一步掃描橋上的設備 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); //看下面 } ...... } //掃描pci插槽 int __devinit pci_scan_slot(struct pci_bus *bus, int devfn) { ...... for (func = 0; func < 8; func++, devfn++) { struct pci_dev *dev; dev = pci_scan_single_device(bus, devfn); ...... } ...... } struct pci_dev * __devinit pci_scan_single_device(struct pci_bus *bus, int devfn) { ...... dev = pci_scan_device(bus, devfn); ...... pci_device_add(dev, bus); pci_scan_msi_device(dev); // ? ...... } //查找設備 //首先看一看 driver/pci/access.c 中 #define PCI_OP_READ(size,type,len) \ int pci_bus_read_config_##size \ (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \ { \ int res; \ unsigned long flags; \ u32 data = 0; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ spin_lock_irqsave(&pci_lock, flags); \ res = bus->ops->read(bus, devfn, pos, len, &data); \ *value = (type)data; \ spin_unlock_irqrestore(&pci_lock, flags); \ return res; \ } //上面列的不全,去看文件吧 static struct pci_dev * __devinit pci_scan_device(struct pci_bus *bus, int devfn) { ...... if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) return NULL; ...... //重試 while (l == 0xffff0001) { ...... if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) ...... } //讀類型 if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) return NULL; dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); dev->bus = bus; dev->sysdata = bus->sysdata; //struct pci_dev 中嵌入了一個 struct device dev; dev->dev.parent = bus->bridge; //設備的父指針是pci_bus->device設備 dev->dev.bus = &pci_bus_type; //設備的總線類型 dev->devfn = devfn; dev->hdr_type = hdr_type & 0x7f; dev->multifunction = !!(hdr_type & 0x80); // dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; dev->cfg_size = pci_cfg_space_size(dev); //讀取設備配置空間大小 dev->error_state = pci_channel_io_normal; dev->dma_mask = 0xffffffff; //初始化設備資源 if (pci_setup_device(dev) < 0) { ...... } return dev; } static int pci_setup_device(struct pci_dev * dev) { ...... switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) goto bad; pci_read_irq(dev); //讀取irq pci_read_bases(dev, 6, PCI_ROM_ADDRESS); //一會看這個 pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); break; ...... } ...... } static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) { ...... for(pos=0; pos<howmany; pos = next) { ...... res = &dev->resource[pos]; //資源一個個配置 //讀取一些信息 ...... if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { //io內存空間 //調整空間大小 sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); if (!sz) continue; res->start = l & PCI_BASE_ADDRESS_MEM_MASK; //資源開始 res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; } else { // io 端口空間,與內存空間相似 ...... } res->end = res->start + (unsigned long) sz; res->flags |= pci_calc_resource_flags(l); ...... //這還有處理64位地址,略. } if (rom) { dev->rom_base_reg = rom; res = &dev->resource[PCI_ROM_RESOURCE]; //第6項 ...... //差很少同樣和上面 } } 到這咱們掃描到了一個設備,也進行了初始化,下面就是 void __devinit pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { device_initialize(&dev->dev); ...... //添加到隊列中 list_add_tail(&dev->bus_list, &bus->devices); ...... } //進一步掃描橋設備 int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass) { ..... //總線已經被bios設置好了 if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) { if (pass) //第二次 goto out; ...... //已經存在 if (pci_find_bus(pci_domain_nr(bus), busnr)) { goto out; } //不存在分配一個新總線 child = pci_add_new_bus(bus, dev, busnr); ...... //看,又到這了,在這個總線上繼續掃描設備或其餘總線 cmax = pci_scan_child_bus(child); ...... } else { //總線尚未被設置,會初始化這個總線 if (!pass) { //第一次 ...... goto out; } ...... child = pci_add_new_bus(bus, dev, ++max); ...... if (!is_cardbus) { max = pci_scan_child_bus(child); //又看到了 } else { ...... } ...... } ...... } struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) { ...... child = pci_alloc_child_bus(parent, dev, busnr); ...... list_add_tail(&child->node, &parent->children); //添加到隊列中 ...... } static struct pci_bus * __devinit pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { ...... child = pci_alloc_bus(); ...... child->self = bridge; //總線本身的設備描述 child->parent = parent; child->ops = parent->ops; child->sysdata = parent->sysdata; child->bus_flags = parent->bus_flags; child->bridge = get_device(&bridge->dev); //橋也是本身設備pci_dev->device child->class_dev.class = &pcibus_class; //也是pcibus_class sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr); class_device_register(&child->class_dev); class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity); child->number = child->secondary = busnr; //總線號初始化 child->primary = parent->secondary; child->subordinate = 0xff; for (i = 0; i < 4; i++) { //資源初始化 PCI_BRIDGE_RESOURCES ( i386 is 7) child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; child->resource[i]->name = child->name; } bridge->subordinate = child; return child; } 如今pci枚舉所有完成,到了添加這些設備了 pci_legacy_init -> void __devinit pci_bus_add_devices(struct pci_bus *bus) { ...... //遍歷總線上的每一個設備 list_for_each_entry(dev, &bus->devices, bus_list) { if (!list_empty(&dev->global_list)) //已經添加 continue; pci_bus_add_device(dev); } list_for_each_entry(dev, &bus->devices, bus_list) { ...... //若是是一個橋設備 if (dev->subordinate) { if (list_empty(&dev->subordinate->node)) { //這個橋是獨立的 ...... list_add_tail(&dev->subordinate->node, &dev->bus->children); //添加這個總線到上一級總線隊列中 ...... } pci_bus_add_devices(dev->subordinate); //遞歸調用 //建立鏈接 sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge"); } } } void __devinit pci_bus_add_device(struct pci_dev *dev) { device_add(&dev->dev); ...... list_add_tail(&dev->global_list, &pci_devices); //添加到全局連表 ...... } 到此咱們所有看完了pci_legacy_init(),也就是pci所有設備枚舉與初始化過程,可是你可能想到設備中的struct resource尚未被請求,下面咱們就繼續看這部分. 這是在pcibios_init(void) -> void __init pcibios_resource_survey(void)中 void __init pcibios_resource_survey(void) //arch/i386/pci/i386.c { pcibios_allocate_bus_resources(&pci_root_buses); pcibios_allocate_resources(0); pcibios_allocate_resources(1); } 咱們仍是一個一個看. static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) { ...... //查詢全部pci總線 list_for_each_entry(bus, bus_list, node) { if ((dev = bus->self)) { for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { ...... r = &dev->resource[idx]; ...... //找到包含這個資源的父節點,也就是看總線資源是否包含這個資源 pr = pci_find_parent_resource(dev, r); //若是有任何錯誤 if (!r->start || !pr || request_resource(pr, r) < 0) { ...... r->flag = 0; } } } pcibios_allocate_bus_resources(&bus->children); //遞歸調用 } } 下面看 static void __init pcibios_allocate_resources(int pass) //參數 一次是0, 一次是1 { ...... for_each_pci_dev(dev) { //遍歷全部設備 pci_read_config_word(dev, PCI_COMMAND, &command); for(idx = 0; idx < 6; idx++) { ...... if (pass == disabled) { pr = pci_find_parent_resource(dev, r); if (!pr || request_resource(pr, r) < 0) { //若是有錯誤 ...... } } if (!pass) { r = &dev->resource[PCI_ROM_RESOURCE]; //處理這種資源 ...... } } } } 看到這裏咱們能夠總結一下了. 1 全部的 struct class 註冊到 class_subsys下 2 全部的 struct class_device 註冊到相應的 class 下 3 全部的 struct device 註冊到 device_subsys 下 4 全部的 *_sub_type 註冊到 bus_subsys 下 任何類型的 *_subsys是最高級別的對象了,都會在/sys/下展示. pci_root_buses是全部pci總線連表的頭,pci_devices是全部pci設備連表的頭. //還能夠繼續 fs_initcall(pcibios_assign_resources);