以RTC为例分析linux platform_bus platform_device和platform_driver注册过程
来源:互联网 发布:淘一兔淘宝信誉查号 编辑:程序博客网 时间:2024/05/01 21:59
说明:内核版本为linux-2.6.37.1;只分析注册过程,未分析注销过程;水平、篇幅
均有限,部分地方未作深入全面分析;分析过程只保留了与注册相关的代码;分析
顺序依照各部分在内核中的注册顺序platform_bus,paltform_device,platform_driver
进行。
1.platform_bus注册过程
platform_bus相关数据结构:
/driver/base/platform.c
struct device platform_bus = {.init_name= "platform",};一种platform虚拟设备,是为了符合内核的设备树模型,将platform作为设备根
(就是其他所有设备的parent)。方便电源管理和与用户空间通信?
struct bus_type platform_bus_type = {.name= "platform",.dev_attrs= platform_dev_attrs,.match= platform_match,.uevent= platform_uevent,.pm= &platform_dev_pm_ops,};platform总线通过bus_type来描述,注册成功后会在/sys/bus目录下找到名为
platform的总线。
下面介绍具体的注册过程。
内核启动后,通过函数start_kernel->rest_init,在rest_init函数中创建内核进程kernel_init
最后由kernel_init->do_basic_setup->driver_init
void __init driver_init(void){/* These are the core pieces */devtmpfs_init();devices_init();buses_init();classes_init();firmware_init();hypervisor_init();/* These are also core pieces, but must come after the * core core pieces. */platform_bus_init();system_bus_init();cpu_dev_init();memory_dev_init();}此函数主要是用于初始话设备模型用,我的理解就是会在sys/目录下创建bus目录
(通过buses_init函数实现),或者创建device目录以及dev目录,包括dev目录下的
block和char目录。然后通过条用platform_bus_init在/sys/device目录下创建platform
设备,在/sys/bus目录下创建platform总线。(sysfs和设备模型的关系参考其他资料)
看下platform_bus_init函数:
int __init platform_bus_init(void){ int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error;}
/sys/device和/sys/bus目录下的platform设备和platform总线就是通过device_register()
和bus_register函数实现的。
2.platform_device注册过程首先rtc设备定义在arch/arm/plat-s3c24xx/devs中,通过platform_device结构体来描述rtc设备
struct platform_device s3c_device_rtc = { .name = "s3c2410-rtc", .id = -1, .num_resources = ARRAY_SIZE(s3c_rtc_resource), .resource = s3c_rtc_resource,};
然后向arch/arm/mach-s3c2440/mach-smdk2440.c文件的设备数组中添加此设备
static struct platform_device *smdk2440_devices[] __initdata = { ... ... &s3c_device_rtc, ... ...};
添加完毕后,系统在启动的过程中会调用到一个函数smdk2440_machine_init,最终由此函数
将上述的设备数组添加到系统的总线上。
static void __init smdk2440_machine_init(void){ ... ... platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); ... ...}在此函数中调用platform_add_devices函数后,设备数组中的设备将被依次注册入内核。
下面具体分析注册platform_device的函数调用过程,仅作简单封装的函数省略分析。
platform_add_devices->platform_device_register->platform_device_add
int platform_device_add(struct platform_device *pdev){... ...pdev->dev.bus = &platform_bus_type; ... ...ret = device_add(&pdev->dev);... ...}
此函数首先设置dev的总线为platform_bus_type,然后通过device_add将dev添加到platform bus上。
注意这里操作的是pdev->dev,就是platform_device中的struct device 结构。
int device_add(struct device *dev){... ...error = bus_add_device(dev);... ...bus_probe_device(dev);}device_add函数通过调用bus_add_device函数将设备添加到内核platform_bus总线上。由于此时总线上未
有对应的驱动,所以此处调用bus_probe_device函数之后并不能找到驱动。
3.platform_driver
driver相关数据类型
static struct platform_driver s3c_rtc_driver = { .probe = s3c_rtc_probe, .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .id_table = s3c_rtc_driver_ids, .driver = { .name = "s3c-rtc", .owner = THIS_MODULE, },};
系统启动的时候首先有subsys_initcall(rtc_init),来调用rtc_init:
static int __init rtc_init(void){rtc_class = class_create(THIS_MODULE, "rtc");if (IS_ERR(rtc_class)) {printk(KERN_ERR "%s: couldn't create class\n", __FILE__);return PTR_ERR(rtc_class);}rtc_class->suspend = rtc_suspend;rtc_class->resume = rtc_resume;rtc_dev_init();rtc_sysfs_init(rtc_class);return 0;}此函数功能主要是1.注册一个rtc设备类,成功后将在/sys/class目下下生成rtc这个目录。
以后会将生成的rtc设备,放在此目录下。(方便管理和与用户空间通信?)2.通过函数
rtc_dev_init->alloc_chrdev_region动态申请设备号。
然后由module_init(s3c_rtc_init)来实现驱动的注册s3c_rtc_init->platform_driver_register
int platform_driver_register(struct platform_driver *drv){drv->driver.bus = &platform_bus_type;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);}函数先将platform_dirver->driver结构初始化。然后通过driver_register函数注册进内核的platform_bus总线
driver_register->bus_add_driver->driver_attach->driver_probe_device->really_probe
static int really_probe(struct device *dev, struct device_driver *drv){... ...dev->driver = drv;... ...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的device_driver赋值给platform_device的device。
也就是操作的结构是struct device和struct devicce_driver。
然后判断调用的是bus的probe方法还是driver的probe方法,此处platform_bus并未初始化
probe方法,所以调用的是drv的probe方法。而drv的probe方法在platform_driver_register
函数总被初始化为:drv->probe = platform_drv_probe。所以此处就是调用此函数。
static int platform_drv_probe(struct device *_dev){struct platform_driver *drv = to_platform_driver(_dev->driver);struct platform_device *dev = to_platform_device(_dev);return drv->probe(dev);}
此函数中首先通过to_platform_driver和to_platform_device来找回struct device对应的
platform_device及platform_driver。然后通过drv->probe调用platform_driver的probe
方法s3c_rtc_probe,并且将找到platform_device通过参数传递给次函数。
4.probe过程
probe中需要用到的数据结构
static const struct rtc_class_ops s3c_rtcops = {.open= s3c_rtc_open,.release= s3c_rtc_release,.read_time= s3c_rtc_gettime,.set_time= s3c_rtc_settime,.read_alarm= s3c_rtc_getalarm,.set_alarm= s3c_rtc_setalarm,.irq_set_freq= s3c_rtc_setfreq,.irq_set_state= s3c_rtc_setpie,.proc= s3c_rtc_proc,.alarm_irq_enable = s3c_rtc_setaie,};rtc设备类的操作方法集
static const struct file_operations rtc_dev_fops = {.owner= THIS_MODULE,.llseek= no_llseek,.read= rtc_dev_read,.poll= rtc_dev_poll,.unlocked_ioctl= rtc_dev_ioctl,.open= rtc_dev_open,.release= rtc_dev_release,.fasync= rtc_dev_fasync,};rtc0字符设备操作方法集,此处的操作会间接调用class中的open操作。
static int __devinit s3c_rtc_probe(struct platform_device *pdev){ ... ... rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE); ... ...}
此函数通过rtc_device_register函数注册一个rtc字符设备,rtc0。
struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner){ ... ...rtc->ops = ops;rtc->dev.parent = dev;rtc->dev.class = rtc_class;rtc->dev.release = rtc_device_release; ... ... dev_set_name(&rtc->dev, "rtc%d", id); rtc_dev_prepare(rtc);err = device_register(&rtc->dev);if (err) {put_device(&rtc->dev);goto exit_kfree;}rtc_dev_add_device(rtc);rtc_sysfs_add_device(rtc);rtc_proc_add_device(rtc); ... ...}次函数首先初始化rtc_device设备,内涵struct_device结构,内核通过此结构来描述
一个硬件rtc设备。然后设置设备名字,此处应该就是rtc0并调用rtc_dev_prepare函数
注册一个rtc字符设备。注册成功在/dev目录下将出现rtc0设备。
void rtc_dev_prepare(struct rtc_device *rtc){... ...cdev_init(&rtc->char_dev, &rtc_dev_fops);... ...}此处将字符设备操作方法rtc_dev_ops和字符设备绑定。
然后继续执行函数device_register,通过device_register->device_add调用添加设备函数。
int device_add(struct device *dev){... ...error = bus_add_device(dev);... ...bus_probe_device(dev);}
此处和之前的platform_device中调用到此函数后的过程类似。但是值得一提的是,在
bus_add_device函数中并不会执行If(bus){}中的函数体,因为此时的dev设备为rtc->dev,
其并未初始化bus设备。所以bus_add_device和bus_probe_device并不会执行有实际意
义的操作。而是做一些设备模型相关的工作,在相应的/sys目录下生成相关目录等。
5.open过程
open(/dev/rtc0,)(系统调用)->s3c_dev_open(file_operation的open方法)->s3c_rtc_open(rtc_class_ops的open方法)。
- 以RTC为例分析linux platform_bus platform_device和platform_driver注册过程
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- platform_device和platform_driver的注册过程,及probe函数何时调用的分析
- Linux设备模型之platform_device和platform_driver
- Linux驱动开发之Platform_device和Platform_driver
- Linux驱动开发之Platform_device和Platform_driver
- Linux驱动开发之Platform_device和Platform_driver
- platform_bus,platform_driver,platform_bus
- platform总线注册过程及platform_driver与platform_device的匹配
- platform总线注册过程及platform_driver与platform_device的匹配
- platform总线注册过程及platform_driver与platform_device的匹配
- platform总线注册过程及platform_driver与platform_device的匹配
- platform_device的注册过程分析
- platform_device和platform_driver
- platform_device和platform_driver
- C语言学习笔记(3)指针初探
- Linux 2.6内核中新的锁机制--RCU
- Arm linux 内核移植及系统初始化过程分析
- 程序员的专业利器--英文
- Android/iOS Notification feature
- 以RTC为例分析linux platform_bus platform_device和platform_driver注册过程
- SQL基本用方法
- HTML table tr colspan rowspan
- 内核启动时,设备及驱动初始化的实现
- 存储过程
- window7下vs2010崩溃的解决办法
- android 4.0.3最新源码下载编译
- C语言中交换两个指针变量所指向的值
- WCF 客户端证书安装