以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方法)。