码迷,mamicode.com
首页 > 其他好文 > 详细

RTC驱动模型分析

时间:2015-04-21 09:41:10      阅读:122      评论:0      收藏:0      [点我收藏+]

标签:

①RTC设备层:
设备资源的定义:arch/arm/plat-s3c24xx/devs.c
static struct resource s3c_rtc_resource[] = {
	[0] = {
		.start = S3C24XX_PA_RTC,
		.end   = S3C24XX_PA_RTC + 0xff,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_RTC,
		.end   = IRQ_RTC,
		.flags = IORESOURCE_IRQ,
	},
	[2] = {
		.start = IRQ_TICK,
		.end   = IRQ_TICK,
		.flags = IORESOURCE_IRQ
	}
};
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-tq2440.c
static struct platform_device *tq2440_devices[] __initdata = {
	......,
	&s3c_device_rtc, //##
	......,
};
tq2440_devices[]这个数组在哪里被使用?这意味着RTC设备在哪里被注册。它是个静态数组,就在该文件找就行了。
tq2440_machine_init(void)
platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));//注意:是platform_add_devices是通过for循环调用platform_devices_add()函数
//通过这个函数就把这个数组里边所有的设备资源都注册到了platform设备总线下
根据宏的展开:
MACHINE_START(S3C2440, "TQ2440")
......
.init_machine = tq2440_machine_init,  //这个函数是被放在“.arch.info.init”段,在加载内核时会被自动调用
......
MACHINE_END

--------------------------------------------------------------------------------------------------------------------------------------------------------------
②RTC驱动层
drivers/rtc/rtc-s3c.c
static struct platform_driver s3c2410_rtc_driver = {
	.probe		= s3c_rtc_probe,
	......
	.driver		= {
		.name	= "s3c2410-rtc",
		.owner	= THIS_MODULE,
	},
};
函数调用关系:
s3c_rtc_init(void)
return platform_driver_register(&s3c2410_rtc_driver);//这样就把RTC驱动注册到platform总线上去了
看看驱动层的探针函数的实现:
s3c_rtc_probe(struct platform_device *pdev)
{
	......
	s3c_rtc_tickno = platform_get_irq(pdev, 1);
	......
	s3c_rtc_alarmno = platform_get_irq(pdev, 0);
	......
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	......
	s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
	......
	s3c_rtc_enable(pdev, 1);
	......
	s3c_rtc_setfreq(&pdev->dev, 1);
	......
	rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);//【进去分析】
}
当我进去分析rtc_device_register()的实现时让我纳闷的是:
按照前面分析platform驱动模型的那种节奏,现在这种时候probe函数应该要完成注册字符设备、实现file_operation为应用层提供API之类的代码,却偏偏莫名其妙地来了个rtc_device_register()函数,给人的感觉貌似又是注册一个设备,若说是添加RTC设备在设备层已经有platform_add_devices函数把资源注册到platform总线上了,蛋疼!
这个函数完成的事情跟platform_add_devices函数究竟有什么区别呢?看来还得研究内核源码!里面肯定藏着不可告人的秘密!
就从rtc_device_register这个函数入手试试看:
/drivers/rtc/class.c
rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)
{
	struct rtc_device *rtc;
	......
	//①申请这么一个结构体往后就初始化他的成员
	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
	......
	rtc->id = id;
	rtc->ops = ops;
	rtc->owner = owner;
	rtc->max_user_freq = 64;
	rtc->dev.parent = dev;
	rtc->dev.class = rtc_class;
	rtc->dev.release = rtc_device_release;
	......
	rtc_dev_prepare(rtc);//这个函数在/drivers/rtc/rtc-dev.c:定义
	//②把这个结构的dev结构成员往上注册
	err = device_register(&rtc->dev); //这个函数最终就是调用device_add(&pdev->dev);
	......
	rtc_dev_add_device(rtc);
	rtc_sysfs_add_device(rtc);
	rtc_proc_add_device(rtc);
	......
}
且看rtc_device结构体:
/include/linux/rtc.h
struct rtc_device
{
	struct device dev;
	struct module *owner;
	int id;
	char name[RTC_DEVICE_NAME_SIZE];
	.......
	const struct rtc_class_ops *ops; //##
	struct mutex ops_lock;
	.......
	struct cdev char_dev;
	unsigned long flags;
	int max_user_freq;
}
在/drivers/rtc/rtc-s3c.c
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,
};
这个结构体的成员函数都是实实在在地要操作底层硬件的函数。
回去看rtc_device_register函数的第②点,仔细一想突然好像想明白了什么,赶快写下来:
根据经验,我们通常在file_operation结构体为APP提供的API函数接口实现中直接进行操作底层硬件的操作,比如前一篇博文中基于平台的led的write()是通过操作GPBDAT寄存器来进行开关led的,那时我们是这么干的:注册字符设备驱动时把file_operation结构体往这个函数一塞就挂到内核里边去了,现在还不是一样么,把这些操作硬件的函数放到rtc_class_ops结构中,不同的是它被rtc_device结构体所包含,同时rtc_device结构体也包含一个内核device结构体,然后通过device_register()把这个device往上注册,应用层想要操作硬件的某种行为完全是可以
这一系列依据来找到rtc_class_ops结构中的这些底层操作函数的。也就是说,以前我们习惯把操作硬件的函数放到file_operation结构体中然后往上注册,感觉一步到位,直接干脆洒脱!
现在rtc设备驱动中似乎是把这些底层操作函数交给了一个中介结构体rtc_class_ops,然后这个结构体又交给了另一个中介结构体rtc_device,这个中介结构体有一个能够代表他自己的结构体device,只要访问到这个device结构体最终不就找到了底层操作函数。豁然开朗!至于谁在哪里会访问到device结构体,直觉告诉我肯定还有一个层次结构!

----------------------------------------------------------------------------------------------------
③RTC核心层:他是platform核心层的子类核心层【这个结论其实是最后才得出来的,提前放在这里意在暗示结局技术分享
在/drivers/rtc/rtc-dev.c:
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,//**ioctl()函数里边命令的响应函数在/drivers/rtc/intreface.c中定义
	.open		= rtc_dev_open,
	.release	= rtc_dev_release,
	.fasync		= rtc_dev_fasync,
};
看看这个file_operation在哪里被使用:
rtc_dev_prepare(struct rtc_device *rtc)
{
	......
	rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
	......
	cdev_init(&rtc->char_dev, &rtc_dev_fops);//在这里被用来注册字符设备
	rtc->char_dev.owner = rtc->owner;
}
那rtc_dev_prepare()这个函数在什么时候在什么地方被调用?
还记得在分析驱动层时的rtc_device_register()函数不,就是它,它是为驱动层提供的注册接口函数:
/drivers/rtc/class.c
rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)
{
	struct rtc_device *rtc;
	......
	//①申请这么一个结构体往后就初始化他的成员
	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
	......
	rtc->id = id;
	rtc->ops = ops;
	......
	rtc->dev.class = rtc_class;
	......
	rtc_dev_prepare(rtc);//##
	//②把这个结构的dev结构成员往上注册
	err = device_register(&rtc->dev); //这个函数最终就是调用device_add(&pdev->dev);
	......
	rtc_dev_add_device(rtc);
	......
}
分析到这里RTC驱动的主线工作路线就呈现出来了,但有一个问题自然会想到,在RTC驱动层和设备层已经把RTC驱动
和设备资源都注册到platform平台了,驱动层的probe函数不是直接注册字符设备实现API接口不就OK了么?这样做有
什么作用?
分析:先明确一下,rtc驱动的probe函数多做了哪些事情:
再次来看看rtc_device_register()函数
rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)
{
	struct rtc_device *rtc;
	......
	//①申请这么一个结构体往后就初始化他的成员
	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
	......
	rtc->ops = ops;
	......
	rtc->dev.class = rtc_class;
	rtc_dev_prepare(rtc);//这个函数在最终完成字符设备的注册
	//②从根源的角度来说以下就是probe函数最终多做的事情
	err = device_register(&rtc->dev); //这个函数最终就是调用device_add(&pdev->dev);
	......
	rtc_dev_add_device(rtc);
	rtc_sysfs_add_device(rtc);
	rtc_proc_add_device(rtc);
	......
}
在/drivers/rtc/class.c
rtc_init(void)
{
	rtc_class = class_create(THIS_MODULE, "rtc"); /* 创建了一个类--rtc */
	......
	rtc_class->suspend = rtc_suspend;
	rtc_class->resume = rtc_resume;
	rtc_dev_init(); /* 为RTC设备动态分配设备号 */
	rtc_sysfs_init(rtc_class);
	return 0;
}
这个函数在linux设备模型sysfs下创建了一个rtc的类,那么肯定有一个地方会往这个类中添加设备,按照这种推理
那么上边说到的“多余”的工作应该就是它了。那就来分析一下device_register()函数究竟会做哪些事情?
device_register(&rtc->dev)
error = device_add_class_symlinks(dev);
就是这个函数了:
static int device_add_class_symlinks(struct device *dev)//从函数的名字猜测:在类下创建设备的符号链接文件
{
	int error;
	......
	error = sysfs_create_link(&dev->kobj,&dev->class->p->class_subsys.kobj,"subsystem");//创建链接文件
	......
	class_name = make_class_name(dev->class->name,&dev->kobj);//设置类下的设备名
}
如此,我们上面关心的问题就都解决了,多做这些事情无非是想把rtc归为一个类,所有这种设备都会统一添加到这rtc类中
最后来个高度总结技术分享
1.RTC驱动框架图:
技术分享

这幅图是网上找的,前辈们把其中的一些工作归结为RTC核心层,现在觉得这样也挺好理解,只是图中RTC核心层中的工作
不单单是图中列出来的那几个,因为还有platform驱动模型的核心层,个人觉得还是要把他也囊括进去才能体现出RTC驱动
的本质所在!
2.万变不离其宗,rtc驱动框架的根本还是依赖于platform驱动模型。
3.有时间尽量多看看Linux的源码,只有源码才是解开心头疑问的最有力的根据!

RTC驱动模型分析

标签:

原文地址:http://blog.csdn.net/clb1609158506/article/details/45165539

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!