linux PCI设备初始化过程

时间:2022-01-31 02:44:26
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 ;
}
这个有些不同,我们来看看
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, , "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();
//pci 枚举已经完成
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
//寻找剩余的总线,万一有一个主机桥总线
pcibios_fixup_peer_bridges(); //自己看吧,很间单
return ;
}
首先
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; //
b->resource[] = &ioport_resource;
b->resource[] = &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 = ; devfn < 0x100; devfn += )
pci_scan_slot(bus, devfn);
......
//读取桥的资源
pcibios_fixup_bus(bus);
//扫描完成根总线的所有设备了
//下面进一步扫描桥上的设备
for (pass=; pass < ; 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 = ; func < ; 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 = ; \
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 >> ) & 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) < ) {
......

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, , 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=; 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 = ; i < ; 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();
pcibios_allocate_resources();
}
我们还是一个一个看.
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) < ) {
......
r->flag = ;
}
}
}
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 = ; idx < ; idx++) {
......
if (pass == disabled) {
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < ) { //如果有错误
......
}
}
if (!pass) {
r = &dev->resource[PCI_ROM_RESOURCE]; //处理这种资源
......
}
}
}
}
看到这里我们可以总结一下了.
所有的 struct class 注册到 class_subsys下
所有的 struct class_device 注册到相应的 class 下
所有的 struct device 注册到 device_subsys 下
所有的 *_sub_type 注册到 bus_subsys 下
任何类型的 *_subsys是*别的对象了,都会在/sys/下展现.
pci_root_buses是所有pci总线连表的头,pci_devices是所有pci设备连表的头.
//还可以继续
fs_initcall(pcibios_assign_resources);