zoukankan      html  css  js  c++  java
  • 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 注册到相应的 class3 所有的 struct device 注册到 device_subsys 下
    4 所有的 *_sub_type 注册到 bus_subsys 下
    任何类型的 *_subsys是最高级别的对象了,都会在/sys/下展现.
    pci_root_buses是所有pci总线连表的头,pci_devices是所有pci设备连表的头.
    //还可以继续
    fs_initcall(pcibios_assign_resources);
  • 相关阅读:
    书籍下载点
    总结一下散乱的开发点滴(3) (高手勿入)
    [收藏]Dynamic Controls in ASP.NET
    [收藏] ORACLE中函数
    面试题收集,面试和被面试中煎熬~~
    一句SQL得出表中重复的数据(TMP)
    总结一下散乱的开发点滴(4) (高手勿入)
    一个同步的例子
    关于学习的反思(下)系网开发记(4)
    关于学习的反思(上)系网开发记(2)
  • 原文地址:https://www.cnblogs.com/super-king/p/3291086.html
Copyright © 2011-2022 走看看