以RTC为例分析linux platform_bus platform_device和platform_driver注册过程

说明:内核版本为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方法)。






  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值