RTC驱动系统分析

来源:互联网 发布:c语言中的error 编辑:程序博客网 时间:2024/06/03 21:33

一. RTC及驱动简介

        RTC即real time clock实时时钟,主要用于为操作系统提供可靠的时间;当系统处于断电 的情况下,RTC记录操作系统时间,并可在电池供电情况下继续正常工作,当系统正常启动后,系统可从RTC读取时间信息,来确保断电后时间运行连续性。

        目前,很多CPU中都已集成RTC系统,且有许多独立的外接RTC芯片可用于实现RTC功能;

        在内核中RTC驱动可分为两层,一层为于硬件无关的抽象层,主要用于管理RTC设备、设备节点,属性节点注册及操作;另一层为与硬件相关的RTC低层驱动层;抽象层程序在/drivers/rtc目录下,主要涉及下面几个文件:

class.c   用于管理和注册RTC设备结构、sysfs、procfs及RTC类;

rtc-dev.c 用于注册和管理RTC设备节点,为用户空间提供devfs操作接口,主要操作有rtc_read,rtc_ioctl; 

rtc-proc.c  用于管理rtc的procfs属性节点,提供一些中断状态、标志查询;

rtc-sysfs.c 用于管理rtc设备的sysfs属性,如获取RTC设备名字、日期、时间等属性信息;

interface.c 为rtc-dev.c 和RTC低层驱动提供操作接口;

        RTC系统结构示意图如图1所示;

图1 RTC系统结构图

       如图1所示,RTC 设备驱动通过rtc_device_register接口向linux系统注册RTC设备,并成生proc、sys目录下的属性文件;当用户通过/dev/rtcx设备节点或proc、sysfs接口来操作RTC设备时,都需要先通过interface接口才能访问真实的设备驱动。

二. 部分数据结构分析

1. RTC设备结构

struct rtc_device
{

struct device dev;

struct module *owner;

int id;   设备编号

char name[RTC_DEVICE_NAME_SIZE];

const struct rtc_class_ops *ops;  rtc设备低层操作接口;

struct mutex ops_lock;                     

struct cdev char_dev;    RTC字符型设备结构;

unsigned long flags;

unsigned long irq_data;    中断数据;

spinlock_t irq_lock;                  

wait_queue_head_t irq_queue;   中断数据等待队列;

struct fasync_struct *async_queue;

struct rtc_task *irq_task;

spinlock_t irq_task_lock;

int irq_freq;         中断频率;

int max_user_freq;

struct timerqueue_head timerqueue;

struct rtc_timer aie_timer;                       报警中断定时器;

struct rtc_timer uie_rtctimer;                  更新中断定时器;

struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */     周期中断高精度定时器;

int pie_enabled;             周期中断使能标志;

struct work_struct irqwork;       


#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL    内部仿真update interrupt时所需;

struct work_struct uie_task;

struct timer_list uie_timer;

/* Those fields are protected by rtc->irq_lock */

unsigned int oldsecs;

unsigned int uie_irq_active:1;

unsigned int stop_uie_polling:1;

unsigned int uie_task_active:1;

unsigned int uie_timer_active:1;

#endif
};

2. RTC设备低层操作接口

struct rtc_class_ops {

int (*open)(struct device *);                           打开设备

void (*release)(struct device *);                    释放设备

int (*ioctl)(struct device *, unsigned int, unsigned long);    

int (*read_time)(struct device *, struct rtc_time *);   读取RTC时间;

int (*set_time)(struct device *, struct rtc_time *);    设置RTC时间;

int (*read_alarm)(struct device *, struct rtc_wkalrm *);  读取RTC报警时间;

int (*set_alarm)(struct device *, struct rtc_wkalrm *);   设置RTC报警时间;

int (*proc)(struct device *, struct seq_file *);                    用于提供procfs查询rtc状态接口;

int (*set_mmss)(struct device *, unsigned long secs);   设置以S为单位RTC时间接口;

int (*read_callback)(struct device *, int data);                        

int (*alarm_irq_enable)(struct device *, unsigned int enabled);  中断使能接口;

};

三.RTC底层驱动实现

        RTC低层驱动实现相对比较简单,只需要通过rtc_device_register向系统注册RTC设备,并实现RTC低层操作接口;本文以samsung s3cxx系列中自带的RTC来进行分析,驱动为/drivers/rtc/rtc-s3c.c。
驱动链调用的顺序是"/dev/rtcx"==> “rtc-dev.c” ==>"interface.c" ==> "rtc-s3c.c"

1.用户程序想要读取rtc的时间,就需要利用/dev/rtc的设备文件进行操作

 #include <stdio.h>  #include <stdlib.h>  #include <linux/rtc.h>  #include <sys/ioctl.h>  #include <sys/time.h>  #include <sys/types.h>  #include <fcntl.h>  #include <unistd.h>  #include <errno.h>  #include <time.h>      int main(void)  {          int fd, retval;          struct rtc_time rtc_tm;          time_t timep;          struct tm *p;            fd = open("/dev/rtc", O_RDONLY);          if (fd == -1) {                  perror("/dev/rtc");                  exit(errno);          }            /* Read the RTC time/date */          retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);          if (retval == -1) {                  perror("ioctl");                  exit(errno);          }          close(fd);  }

2.底层通过rtc-dev.c和interface.c完成驱动的open和ioctl

$ vi driver/rtc/rtc-dev.c static const struct file_operations rtc_dev_fops = {                                              |||     rtc_uie_timer    .owner      = THIS_MODULE,                                                                    |||     clear_uie    .llseek     = no_llseek,                                                                      |||     set_uie    .read       = rtc_dev_read,                                                                   |||     rtc_dev_update_irq_enable_emul    .poll       = rtc_dev_poll,                                                                   |||     rtc_dev_read    .unlocked_ioctl = rtc_dev_ioctl,                                                              |||     rtc_dev_poll    .open       = rtc_dev_open,                                                                   |||     rtc_dev_ioctl    .release    = rtc_dev_release,                                                                |||     rtc_dev_fasync    .fasync     = rtc_dev_fasync,                                                                 |||     rtc_dev_release};    static int rtc_dev_open(struct inode *inode, struct file *file)                                   ||  {                                                                                                 ||-   function    int err;                                                                                      |||     rtc_dev_open    struct rtc_device *rtc = container_of(inode->i_cdev,                                          |||     rtc_uie_task                    struct rtc_device, char_dev);                                                 |||     rtc_uie_timer    const struct rtc_class_ops *ops = rtc->ops;                                                   |||     clear_uie                                                                                                  |||     set_uie    if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))                                         |||     rtc_dev_update_irq_enable_emul        return -EBUSY;                                                                            |||     rtc_dev_read                                                                                                  |||     rtc_dev_poll    file->private_data = rtc;                                                                     |||     rtc_dev_ioctl                                                                                                  |||     rtc_dev_fasync    err = ops->open ? ops->open(rtc->dev.parent) : 0;                                             |||     rtc_dev_release    if (err == 0) {                                                                               |||     rtc_dev_prepare        spin_lock_irq(&rtc->irq_lock);                                                            |||     rtc_dev_add_device        rtc->irq_data = 0;                                                                        |||     rtc_dev_del_device        spin_unlock_irq(&rtc->irq_lock);                                                          |||     rtc_dev_init                                                                                                  |||     rtc_dev_exit        return 0;                                                                                 |       }                                                                                             |   ~                                                                                                                                      |   ~                                        /* something has gone wrong */                                                                |   ~                                        clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);                                                  |   ~                                        return err;                                                                                   |   ~                                    }      
static long rtc_dev_ioctl(struct file *file,                                                      |||     rtc_uie_timer        unsigned int cmd, unsigned long arg)                                                      |||     clear_uie{                                                                                                 |||     set_uie    int err = 0;                                                                                  |||     rtc_dev_update_irq_enable_emul    struct rtc_device *rtc = file->private_data;                                                  |||     rtc_dev_read    const struct rtc_class_ops *ops = rtc->ops;                                                   |||     rtc_dev_poll    struct rtc_time tm;                                                                           |||     rtc_dev_ioctl    struct rtc_wkalrm alarm;                                                                      |||     rtc_dev_fasync    void __user *uarg = (void __user *) arg;                                                      |||     rtc_dev_release                                                                                                  |||     rtc_dev_prepare    err = mutex_lock_interruptible(&rtc->ops_lock);                                               |||     rtc_dev_add_device    if (err)                                                                                      |||     rtc_dev_del_device        return err;                                                                               |||     rtc_dev_init                                                                                                  |||     rtc_dev_exit    /* check that the calling task has appropriate permissions                                    |        * for certain ioctls. doing this check here is useful                                        |   ~                                         * to avoid duplicate code in each driver.                                                    |   ~                                         */                                                                                           |   ~                                        switch (cmd) {        case RTC_RD_TIME:                                                                             |||     clear_uie        mutex_unlock(&rtc->ops_lock);                                                             |||     set_uie                                                                                                  |||     rtc_dev_update_irq_enable_emul        err = rtc_read_time(rtc, &tm);                                                            |||     rtc_dev_read        if (err < 0)                                                                              |||     rtc_dev_poll            return err;                                                                           |||     rtc_dev_ioctl                                                                                                  |||     rtc_dev_fasync        if (copy_to_user(uarg, &tm, sizeof(tm)))                                                  |||     rtc_dev_release            err = -EFAULT;                                                                        |||     rtc_dev_prepare        return err;           }}

rtc-dev直接获取rtc的ops来操作open,而ioctl调用了interface.c里面的rtc_read_time接口
$ vi driver/rtc/interface.cstatic int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)                           |||     __rtc_read_time{                                                                                                 |||     rtc_read_time    int err;                                                                                      |||     rtc_set_time    if (!rtc->ops)                                                                                |||     rtc_set_mmss        err = -ENODEV;                                                                            |||     rtc_read_alarm_internal    else if (!rtc->ops->read_time)                                                                |||     __rtc_read_alarm        err = -EINVAL;                                                                            |||     rtc_read_alarm    else {                                                                                        |||     ___rtc_set_alarm        memset(tm, 0, sizeof(struct rtc_time));                                                   |||     __rtc_set_alarm        err = rtc->ops->read_time(rtc->dev.parent, tm);                                           |||     rtc_set_alarm    }                                                                                             |||     rtc_initialize_alarm    return err;                                                                                   |||     rtc_alarm_irq_enable}                                                                                                 |||     rtc_update_irq_enable                                                                                                  |||     rtc_handle_legacy_irqint rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)                                    |||     rtc_aie_update_irq{                                                                                                 |||     rtc_uie_update_irq    int err;                                                                                      |||     rtc_pie_update_irq                                                                                                  |||     rtc_update_irq    err = mutex_lock_interruptible(&rtc->ops_lock);                                               |||     __rtc_match    if (err)                                                                                      |||     rtc_class_open        return err;                                                                               |||     rtc_class_close                                                                                                  |||     rtc_irq_register    err = __rtc_read_time(rtc, tm);                                                               |||     rtc_irq_unregister    mutex_unlock(&rtc->ops_lock);                                                                 |||     rtc_update_hrtimer    return err;                                                                                   |||     rtc_irq_set_state}                                 
 
最终程序是调用rtc-s3c.c里面的read方法
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,                                                                                    },                                                                                                        };       static const struct rtc_class_ops s3c_rtcops = {                                                  |   ~                                        .read_time  = s3c_rtc_gettime,                                                                |   ~                                        .set_time   = s3c_rtc_settime,                                                                |   ~                                        .read_alarm = s3c_rtc_getalarm,                                                               |   ~                                        .set_alarm  = s3c_rtc_setalarm,                                                               |   ~                                        .alarm_irq_enable = s3c_rtc_setaie,                                                           |   ~                                    };    static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)                                                                                                                            |||     wake_en{                                                                                                                                                                                                  |||     s3c_rtc_cpu_type    unsigned int have_retried = 0;                                                                                                                                                                 |||     s3c_rtcops    void __iomem *base = s3c_rtc_base;                                                                                                                                                             |||     s3c_rtc_driver_ids                                                                                                                                                                                                   |||     s3c_rtc_driver retry_get_time:                                                                                                                                                                                   |||     banner    rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);                                                                                                                                                |||     s3c_rtc_init    rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);                                                                                                                                               |||     s3c_rtc_exit    rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);                                                                                                                                               ||      rtc_tm->tm_mon  = readb(base + S3C2410_RTCMON);                                                                                                                                                ||-   function    rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);                                                                                                                                               |||     s3c_rtc_alarmirq    rtc_tm->tm_sec  = readb(base + S3C2410_RTCSEC);                                                                                                                                                |||     s3c_rtc_setaie                                                                                                                                                                                                   |||     s3c_rtc_gettime    /* the only way to work out wether the system was mid-update                                                                                                                                   |||     s3c_rtc_settime     * when we read it is to check the second counter, and if it                                                                                                                                   |||     s3c_rtc_getalarm     * is zero, then we re-try the entire read                                                                                                                                                     |||     s3c_rtc_setalarm     */                                                                                                                                                                                            |||     s3c_rtc_enable                                                                                                                                                                                                   |||     s3c_rtc_remove    if (rtc_tm->tm_sec == 0 && !have_retried) {                                                                                                                                                    |||     s3c_rtc_probe        have_retried = 1;                                                                                                                                                                          |||     s3c_rtc_suspend        goto retry_get_time;                                                                                                                                                                       |||     s3c_rtc_resume    }                                                                                                                                                                                              |||     s3c_rtc_init                                                                                                                                                                                                   |||     s3c_rtc_exit    rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);                                                                                                                                                      |       rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);                                                                                                                                                      |   ~                                        rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);                                                                                                                                                    |   ~                                        rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);                                                                                                                                                    |   ~                                        rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);                                                                                                                                                      |   ~                                        rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);                                                                                                                                                    |   ~                                                                                                                                                                                                                                       |   ~                                        rtc_tm->tm_year += 100;                                                                                                                                                                        |   ~                                                                                                                                                                                                                                       |   ~                                        pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n",                                                                                                                                          |   ~                                             1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,                                                                                                                                  |   ~                                             rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);                                                                                                                                         |   ~                                        printk("%s() %d-%d-%d %d:%d:%d\n", __FUNCTION__,                                                                                                                                               |   ~                                             1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,                                                                                                                                  |   ~                                             rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);                                                                                                                                         |   ~                                                                                                                                                                                                                                       |   ~                                        rtc_tm->tm_mon -= 1;                                                                                                                                                                           |   ~                                                                                                                                                                                                                                       |   ~                                        return rtc_valid_tm(rtc_tm);                                                                                                                                                                   |   ~                                    }                           




参考资料:http://blog.csdn.net/fanqipin/article/details/8089995

http://www.embedu.org/Column/Column468.htm

http://www.linuxidc.com/Linux/2013-08/88964.htm

http://blog.csdn.net/crycheng/article/details/7802502

https://yq.aliyun.com/articles/8396

http://blog.csdn.net/qq_29350001/article/details/51691783

原创粉丝点击