linux PCI设备初始化过程
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);
posted on 2013-08-30 11:35 SuperKing 阅读(4055) 评论(0) 编辑 收藏 举报