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);

 

posted on 2013-08-30 11:35  SuperKing  阅读(4055)  评论(0编辑  收藏  举报

导航