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