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