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);
相關文章
相關標籤/搜索