[笔记分享] [OS] Linux设备模型之设备注册
2017-08-24 17:14
363 查看
Platform: msm8x60
Kernel: 2.6
![](https://img-blog.csdn.net/20170824165540130?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20170824165558809?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
在系统初始化的时候,surf_devices会被platform_add_devices(surf_devices, ARRAY_SIZE(surf_devices)) 调用,而platform_add_devices是被包含在static void __init msm8x60_init(void), 有人就有疑问了,这个函数是在哪里调用的。好吧,我们从系统初始化一开始讲。
![](https://img-blog.csdn.net/20170824165619972?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20170824165631254?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
start_kernel-> rest_init-> kernel_init-> do_basic_setup-> do_initcall,到这里,我们先打住,来看下module_init。
在编写驱动的时候,我们经常可以看到如下代码:
![](https://img-blog.csdn.net/20170824165908902?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
可以发现,msm_batt_init()的指针被放在initcall6.init这一个section中。那么它又是如何被系统调用到的呢?接下来看:
上文的do_basic_setup()中有调用到:
![](https://img-blog.csdn.net/20170824170303251?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
以上两部分代码,即指定了__early_initcall_end到__initcall_end所对应的function指针。找到这些指针后,do_initcalls只需要一一调用这些function即可。当然,这些init当中就包括了我们所关注的module_init。
那么我们看看msm8x60_init()是在哪里被调到用到的。首先看下面这个的结构:
![](https://img-blog.csdn.net/20170824170330830?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
展开后就是
我们再看.init_machine在哪里被用到,前面我们不是看到start_kernel()有个setup_arch()函数吗,现在在看其中的一行。如下:
![](https://img-blog.csdn.net/20170824170350696?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
原来也是module_init(),到此应该不用我多解释了。
platform_add_devices()代码如下:
![](https://img-blog.csdn.net/20170824170415930?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
代码中:
device_initialize()对设备的一些参数初始化,这是必须的。
platform_device_add()用来对设备进行注册。
device_initialize()的代码如下:
![](https://img-blog.csdn.net/20170824170535643?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
代码中,
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()代码如下:
![](https://img-blog.csdn.net/20170824170550812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
代码中,
1. 如果dev没有设置parent,显然本例中没有设置,则执行pdev->dev.parent =&platform_bus,这个 platform_bus的定义如下:
![](https://img-blog.csdn.net/20170824170602776?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
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()代码:
![](https://img-blog.csdn.net/20170824170634980?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
`
![](https://img-blog.csdn.net/20170824170650802?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
代码中:
1. kobject_add ()接口会调用kobject_add_internal()接口来完成kobj的添加工作,下面简要说明下kobject_add_internal()接口,其中有
例子里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,代码如下:
![](https://img-blog.csdn.net/20170824170703063?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
driver_match_device是用来匹配device和driver,如果匹配不成功则返回。一开始device去匹配driver肯定会失败的,因为driver还没加载。
driver_probe_device()也是定义在drivers/base/dd.c中,代码如下:
![](https://img-blog.csdn.net/20170824170737133?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
其中
这部分会调用我们platform_driver中设置好的probe函数。
至此,platform_add_devices()函数完成了设备的注册。总得来如下流程:
![](https://img-blog.csdn.net/20170824171020673?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva3Jpc19mZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
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()函数完成了设备的注册。总得来如下流程:
相关文章推荐
- [笔记分享] [OS] Linux设备模型之驱动注册
- [笔记分享] [OS] Linux设备模型之基础概念
- Linux2.6.32驱动笔记(2)字符设备驱动编程模型
- Linux字符设备驱动模型--字符设备的注册
- [笔记分享] [OS] Linux的内存管理
- 嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)
- [笔记分享] [OS] Linux的进程调度
- Linux设备驱动模型学习笔记(kobject,kset,subsystem子系统)
- 学习笔记——《LINUX设备驱动程序(第三版)》Linux设备模型:内核添加、删除设备、驱动程序
- Linux设备模型学习笔记——结构体描述
- Linux设备模型之注册kobject和 kset
- linux学习笔记2--diver初始化及设备模型
- [笔记分享] [OS] Linux的进程管理
- [笔记分享] [OS] Linux的中断处理
- [笔记分享] [OS] Linux的内核同步
- Linux驱动开发-混杂字符设备驱动模型笔记 4
- linux2.6标准字符设备驱动模型(手动注册)
- linux设备模型学习笔记——理论篇
- [笔记分享] [OS] Linux的时间管理
- Linux设备模型之tty驱动架构分析