Linux驱动开发15之RTC驱动模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangdapao12138/article/details/82120773

1.需要的文件有哪些

driver/rtc/class.c:         此文件向linux内核驱动模型注册了一个类RTC,同时为底层的RTC驱动提供了注册/注销RTC接口。同时实现了RTC相关的PM操作。

driver/rtc/rtc-dev.c:      将各种各样的RTC设备抽象成一个字符设备,同时提供文件操作函数集。

driver/rtc/rtc-sysfs.c:    用户可以通过sysfs文件系统方便快捷的操作rtc设备。

driver/rtc/rtc-proc.c:     可以通过proc文件系统获得rtc的相关信息,比如rtc_time, rtc_data等信息。

driver/rtc/interface.c:    提供应用程序和驱动的接口函数,主要是为rtc提供相关的调用接口。

driver/rtc/rtc-lib.c:         提供了一个rtcdata以及time之间的转换函数

driver/rtc/hctosys.c:     用于开机启动的时候获取rtc的值。

driver/rtc/rtc-xxx.c:       各式各样的rtc驱动。

2.RTC的设备在哪里注册的?

arch/arm/mach-s5pv210/mach-x210.c中。在initdata段加上的rtc

我们可以看出,名字被改了!但被改掉了!initdata是个数组,在哪里使用呢?

 

//注意:是platform_add_devices是通过for循环调用platform_devices_add()函数
//通过这个函数就把这个数组里边所有的设备资源都注册到了platform设备总线下

根据宏的展开:
MACHINE_START(SMDKC110, "SMDKC110")
......
.init_machine = smdkc110_machine_init,,  //
这个函数是被放在“.arch.info.init”段,在加载内核时会被自动调用
......
MACHINE_END

好了,上述就是设备的注册!

3.RTC的驱动注册在哪里?

Drivers/rtc/rtc-smdkc110.c

00482: static int __init smdkc110_rtc_init(void)
00483: {
00484: printk(banner);
00485: return platform_driver_register(&smdkc110_rtc_driver);
00486: }

下面看下smdkc110_rtc_driver这个结构体。

00468: static struct platform_driver smdkc110_rtc_driver = {
00469: .probe = smdkc110_rtc_probe,
00470: .remove = __devexit_p(smdkc110_rtc_remove),
00471: .suspend = smdkc110_rtc_suspend,
00472: .resume = smdkc110_rtc_resume,
00473: .driver = {
00474:           .name = "smdkc110-rtc",
00475:           .owner = THIS_MODULE,
00476:           },
00477: };

那么系统启动时候,smdkc110_rtc_driver.driver.name = s5p_device_rtc.name此时驱动和设备匹配了!,然后执行probe函数!

00275: static const struct rtc_class_ops smdkc110_rtcops = {
00276: .read_time = smdkc110_rtc_gettime,
00277: .set_time = smdkc110_rtc_settime,
00278: .read_alarm = smdkc110_rtc_getalarm,
00279: .set_alarm = smdkc110_rtc_setalarm,
00280: };

00471: static const struct file_operations rtc_dev_fops = {
00472: .owner = THIS_MODULE,
00473: .llseek = no_llseek,
00474: .read = rtc_dev_read,       //读方法
00475: .poll = rtc_dev_poll, //轮询
00476: .unlocked_ioctl = rtc_dev_ioctl,      //IO控制
00477: .open = rtc_dev_open,     //打开
00478: .release = rtc_dev_release,       //释放
00479: .fasync = rtc_dev_fasync, //异步通知
00480: };

00159: struct rtc_device{
00161: struct device dev;
00162: struct module *owner;
00164: int id;
00165: char name[RTC_DEVICE_NAME_SIZE];
00167: const struct rtc_class_ops *ops;
00168: struct mutex ops_lock;
00170: struct cdev char_dev;
00171: unsigned long flags;
00173: unsigned long irq_data;
00174: spinlock_t irq_lock;
00175: wait_queue_head_t irq_queue;
00176: struct fasync_struct *async_queue;
00178: struct rtc_task *irq_task;
00179: spinlock_t irq_task_lock;
00180: int irq_freq;
00181: int max_user_freq;
00182: } ? end rtc_device ? ;

 

 

到此完成了设备驱动的注册,在目录下建立:/dev/rtc0/sys/class/rtc/rtc0/proc/driver/rtc/sys/bus/platform/drivers/smdkc110-rtc/smdkc110-rtc/rtc/rtc0,建立这四个东西。

4.这些结构体的作用?到底是什么关系?

static const struct rtc_class_ops smdkc110_rtcops

static const struct file_operations rtc_dev_fops

struct rtc_device *rtc

首先我们要知道的,对于目前platform平台总线的注册,必须要注册三个东西:

  1. RtcPlatform平台总线的注册,包括设备和驱动;
  2. device设备注册出现设备号,应用层通过file_operations结构体中的函数操控硬件,这是操控硬件第一种方法;
  3. class属性文件注册,通过/sys/class中的接口直接对硬件进行操控,这是操控硬件第二种方法;

这样说来,有很多个结构体,分别对应file_operations结构体、class结构体、硬件相关的device结构体等等。比较乱,而且不好操作,不好找。那么这就发明了针对不同的硬件包含了所有结构体信息的一个东西,叫做rtc_device结构体,我们来看下:

00159: struct rtc_device{
00161: struct device dev;
00162: struct module *owner;
00164: int id;
00165: char name[RTC_DEVICE_NAME_SIZE];
00167: const struct rtc_class_ops *ops;
00168: struct mutex ops_lock;
00170: struct cdev char_dev;
00171: unsigned long flags;
00173: unsigned long irq_data;
00174: spinlock_t irq_lock;
00175: wait_queue_head_t irq_queue;
00176: struct fasync_struct *async_queue;
00178: struct rtc_task *irq_task;
00179: spinlock_t irq_task_lock;
00180: int irq_freq;
00181: int max_user_freq;
00182: } ? end rtc_device ? ;

我们看到这个结构体中,既包含了struct device devchar name[RTC_DEVICE_NAME_SIZE];const struct rtc_class_ops *ops;struct cdev char_dev(file_operations);等等都包含了,也就是说通过这个结构体,我们可以完成所有注册,也可以完成所有的硬件操作。

5.cdev_init/cdev_adddevice_add是什么关系?

首先我们来看下这个,感觉有点疑惑!为什么中间有一个device_add

 

Linux 内核中使用 struct cdev 结构来描述字符设备,我们在驱动程序中必须将已分配到的设备号以及设备操作接口(即为 struct file_operations 结构)赋予 struct cdev 结构变量。1)首先使用 cdev_alloc()函数向系统申请分配 struct cdev 结构, 2)再用cdev_init()函数初始化已分配到的结构并与 file_operations 结构关联起来。3)最后调用 cdev_add()函数将设备号与 struct  cdev 结构进行关联并向内核正式报告新的字符设备的注册,这样新设备可以被用起来了。

也就是说cdev_initcdev_add完成了字符设备的注册,我们加上其中包括前面的主次设备号的分配。但是这个时候并没有创建设备节点!!!!也就是说/dev下还没有此设备节点。我们创建设备节点的函数为:device_create。那么我们来看下这个设备节点怎么创建的!

device_create
    |
   + -- kzalloc struct device
    |
   +---device_register
                |
               +-----device_initialize
                |
               +-----device_add

device_createdevice_add多做的事情非常清楚了:
1. 新建struct device device_add是不会新建的,只会加。
2. 进行了初始化, 如果不调device_register 就得自己去调用device_initiali初始化。

关于正真设备文件的创建(不是指sys下的文件), 最终是由device_add函数里头的kobject_uevent(&dev->kobj, KOBJ_ADD)完成的调用的。

那么我们清楚了,

cdev_init、cdev_add完成设备的注册,device_add完成设备节点创建!

6RTC结构的框图

http://hi.csdn.net/attachment/201109/27/0_13170887847bk3.gif

7.RTC驱动的读写

App:                  open(“/dev/rtc0”)

----------------------------------------------------------------------

Kernel:             sys_open

                            rtc_dev_fops.open

rtc_dev_open

                                              //根据次设备号找到以前用“rtc_device_register”注册的rtc_device

                                              struct rtc_device *rtc = container_of(inode->i_cdev, struct rtc_device, char_dev);

                                              const struct rtc_class_ops *ops = rtc->ops;

                                              err = ops->open ? ops->open(rtc->dev.parent) : 0;

                                              class结构体中没有open函数,因此不执行!

App:               ioctl(fd, RTC_ED_TIME,….)

--------------------------------------------------------------------------------

Kernel: sys_ioctl

                            rtc_dev_fops.ioctl

rtc_dev_ioctl

struct rtc_device *rtc = file->private_data;

                                              err = rtc_read_time(rtc, &tm);

                                                        err = rtc->ops->read_time(rtc->dev.parent, tm);

                                                                 smdkc110_rtc_gettime

猜你喜欢

转载自blog.csdn.net/wangdapao12138/article/details/82120773