博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
总线注册(platform,PCI)
阅读量:4095 次
发布时间:2019-05-25

本文共 5609 字,大约阅读时间需要 18 分钟。

在内核中可能存在不同种类的总线,需要将这些不同的内存存入内核中进行管理。这里,以platform总线和pci总线为例来进行说明。

platform总线是一种虚拟总线,当某一设备与处理器直接连接时,可以利用该虚拟总线来模拟设备通过platform总线连接到处理器这一结构。

内核中,关于platform总线的注册位于drivers/base/platform.c文件中。

int __init platform_bus_init(void){
int error; early_platform_cleanup(); //该函数的实现与CPU架构相关,当前分析的架构中其实现为空 error = device_register(&platform_bus); //注册总线设备,关于总线设备的定义如下: //struct device platform_bus = {
// .init_name = "platform", //}; if (error) {
put_device(&platform_bus); return error; } error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); of_platform_register_reconfig_notifier(); return error;}

关于设备的注册,如下:

int device_register(struct device *dev){
device_initialize(dev); return device_add(dev);}//可见,设备注册主要完成两个任务,分别是:初始化设备,添加设备void device_initialize(struct device *dev){
dev->kobj.kset = device_kset; //设置该设备所属的集合 kobject_init(&dev->kobj, &device_ktype); //设置该设备所属类型 //上边的操作与内核所提出的设备模型机制相关,利用该机制,内核可对所有的设备进行管理 INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex);#ifdef CONFIG_PROVE_LOCKING mutex_init(&dev->lockdep_mutex);#endif lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_pm_init(dev); set_dev_node(dev, -1); //设置该设备关联的NUMA节点,此时设置为-1#ifdef CONFIG_GENERIC_MSI_IRQ INIT_LIST_HEAD(&dev->msi_list);#endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER;}int device_add(struct device *dev){
struct device *parent; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; struct kobject *glue_dir = NULL; dev = get_device(dev); //该函数的主要目的为对dev->kref->refcount变量进行自增 if (!dev) goto done; if (!dev->p) {
error = device_private_init(dev); //该函数主要用来初始设备的私有数据 if (error) goto done; } if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name); //设置dev->kobj->name为dev->init_name dev->init_name = NULL; } if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); //注册总线设备时,该判断条件不成立 if (!dev_name(dev)) {
error = -EINVAL; goto = name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); //获取当前设备的父节点设备 kobj = get_device_parent(dev, parent); //获取父节点设备的kobject对象 if (IS_ERR(kobj)) {
error = PTR_ERR(kobj); goto parent_error; } if (kobj) dev->kobj.parent = kobj; //如果父节点设备的kobject对象存在,则设置当前设备的kobject对象的父节点 if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); //如果设备的父节点存在,且当前设备未设置NUMA节点,则将当前设备的NUMA节点设置父节点的NUMA节点。 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) {
glue_dir = get_glue_dir(dev); goto Error; } error = device_platform_notify(dev, KOBJ_ADD); if (error) goto platform_error; error = device_create_file(dev, &dev_attr_uevent); //根据属性dev_attr_uevent创建相关文件 if (error) goto attrError; error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; error = bus_add_device(dev); //向总线中添加设备,如果当前设备为总线,则该函数不执行,直接返回0 if (error) goto BusError; ... bus_probe_device(dev); //如果当前设备是实际存在的设备时,会检测该设备所挂载的总线上是否使能了设备驱动自动注册,如果已经使能,则调用device_initial_probe函数来间接的调用__device_attach函数来为该设备关联已存在的设备驱动 ...}

当总线按照上述过程,以设备的形式注册完成之后,接下来则注册实际的总线对象。

int bus_register(struct bus_type *bus){
//这里以platform总线为例,因此传参对象为platform_bus_type,定义如下: //struct bus_type platform_bus_type = {
// .name = "platform", // .dev_groups = platform_dev_groups, // .match = platform_match, // .uevent = plaform_uevent, // .dma_configure = platform_dma_configure, // .pm = &platform_dev_pm_ops, //}; int retval; struct subsys_private *priv; struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); ... retval = bus_create_file(bus, &bus_attr_uevent); ... priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); ... priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); ... klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); ... retval = add_probe_files(bus); ... retval = bus_add_groups(bus, bus->bus_groups);}

通过对上边代码的分析,可知,总线注册的过程中并没有实质性的与设备相关的操作,而主要是以内核所提出的设备模型概念为主来创建相关的总线文件。

关于PCI总线,其实现形式与platform总线不同:

第一,platform总线的初始化是在内核初始化的前期,即start_kernel->arch_call_rest_init->rest_init->kernel_init->kernel_init_freeable->driver_init->platform_bus_init。而PCI总线的初始化则是通过驱动形式来完成初始化。
第二,在初始化platform总线时,需要特别将总线抽象成设备,并注册该设备,随后注册总线。而PCI总线则不需要,只需要和前者一样,注册总线即可。

PCI总线注册原型如下:

static int __init pci_driver_init(void){
int ret; ret = bus_register(&pci_bus_type); //关于pci_bus_type,其原型定义如下: //struct bus_type pci_bus_type {
// .name = "pci", // .match = pci_bus_match, // .uevent = pci_uevent, // .probe = pci_device_probe, // ... //}; if (ret) return ret;#ifdef CONFIG_PCIEPORTBUS ret = bus_register(&pcie_port_bus_type); if (ret) return ret;#endif dma_debug_add_bus(&pci_bus_type); return 0;}

从上边的代码可以看到,在PCI总线的注册过程中确实没有注册设备的相关操作。而总线注册的过程,两者都调用同一个注册函数,因此两者的执行过程大致相同。

关于两者之间的不同,可能与以下两点因素相关:

  1. platform是虚拟总线,pci并不是虚拟总线;
  2. platform没有与之对应的实际设备,pci存在与之对应的设备(即PCI控制器)。

综上,便是对总线的注册过程中的理解。

转载地址:http://dpxii.baihongyu.com/

你可能感兴趣的文章
最小费用流 Bellman-Ford与Dijkstra 模板
查看>>
实现高性能纠删码引擎 | 纠删码技术详解(下)
查看>>
scala(1)----windows环境下安装scala以及idea开发环境下配置scala
查看>>
zookeeper(3)---zookeeper API的简单使用(增删改查操作)
查看>>
zookeeper(4)---监听器Watcher
查看>>
zookeeper(2)---shell操作
查看>>
mapReduce(3)---入门示例WordCount
查看>>
hbase(3)---shell操作
查看>>
hbase(1)---概述
查看>>
hbase(5)---API示例
查看>>
SSM-CRUD(1)---环境搭建
查看>>
SSM-CRUD(2)---查询
查看>>
SSM-CRUD (3)---查询功能改造
查看>>
Nginx(2)---安装与启动
查看>>
springBoot(5)---整合servlet、Filter、Listener
查看>>
C++ 模板类型参数
查看>>
C++ 非类型模版参数
查看>>
设计模式 依赖倒转原则 & 里氏代换原则
查看>>
DirectX11 光照
查看>>
图形学 图形渲染管线
查看>>