您的位置:首页 > 运维架构 > Linux

[笔记分享] [OS] Linux设备模型之设备注册

2017-08-24 17:14 363 查看
Platform: msm8x60

Kernel: 2.6

overview

大部分device和driver都被包含在一个特定bus中,platform_device和platform_driver就是如此,包含在 platform_bus_type中。这里就以对platform_bus_type的调用为主线,浅析platform_device的注册过程,从而理解linux设备模型。platform_bus_type用于关联platform device和 platform driver。这里就以msm_fb_device为例,文件为board-msm8x60.c,结构如下:





在系统初始化的时候,surf_devices会被platform_add_devices(surf_devices, ARRAY_SIZE(surf_devices)) 调用,而platform_add_devices是被包含在static void __init msm8x60_init(void), 有人就有疑问了,这个函数是在哪里调用的。好吧,我们从系统初始化一开始讲。

设备初始化

系统一开始启动进入start_kernel,然后我们一步步往下追:





start_kernel-> rest_init-> kernel_init-> do_basic_setup-> do_initcall,到这里,我们先打住,来看下module_init。

在编写驱动的时候,我们经常可以看到如下代码:



可以发现,msm_batt_init()的指针被放在initcall6.init这一个section中。那么它又是如何被系统调用到的呢?接下来看:

上文的do_basic_setup()中有调用到:



以上两部分代码,即指定了__early_initcall_end到__initcall_end所对应的function指针。找到这些指针后,do_initcalls只需要一一调用这些function即可。当然,这些init当中就包括了我们所关注的module_init。

那么我们看看msm8x60_init()是在哪里被调到用到的。首先看下面这个的结构:



展开后就是

_mach_desc_MSM8X60_SURF = {
.init_machine = msm8x60_init,
} ;


我们再看.init_machine在哪里被用到,前面我们不是看到start_kernel()有个setup_arch()函数吗,现在在看其中的一行。如下:



原来也是module_init(),到此应该不用我多解释了。

设备注册

好了,至此我们的platform_add_devices(surf_devices, ARRAY_SIZE(surf_devices))总算被调用到了,继续从这函数追代码。(有省略号部分表示不重要部分省略了)

platform_add_devices()代码如下:



代码中:

device_initialize()对设备的一些参数初始化,这是必须的。

platform_device_add()用来对设备进行注册。

device_initialize()的代码如下:



代码中,

1. devices_kset是所有dev的kset,也就是所有dev都被链接在该kset下,其在初始化devices_init()中通过调用kset_create_and_add(“devices”, &device_uevent_ops, NULL)来创建。由于参数parent=NULL ,所以生成/sys/devices 目录。kset结构体中包含有一个kobj,一个kobj生成一个目录,在这里就是”devices “目录,通过调用kobject_add_internal()生成。所以从dev->kobj.kset = devices_kset 可以看出,该dev.kobj添加到了devices_kset容器中,所以kobj都归属于一个特定的kset。关于kset,kobj,ktype,kref的关系可以参考书LDD3的第十四章,在第370页有一张说明kobj和kset关系的图(英文版)。

2. kobject_init(&dev->kobj, &device_ktype)用于初始化 dev->kobj中变量的参数,如ktype、kref、entry和state*等。初试化devices_init()还会调用kobject_create_and_add()生成/sys/dev、/sys/dev/block和/sys/dev/char目录。

platform_device_add()代码如下:



代码中,

1. 如果dev没有设置parent,显然本例中没有设置,则执行pdev->dev.parent =&platform_bus,这个 platform_bus的定义如下:



platform_bus_init ()在内核初始化的时候会被调用。 platform_bus 通过device_register添加到device hierarchy, platform_bus .kobj->parent = devices_kset.kobj,故生成/sys/devices/platform目录。由于dev.parent = &platform_bus,所以注册的dev.kobj生成的目录在/sys/devices/platform/下。在本例中将生成/sys/devices/platform/msm-fb目录,后面会讲到

2. pdev->dev.bus = &platform_bus_type表明该dev是platform_bus_type类型的,并且该dev也会被添加到platform_bus_type的devices_kset容器中和klist_devices列表中。

3. 如果pdev->id 不等于-1,那么说明其指定了序号,会被添加到pdev-name名字后面再拷贝给dev->bus_id,等于-1则直接拷贝。

4. 把所有的 resources 添加到列表中,iomem_resource和ioport_resource 为父节点。

5. 最后调用device_add(),把该dev添加到device hierarchy,下面就研究它。

device_add()定义在drivers/base/core.c中,其调用很多接口完成dev的添加工作,这个

接口调用完后则完成添加工作。

device_add()代码:



`


代码中:

1. kobject_add ()接口会调用kobject_add_internal()接口来完成kobj的添加工作,下面简要说明下kobject_add_internal()接口,其中有

parent = kobject_get(kobj->parent);
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}


例子里if (kobj->kset)里的代码会被执行,因为 kobj->kset =devices_kset。但由于dev->kobj-> parent = dev->parent.kobj(即platform_bus.kobj),所以if (!parent)面的代码不执行,故dev->kobj的目录(”msb-fb”)产生在 dev->parent.kobj目录(”platform”)里,即/sys/devices/platform/msb-fb。

2. 我们可以使用cat /sys/devices/platform/msb-fb/uevent来查看其内容,具体的过程是这样的,cat()–>……–>( dev.kobj->ktype->sysfs_ops->show() )–>( uevent_attr->show() ),其中 ktype就是在device_initialize()中通过kobject_init(&dev->kobj, &device_ktype)接口调用初始化的device_ktype ,在sysfs_ops->show() 中是通过to_dev_attr(attr)(也就是container_of())得到注册的 uevent_attr。对所有的属性文件的读取和写入都是这个过程,都是使用其所在目录的kobj->ktype来完成的。

setup_parent()定义在drivers/base/core.c中,用于找到并设置dev->kobj.parent,代码如下:



driver_match_device是用来匹配device和driver,如果匹配不成功则返回。一开始device去匹配driver肯定会失败的,因为driver还没加载。

driver_probe_device()也是定义在drivers/base/dd.c中,代码如下:



其中

if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}


这部分会调用我们platform_driver中设置好的probe函数。

至此,platform_add_devices()函数完成了设备的注册。总得来如下流程:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: