Chinaunix首页 | 论坛 | 博客
  • 博客访问: 395490
  • 博文数量: 119
  • 博客积分: 1796
  • 博客等级: 上尉
  • 技术积分: 890
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-14 10:16
个人简介

守正

文章分类
文章存档

2013年(1)

2011年(40)

2010年(78)

分类: LINUX

2011-08-31 16:13:36

linux 2.6.23-PCI总线枚举源代码分析
linux 2.6.23-PCI总线枚举源代码分析


pci总线枚举是在initcalls被调用的时候进行的,do_initcalls分别调用很多init节中的的函数,调用顺序

由类似__define_initcall("0",fn,1)宏定义的参数决定。当调用到pcibios_init的时候开始进行总线初始化

pcibios_init-->pcibios_sort-->while (!list_empty(&pci_devices)) { 

ln = pci_devices.next;

dev = pci_dev_g(ln);

idx = found = 0;

while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) {

idx++;

list_for_each(ln, &pci_devices) {

d = pci_dev_g(ln);//d为pci_dev

...... -->pci_bios_find_device:此为汇编代码

struct pci_bus * __devinit pcibios_scan_root(int busnum)

{

struct pci_bus *bus = NULL;

dmi_check_system(pciprobe_dmi_table);

while ((bus = pci_find_next_bus(bus)) != NULL) {

if (bus->number == busnum) {

return bus;

}

}

printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum);

return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, NULL);-->|pci_bus *b = pci_create_bus(parent, bus, ops, sysdata)

} |pci_scan_child_bus(b)-->见下面代码:

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)

{

unsigned int devfn, pass, max = bus->secondary;

struct pci_dev *dev;

pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);

for (devfn = 0; devfn < 0x100; devfn += 8)

pci_scan_slot(bus, devfn);

pr_debug("PCI: Fixups for bus %04x:%02x\n", pci_domain_nr(bus), bus->number);

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

}

pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",

pci_domain_nr(bus), bus->number, max);

return max;



以上最重要的函数就是:pci_scan_slot(bus, devfn)-->dev = pci_scan_single_device(bus, devfn)-->|dev = pci_scan_device(bus, devfn)这个函数就是我一直找的建立pci_dev的函数,终于找到了,这个函数主要就是读取设备的配置空间,检查槽是否被占用,若被占用则分配一个pci_dev结构将之初始化并挂入当前的bus

|pci_device_add(dev, bus)这个函数是将上面返回的pci_dev结构挂入当前bus的设备链表,在之后的时间将要把这个Bus的所有设备挂入pci_devices全局链表

|pci_scan_msi_device(dev)这个是一个支持选项,可以将中断写入一个特定空间,然后这个空间向cpu发中断

pci_scan_bridge(bus, dev, max, pass)-->pci_scan_child_bus(child)这里是个递归的树建立过程

以上就是建立pci树的过程这些都是在pci初始化代码中建立的,每发现一个可用的pci设备就建立一个pci_dev

结构体,并且将之挂入pci_devices队列

上面是从pcibios_scan_root开始的,但是到底是谁调用的pcibios_scan_root呢?继续分析:

遍查代码后发现是pci_legacy_init调用的,但是再往前呢?很有意思,和读侦探小说一样,继续吧:注意在此

函数中有if (pcibios_scanned++)return 0;这是判断是否已经遍历过一次了,若是则不做了,直接返回,若不是

则进一步调用pcibios_scan_root,接下来分析是谁调用了pci_legacy_init,我估计马上就可以把一切联系起来了 

当我遍查代码的时候发现没有一个函数显示调用它,原来他是subsys_initcall调用的内存位置时.initcall4.init

它的级别是4,它是在do_basic_setup中的do_initcalls中被调用的,看它的定义也可以:subsys_initcall(pci_legacy_init)

一切基本上结束了,它的级别是4,属于subsys范畴,接下来关于pci的就是调用级别5,6...了,他们是pcibios_init,

下面跟踪一下:

pcibios_init-->pcibios_sort-->...这不用了,最上面已经有了

另外pci_scan_bus也可以开始pci枚举过程,它和pci_scan_root差不多,它是:

static inline struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata)

{

struct pci_bus *root_bus;

root_bus = pci_scan_bus_parented(NULL, bus, ops, sysdata);

if (root_bus)

pci_bus_add_devices(root_bus);

return root_bus;

}

只是它一般是在枚举过后进行fixup时候调用的,比如pci_fixup_i450nx(struct pci_dev *d)中就会调用

由此我认为在启动的时候并不是一次进行枚举,而是进行了很多次枚举,每次fixup的时候一般都要进行枚举

再看看pci_access_init的跟踪:

static __init int pci_access_init(void)

{

#ifdef CONFIG_PCI_MMCONFIG

pci_mmcfg_init();

#endif

if (raw_pci_ops)

return 0;

#ifdef CONFIG_PCI_BIOS

pci_pcbios_init();-->pci_find_bios-->check_pcibios这个函数是一个内联汇编代码

#endif

if (raw_pci_ops)

return 0;

#ifdef CONFIG_PCI_DIRECT

pci_direct_init();

#endif

return 0;

}

arch_initcall(pci_access_init);

总结一下:在start_kernel的最后一步启用函数rest_init,它内部启动内核进程init,然后调用do_initcalls

开始进行二期初始化,比如设备初始化,我这个分析中主要讨论了pci初始化,在进行设备初始化配置的时候(比

如中断号的分配)可能用到bios服务,从设备的配置空间读取中断号,这就要在pci枚举之前首先找到bios为pci服务

的地址,这个在pci_access_init中指定,然后就开始了总线枚举,完事以后pci_devices全局链表就被填充了,然后

就开始pcibios_init了,这就是一切过程。这里仅仅把设备初始化了,但是还是得需要驱动才能使设备开始工作。

这就和前些天我研究的统一的设备模型联系起来了。

终于把pci枚举的来龙去脉弄明白了,这个问题是从我看usb驱动源代码的时候引出的,当时我发现usb的主机

控制器是个pci设备,于是就一路跟下来了,其间读了irq的代码,明白了中断号的分配,中断的处理过程。在

这个分析的过程里学习了几段代码,就是在start_kernel里的处理初始参数的问题,一共有两个,一个是特权参数

的解析,一个是非特权参数的解析,初始化参数和initcalls都在内核映像的_init_begin和_init_end之间,这种安排

我今天才明白,感觉最近分析代码以后对Linux越来越清晰了,当遇到困难的时候,阅读代码是最好的方法。

看了几天Linux源代码,感觉挺好的,现在十分想知道windows是怎么实现的,可是windows的代码不会让我看的!
//--------------------------------------------------------------------------------------------------------------------------------
原文地址:
阅读(1159) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~