深入解析linux下rtc架构

来源:互联网 发布:人工智能的智能含义 编辑:程序博客网 时间:2024/06/15 03:46

一.描述rtc相关结构体

1.rtc设备

[cpp] view plain copy
  1. struct rtc_device   //rtc设备  
  2. {  
  3.     struct device dev;                  //设备文件  
  4.     struct module *owner;               //所有者  
  5.     int id;                             //次设备号  
  6.     char name[RTC_DEVICE_NAME_SIZE];    //rtc设备名  
  7.     const struct rtc_class_ops *ops;    //rtc类操作函数集  
  8.     struct mutex ops_lock;  
  9.     struct cdev char_dev;               //字符设备  
  10.     unsigned long flags;                //忙标志 (RTC_DEV_BUSY)  
  11.     unsigned long irq_data;             //中断数据  
  12.     spinlock_t irq_lock;  
  13.     wait_queue_head_t irq_queue;  
  14.     struct fasync_struct *async_queue;  
  15.     struct rtc_task *irq_task;          //中断任务  
  16.     spinlock_t irq_task_lock;  
  17.     int irq_freq;                       //中断频率  
  18.     int max_user_freq;                  //默认64  
  19. };  

1.1 同时也定义了一个宏,通过设备文件查找rtc设备

[cpp] view plain copy
  1. #define to_rtc_device(d) container_of(d, struct rtc_device, dev)  

2.rtc类操作函数集

[cpp] view plain copy
  1. struct rtc_class_ops {  
  2.     int (*open)(struct device *);                                       //打开  
  3.     void (*release)(struct device *);                                   //释放  
  4.     int (*ioctl)(struct device *, unsigned int, unsigned long);         //控制  
  5.     int (*read_time)(struct device *, struct rtc_time *);               //读时间  
  6.     int (*set_time)(struct device *, struct rtc_time *);                //设置时间  
  7.     int (*read_alarm)(struct device *, struct rtc_wkalrm *);            //读闹钟  
  8.     int (*set_alarm)(struct device *, struct rtc_wkalrm *);             //设闹钟  
  9.     int (*proc)(struct device *, struct seq_file *);                    //proc接口  
  10.     int (*set_mmss)(struct device *, unsigned long secs);               //设置时间mmss  
  11.     int (*irq_set_state)(struct device *, int enabled);                 //设置中断状态  
  12.     int (*irq_set_freq)(struct device *, int freq);                     //设置中断频率  
  13.     int (*read_callback)(struct device *, int data);                    //读回调函数  
  14.     int (*alarm_irq_enable)(struct device *, unsigned int enabled);     //闹钟中断使能  
  15.     int (*update_irq_enable)(struct device *, unsigned int enabled);    //更新中断使能  
  16. };  

这里有两种设置时间的方法set_time和set_mmss,看它们参数可以区别出set_time使用rtc时间来设置,
set_mmss是根据秒数来设置(“Gregorian”时间)

二.rtc架构

1.rtc设备初始化函数

[cpp] view plain copy
  1. void __init rtc_dev_init(void)  //入口函数  
  2. {  
  3.     int err;  
  4.     err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");    //动态分配rtc设备号 RTC_DEV_MAX=16个  
  5.     if (err < 0)  
  6.         printk(KERN_ERR "%s: failed to allocate char dev region\n",__FILE__);  
  7. }  

2.rtc设备的注册

rtc注册由具体设备驱动调用,同时设备驱动必须提供rtc_class_ops操作函数集

[cpp] view plain copy
  1. struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)  
  2. {  
  3.     struct rtc_device *rtc; //rtc设备  
  4.     int id, err;  
  5.     if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {   //idr机制预分配  
  6.         err = -ENOMEM;  
  7.         goto exit;  
  8.     }  
  9.     mutex_lock(&idr_lock);  
  10.     err = idr_get_new(&rtc_idr, NULL, &id); //通过idr机制获取id号  
  11.     mutex_unlock(&idr_lock);  
  12.     if (err < 0)  
  13.         goto exit;  
  14.     id = id & MAX_ID_MASK;  //id掩码过滤  
  15.     rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);   //分配rtc设备  
  16.     if (rtc == NULL) {  
  17.         err = -ENOMEM;  
  18.         goto exit_idr;  
  19.     }  
  20.     rtc->id = id;                //次设备号  
  21.     rtc->ops = ops;              //rtc类操作函数集  
  22.     rtc->owner = owner;          //所有者  
  23.     rtc->max_user_freq = 64; //最大用户频率  
  24.     rtc->dev.parent = dev;       //设备父设备  
  25.     rtc->dev.class = rtc_class;  //2.1 设备类  
  26.     rtc->dev.release = rtc_device_release;   //设备释放方法  
  27.     mutex_init(&rtc->ops_lock);  
  28.     spin_lock_init(&rtc->irq_lock);  
  29.     spin_lock_init(&rtc->irq_task_lock);  
  30.     init_waitqueue_head(&rtc->irq_queue);  
  31.     strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);  //设置rtc设备名  
  32.     dev_set_name(&rtc->dev, "rtc%d", id);            //设置设备名  
  33.     rtc_dev_prepare(rtc);                           //2.2 rtc设备准备  
  34.     err = device_register(&rtc->dev);                //注册设备文件  
  35.     if (err) {  
  36.         put_device(&rtc->dev);  
  37.         goto exit_kfree;  
  38.     }  
  39.     rtc_dev_add_device(rtc);    //2.3 rtc添加设备  
  40.     rtc_sysfs_add_device(rtc);  //sysfs添加设备文件  
  41.     rtc_proc_add_device(rtc);   //procfs添加设备文件  
  42.     dev_info(dev, "rtc core: registered %s as %s\n",rtc->name, dev_name(&rtc->dev));  
  43.     return rtc;  
  44. exit_kfree:  
  45.     kfree(rtc);  
  46. exit_idr:  
  47.     mutex_lock(&idr_lock);  
  48.     idr_remove(&rtc_idr, id);  
  49.     mutex_unlock(&idr_lock);  
  50. exit:  
  51.     dev_err(dev, "rtc core: unable to register %s, err = %d\n",name, err);  
  52.     return ERR_PTR(err);  
  53. }  
  54. EXPORT_SYMBOL_GPL(rtc_device_register);  

2.1 设备类rtc_class

[cpp] view plain copy
  1. subsys_initcall(rtc_init);  //rtc子系统初始化  

设备类初始化

[cpp] view plain copy
  1. static int __init rtc_init(void)  
  2. {  
  3.     rtc_class = class_create(THIS_MODULE, "rtc");   //创建设备类“/sys/class/rtc”  
  4.     if (IS_ERR(rtc_class)) {  
  5.         printk(KERN_ERR "%s: couldn't create class\n", __FILE__);  
  6.         return PTR_ERR(rtc_class);  
  7.     }  
  8.     rtc_class->suspend = rtc_suspend;    //挂起  
  9.     rtc_class->resume = rtc_resume;      //唤醒  
  10.     rtc_dev_init(); //这里发现 rtc设备初始化函数是在这里调用  
  11.     rtc_sysfs_init(rtc_class);  //sysfs接口  
  12.     return 0;  
  13. }  

rtc子系统初始化:rtc类初始化->rtc设备初始化->注册rtc设备

2.2 rtc设备准备

[cpp] view plain copy
  1. void rtc_dev_prepare(struct rtc_device *rtc)  
  2. {  
  3.     if (!rtc_devt)  //主设备号是否分配  
  4.         return;  
  5.     if (rtc->id >= RTC_DEV_MAX) { //判断次设备号是否>16,最多支持16个RTC  
  6.         pr_debug("%s: too many RTC devices\n", rtc->name);  
  7.         return;  
  8.     }  
  9.     rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);  //设置rtc设备号  
  10.     cdev_init(&rtc->char_dev, &rtc_dev_fops);    //初始化字符设备 捆绑了字符设备操作函数集  
  11.     rtc->char_dev.owner = rtc->owner; //模块所有者  
  12. }  

2.3 rtc添加设备

[cpp] view plain copy
  1. void rtc_dev_add_device(struct rtc_device *rtc)  
  2. {  
  3.     if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))   //添加字符设备  
  4.         printk(KERN_WARNING "%s: failed to add char device %d:%d\n",rtc->name, MAJOR(rtc_devt), rtc->id);  
  5.     else  
  6.         pr_debug("%s: dev (%d:%d)\n", rtc->name,MAJOR(rtc_devt), rtc->id);  
  7. }  

字符设备相关的初始化总结:
"1"分配设备号,"2.2"初始化字符设备(捆绑操作函数集),“2.3”添加字符设备
“2.1”初始化设备类 "2"注册设备文件
rtc字符设备操作函数集rtc_dev_fops是系统提供的,每次操作/dev/rtcXXX就会调用其操作函数集的方法

三.rtc设备接口

1.rtc字符设备操作函数集

[cpp] view plain copy
  1. static const struct file_operations rtc_dev_fops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .llseek     = no_llseek,  
  4.     .read       = rtc_dev_read,         //读方法  
  5.     .poll       = rtc_dev_poll,         //轮询  
  6.     .unlocked_ioctl = rtc_dev_ioctl,    //控制  
  7.     .open       = rtc_dev_open,         //打开  
  8.     .release    = rtc_dev_release,      //释放  
  9.     .fasync     = rtc_dev_fasync,       //异步通知  
  10. };  

2.open方法

[cpp] view plain copy
  1. static int rtc_dev_open(struct inode *inode, struct file *file)  
  2. {  
  3.     int err;  
  4.     struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);    //获取对应的rtc设备  
  5.     const struct rtc_class_ops *ops = rtc->ops;  //获取对应的rtc设备类操作函数集  
  6.     if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))    //测试是否忙,不忙则设置忙标志  
  7.         return -EBUSY;  
  8.     file->private_data = rtc;    //将rtc设备作为文件的私有数据  
  9.     err = ops->open ? ops->open(rtc->dev.parent) : 0;  //存在open方法则调用其open方法  
  10.     if (err == 0) {  
  11.         spin_lock_irq(&rtc->irq_lock);  
  12.         rtc->irq_data = 0;       //中断数据清0  
  13.         spin_unlock_irq(&rtc->irq_lock);  
  14.         return 0;  
  15.     }  
  16.     clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); //清除忙标志  
  17.     return err;  
  18. }  

涉及两个操作函数集
a.rtc设备类操作函数集 rtc_class_ops   --- 具体设备驱动提供
b.rtc字符设备操作函数集 rtc_dev_fops --- rtc子系统提供
3.ioctl方法
3.1控制命令定义

[cpp] view plain copy
  1. #define RTC_AIE_ON      _IO('p', 0x01)                          /* Alarm int. enable on     使能RTC闹钟中断*/  
  2. #define RTC_AIE_OFF _IO('p', 0x02)                          /* ... off                  禁用RTC闹钟中断*/  
  3. #define RTC_UIE_ON      _IO('p', 0x03)                          /* Update int. enable on    使能更新RTC中断*/  
  4. #define RTC_UIE_OFF _IO('p', 0x04)                          /* ... off                  禁能更新RTC中断*/  
  5. #define RTC_PIE_ON      _IO('p', 0x05)                          /* Periodic int. enable on  使能RTC周期中断*/  
  6. #define RTC_PIE_OFF _IO('p', 0x06)                          /* ... off                  禁能RTC周期中断*/  
  7. #define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time)        /* Set alarm time           设置闹钟时间*/  
  8. #define RTC_ALM_READ    _IOR('p', 0x08, struct rtc_time)        /* Read alarm time          读取闹钟时间*/  
  9. #define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time)        /* Read RTC time            读取时间与日期*/  
  10. #define RTC_SET_TIME    _IOW('p', 0x0a, struct rtc_time)        /* Set RTC time             设置时间与日期*/  
  11. #define RTC_IRQP_READ   _IOR('p', 0x0b, unsigned long)          /* Read IRQ rate            读取中断频率*/  
  12. #define RTC_IRQP_SET    _IOW('p', 0x0c, unsigned long)          /* Set IRQ rate             设置中断频率*/  
  13. #define RTC_WKALM_SET   _IOW('p', 0x0f, struct rtc_wkalrm)      /* Set wakeup alarm         设置唤醒闹钟*/  
  14. #define RTC_WKALM_RD    _IOR('p', 0x10, struct rtc_wkalrm)      /* Get wakeup alarm         获取唤醒闹钟*/  

命令带的参数结构体:
3.1.1 rtc时间 rtc_time

[cpp] view plain copy
  1. struct rtc_time {   //rtc时间结构体  
  2.     int tm_sec;     //秒  
  3.     int tm_min;     //分  
  4.     int tm_hour;    //时  
  5.     int tm_mday;    //日  
  6.     int tm_mon;     //月  
  7.     int tm_year;    //年数(xxx-1900)  
  8.     int tm_wday;    //星期几  
  9.     int tm_yday;    //一年的第几天  
  10.     int tm_isdst;   //夏令时  
  11. };  

3.1.2 rtc闹钟 rtc_wkalrm

[cpp] view plain copy
  1. struct rtc_wkalrm { //rtc闹钟结构体  
  2.     unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled 闹钟使能开关*/  
  3.     unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending 闹钟挂起*/  
  4.     struct rtc_time time;   /* time the alarm is set to 闹钟时间*/  
  5. };  

3.2 rtc设备控制

[cpp] view plain copy
  1. static long rtc_dev_ioctl(struct file *file,unsigned int cmd, unsigned long arg)  
  2. {  
  3.     int err = 0;  
  4.     struct rtc_device *rtc = file->private_data; //获取rtc设备  
  5.     const struct rtc_class_ops *ops = rtc->ops;  //获取rtc操作函数集  
  6.     struct rtc_time tm//rtc时间  
  7.     struct rtc_wkalrm alarm;    //rtc闹钟  
  8.     void __user *uarg = (void __user *) arg;  
  9.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  10.     if (err)  
  11.         return err;  
  12.     //根据命令判断调用任务是否有权限  
  13.     switch (cmd) {    
  14.     case RTC_EPOCH_SET:  
  15.     case RTC_SET_TIME:  
  16.         if (!capable(CAP_SYS_TIME))  
  17.             err = -EACCES;  
  18.         break;  
  19.     case RTC_IRQP_SET:  
  20.         if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))  
  21.             err = -EACCES;  
  22.         break;  
  23.     case RTC_PIE_ON:  
  24.         if (rtc->irq_freq > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))  
  25.             err = -EACCES;  
  26.         break;  
  27.     }  
  28.     if (err)  
  29.         goto done;  
  30.     /* 先调用设备驱动的类操作函数集结构体*/  
  31.     if (ops->ioctl) {    //rtc操作函数集存在控制方法  
  32.         err = ops->ioctl(rtc->dev.parent, cmd, arg);  //调用其控制方法  
  33.         if (err != -ENOIOCTLCMD) {  
  34.             mutex_unlock(&rtc->ops_lock);  
  35.             return err;  
  36.         }  
  37.     }  
  38. //______________________________________________________________________________________  
  39.     switch (cmd) {  
  40.     case RTC_ALM_READ:  //读取闹钟时间  
  41.         mutex_unlock(&rtc->ops_lock);  
  42.         err = rtc_read_alarm(rtc, &alarm);  //3.1.3 读取闹钟时间  
  43.         if (err < 0)  
  44.             return err;  
  45.         if (copy_to_user(uarg, &alarm.time, sizeof(tm)))    //复制到用户空间  
  46.             err = -EFAULT;  
  47.         return err;  
  48.     case RTC_ALM_SET:   //设置闹钟  
  49.         mutex_unlock(&rtc->ops_lock);  
  50.         if (copy_from_user(&alarm.time, uarg, sizeof(tm)))  //拷贝到内核空间  
  51.             return -EFAULT;  
  52.         //初始化清除部分属性  
  53.         alarm.enabled = 0;  
  54.         alarm.pending = 0;  
  55.         alarm.time.tm_wday = -1;  
  56.         alarm.time.tm_yday = -1;  
  57.         alarm.time.tm_isdst = -1;  
  58.         {  
  59.             unsigned long now, then;  
  60.             err = rtc_read_time(rtc, &tm);  //读取时间  
  61.             if (err < 0)  
  62.                 return err;  
  63.             rtc_tm_to_time(&tm, &now);  //当前时间 转换成格里高里历法时间  
  64.             alarm.time.tm_mday = tm.tm_mday;    //设置日期  
  65.             alarm.time.tm_mon = tm.tm_mon;      //设置月  
  66.             alarm.time.tm_year = tm.tm_year;    //设置年  
  67.             err  = rtc_valid_tm(&alarm.time);   //校验时间合理性  
  68.             if (err < 0)  
  69.                 return err;  
  70.             rtc_tm_to_time(&alarm.time, &then); //闹钟时间 转换成“Gregorian”时间  
  71.             if (then < now) {    //现在的时间已经过了闹钟时间,那么就设置明天闹  
  72.                 rtc_time_to_tm(now + 24 * 60 * 60, &tm);    //“Gregorian”时间时间+1天 转rtc_time  
  73.                 alarm.time.tm_mday = tm.tm_mday;    //设置日期  
  74.                 alarm.time.tm_mon = tm.tm_mon;      //设置月  
  75.                 alarm.time.tm_year = tm.tm_year;    //设置年  
  76.             }  
  77.         }  
  78.         return rtc_set_alarm(rtc, &alarm);  //3.1.4 设置闹钟  
  79.     case RTC_RD_TIME:   //获取时间  
  80.         mutex_unlock(&rtc->ops_lock);  
  81.         err = rtc_read_time(rtc, &tm);      //3.1.1 获取时间  
  82.         if (err < 0)  
  83.             return err;  
  84.         if (copy_to_user(uarg, &tmsizeof(tm)))    //复制到用户空间  
  85.             err = -EFAULT;  
  86.         return err;  
  87.     case RTC_SET_TIME:  //设置时间  
  88.         mutex_unlock(&rtc->ops_lock);  
  89.         if (copy_from_user(&tm, uarg, sizeof(tm)))  //复制到用户空间  
  90.             return -EFAULT;  
  91.         return rtc_set_time(rtc, &tm);  //3.1.2 设置系统时间  
  92.     case RTC_PIE_ON:    //使能周期中断  
  93.         err = rtc_irq_set_state(rtc, NULL, 1);  //使能周期中断  
  94.         break;  
  95.     case RTC_PIE_OFF:   //禁用周期中断  
  96.         err = rtc_irq_set_state(rtc, NULL, 0);  //禁用周期中断  
  97.         break;  
  98.     case RTC_AIE_ON:    //使能闹钟中断  
  99.         mutex_unlock(&rtc->ops_lock);  
  100.         return rtc_alarm_irq_enable(rtc, 1);    //使能闹钟中断  
  101.     case RTC_AIE_OFF:   //禁用闹钟中断  
  102.         mutex_unlock(&rtc->ops_lock);  
  103.         return rtc_alarm_irq_enable(rtc, 0);    //禁用闹钟中断  
  104.     case RTC_UIE_ON:    //使能更新中断  
  105.         mutex_unlock(&rtc->ops_lock);  
  106.         return rtc_update_irq_enable(rtc, 1);   //使能更新中断  
  107.     case RTC_UIE_OFF:   //禁用更新中断  
  108.         mutex_unlock(&rtc->ops_lock);  
  109.         return rtc_update_irq_enable(rtc, 0);   //禁用更新中断  
  110.     case RTC_IRQP_SET:  //设置中断频率  
  111.         err = rtc_irq_set_freq(rtc, NULL, arg); //设置中断频率  
  112.         break;  
  113.     case RTC_IRQP_READ: //读取中断频率  
  114.         err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); //读取中断频率  
  115.         break;  
  116.     case RTC_WKALM_SET: //设置唤醒闹钟  
  117.         mutex_unlock(&rtc->ops_lock);  
  118.         if (copy_from_user(&alarm, uarg, sizeof(alarm)))    //从用户空间复制  
  119.             return -EFAULT;  
  120.         return rtc_set_alarm(rtc, &alarm);  //设置闹钟  
  121.     case RTC_WKALM_RD:  //读取唤醒闹钟  
  122.         mutex_unlock(&rtc->ops_lock);  
  123.         err = rtc_read_alarm(rtc, &alarm);  //读取闹钟  
  124.         if (err < 0)  
  125.             return err;  
  126.         if (copy_to_user(uarg, &alarm, sizeof(alarm)))  //复制到用户空间  
  127.             err = -EFAULT;  
  128.         return err;  
  129.     default:  
  130.         err = -ENOTTY;  
  131.         break;  
  132.     }  
  133. done:  
  134.     mutex_unlock(&rtc->ops_lock);  
  135.     return err;  
  136. }  

3.1.0 时间相关函数解析
A.api函数

[cpp] view plain copy
  1. int rtc_month_days(unsigned int month, unsigned int year)   //计算某年某月的天数  
  2. int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)  //计算某年某月某日是该年的第几日  
  3. int rtc_valid_tm(struct rtc_time *tm)   //检测rtc时间的合理性  
  4. void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)    //“Gregorian”时间转换成rtc时间  
  5. int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)    //rtc时间转换成“Gregorian”时间  

“Gregorian”时间:自01-01-1970 00:00:00到现在的秒数值
rtc时钟的年数是今年减去1900年的数值
B.使用的全局数组
B.1 每个月的天数rtc_days_in_month

[cpp] view plain copy
  1. static const unsigned char rtc_days_in_month[] = {  //每个月的天数  
  2.     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31  
  3. };  

B.2 从年头1月1日到各个月的月头经过的日子数

[cpp] view plain copy
  1. static const unsigned short rtc_ydays[2][13] = {  
  2. /*常年    1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月,1月*/  
  3.        { 0 ,31 ,59 ,90 ,120,151,181,212,243,273 ,304 ,334,365 },  
  4. /*闰年情况*/  
  5.        { 0 ,31 ,60 ,91 ,121,152,182,213,244,274 ,305 ,335,366 }  
  6. };  

B.3 判断是否闰年

[cpp] view plain copy
  1. static inline bool is_leap_year(unsigned int year)  //判断是否闰年  
  2. {  
  3.     return (!(year % 4) && (year % 100)) || !(year % 400);  
  4. }  

B.4 计算自公元0年经过了多少个闰年

[cpp] view plain copy
  1. #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)  

C 函数解析
C.1 rtc_month_days

[cpp] view plain copy
  1. int rtc_month_days(unsigned int month, unsigned int year)  
  2. {           //month月的天数         +  是否闰年?&&是否二月份?    都是旧加多1天  
  3.     return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);  
  4. }  
  5. EXPORT_SYMBOL(rtc_month_days);  

C.2 rtc_year_days

[cpp] view plain copy
  1. int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)  //日期计算 是一年中的第几日  
  2. {          //日子数[year是否闰年?][第month月]+日-1  
  3.     return rtc_ydays[is_leap_year(year)][month] + day-1;  
  4. }  
  5. EXPORT_SYMBOL(rtc_year_days);  

C.3 rtc_valid_tm

[cpp] view plain copy
  1. int rtc_valid_tm(struct rtc_time *tm)   //检测rtc时间的合理性  
  2. {  
  3.     if (tm->tm_year < 70  //年分小于1970年  
  4.         || ((unsigned)tm->tm_mon) >= 12   //月份大于12月  
  5.         || tm->tm_mday < 1    //日期小于1  
  6.         || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) //日期大于当月最大日期  
  7.         || ((unsigned)tm->tm_hour) >= 24  //时大于24  
  8.         || ((unsigned)tm->tm_min) >= 60       //分大于60  
  9.         || ((unsigned)tm->tm_sec) >= 60)  //秒大于60  
  10.         return -EINVAL;  
  11.     return 0;  
  12. }  
  13. EXPORT_SYMBOL(rtc_valid_tm);  

C.4 rtc_time_to_tm

[cpp] view plain copy
  1. void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)    //“Gregorian”时间转换rtc时间  
  2. {  
  3.     unsigned int month, year;  
  4.     int days;  
  5.     days = time / 86400;    //总秒数/(24*60*60[1天的秒数])等于过去的日子数  
  6.     time -= (unsigned int) days * 86400;    //剩余不足一天的秒数  
  7.     tm->tm_wday = (days + 4) % 7;    //计算星期几(1970-01-01刚好是星期3所以+4)  
  8.     year = 1970 + days / 365;   //计算现在是哪一年=1970+过去的日字数/365  
  9.     days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1);  
  10.     //计算剩下不足一年的日子数,并调整闰年  
  11.     if (days < 0) {  //调整  
  12.         year -= 1;  
  13.         days += 365 + is_leap_year(year);  
  14.     }  
  15.     tm->tm_year = year - 1900;   //rtc时间是1900年到现在的年数  
  16.     tm->tm_yday = days + 1;  //一年中的第几天  
  17.     for (month = 0; month < 11; month++) {   //计算是几月几日  
  18.         int newdays;  
  19.         newdays = days - rtc_month_days(month, year);   //减每个月的天数  
  20.         if (newdays < 0)  
  21.             break;  
  22.         days = newdays;  
  23.     }  
  24.     tm->tm_mon = month;  //月份  
  25.     tm->tm_mday = days + 1;  //日期  
  26.     tm->tm_hour = time / 3600;   //小时 3600=60s*60m  
  27.     time -= tm->tm_hour * 3600;      //剩下不足1小时的秒数  
  28.     tm->tm_min = time / 60;  //分钟  
  29.     tm->tm_sec = time - tm->tm_min * 60;  //剩下不足1分钟的秒数  
  30. }  
  31. EXPORT_SYMBOL(rtc_time_to_tm);  

C.5 rtc_tm_to_time

[cpp] view plain copy
  1. int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)    //rtc时间转换成“Gregorian”时间  
  2. {  
  3.     *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec);  
  4.     return 0;  
  5. }  
  6. EXPORT_SYMBOL(rtc_tm_to_time);  

调用mktime函数 加上1900得到真正的年数,(月份+1)调整到正常的年数tm_mon是[0~11]调整成[1~12]

[cpp] view plain copy
  1. unsigned long mktime(const unsigned int year0, const unsigned int mon0,const unsigned int day,   
  2.                         const unsigned int hour,const unsigned int min, const unsigned int sec)  
  3. {  
  4.     unsigned int mon = mon0, year = year0;  
  5.     /* 1..12 -> 11,12,1..10 */  
  6.     if (0 >= (int) (mon -= 2)) { //判断是否过了2月份,同时mon减去2  
  7.         mon += 12;  //月份+12   
  8.         year -= 1;  //年份也调少1年  
  9.     }     
  10.     //上面调整了月份排序,也等价于,忽略1,2月份过去的天数,新调整的月份排序参考D.4  
  11.     return ((((unsigned long)  
  12.           (year/4 - year/100 + year/400 + 367*mon/12 + day) +   //  
  13.           year*365 - 719499                 //                                      =总天数  
  14.         )*24 + hour /* now have hours */    //日子数*24 + 剩余的小时数               =总小时数  
  15.       )*60 + min /* now have minutes */ //总小时数*60 + 剩余分数                    =总分钟数  
  16.     )*60 + sec; /* finally seconds */   //总分钟数*60 + 剩余秒数                    =总秒数  
  17. }  
  18. EXPORT_SYMBOL(mktime);  

D.针对(year/4 - year/100 + year/400 + 367*mon/12 + day) + year*365 - 719499总天数的解析
D.1 先对719499分拆,1970年1月1日00:00:00距离公元元年=(1970-1)*365+(1970/4-1970/100+1970/400)=718685+(492-19+4)=719162(1970日字数)
 719499-719162=337
D.2 现在距离公元元年的整年计算(year-1)*365+(year/4 - year/100 + year/400)//可以假设是在年头1月1日00:00:00 mon=1,day=1,
 结合D1结论距离1970年1月1日00:00:00可化简为:
 [(year-1)*365+(year/4 - year/100 + year/400)+30]+1- 719499 + 365
    [(整年日子数)+367*mon/12]+day-(1970日子数)-337 + 365
D.3 整日的计算(day-1),当天不能算,假如现在是2号,那其实只有1号是整日的,2号还没过完所以2-1
 结合D.2结论调整公式:
 [(year-1)*365+(year/4 - year/100 + year/400)]+ 367*mon/12 + (day-1 )-   719162  -(337+1) + 365
   [(整年日子数)]       + 367*mon/12 + [整日数]-(1970日子数)-(336)   + 365
 调整位置 [整年日字数] + [367*mon/12 -(336)  + 365] + [整日数] - [1970日子数]
 剩下的应该是整月日子数咯:[367*mon/12 -(336)  + 365] =[367*mon/12 + 29]
D.4 新调整的月份排序,改排序等价于换了天数
 1 2 3 4 5 6 7 8 9 10 11 12
 31 30 31 30 31 31 30 31 30 31 31 28
 由于调整把28天的放到了12月份,所以在计算某个月过去的天数的时候12月份28天,不参与进来,参与进来就按整年日子数算了
 所以剩余整月日子数约等于30*(mon-1) 这里是假设每个mon都为30所以用约等于,正确值需要修正也就是把某个月有31天的情况添加进去
 那么就1,3,5,6,8,10,11这几个月是31天,那就等价于要创建一个公式来表达多出来的1天的总天数 令x=mon,那么y的表达式应该是
 y{
      y=0 (x=1)
      y=1 (x=2 ~ 3)
     y=2 (x=4 ~ 5)
     y=3 (x=6)
     y=4 (x=7 ~ 8)
     y=5 (x=9 ~ 10) 
     y=6 (x=11)
     y=7 (x=12)
 }
 这个函数可以有很多表达式,这里mktime使用的表达式是 y=(x + x/6)/2 可以依次待入x检验函数
 那么剩余日字数就可以表达为30*(mon-1)+(mon+mon/6)/2通分一下(分子分母都乘以6):180*(mon-1)*2/12+(6*mon+mon)/12=(360+6+1)*mon/12-30=367*mon/12-30
 1月份和2月份的天数加回来:367*mon/12-30+(31+28)= 367*mon/12 + 29
D.5 总结一下
  (year/4 - year/100 + year/400 + 367*mon/12 + day) + year*365 - 719499
 = (year/4 - year/100 + year/400)+ (367*mon/12-30) +30 + (day-1) + 1 + (year-1)*365 + 365 - [(1970-1)*365+(1970/4-1970/100+1970/400)]-337
 =   {距离公元元年闰年数    + 整月日子数  + 整日数      + 整年日字数     -  [1970年头到公元元年日子数]} +(30+1+365-337)
 = {xxx} + (31[1月份天数]+28[2月份天数])
3.1.1 获取时间 rtc_read_time

[cpp] view plain copy
  1. int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)  //读取时间  
  2. {  
  3.     int err;  
  4.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  5.     if (err)  
  6.         return err;  
  7.     if (!rtc->ops)   //rtc操作函数集存在  
  8.         err = -ENODEV;  
  9.     else if (!rtc->ops->read_time)    //不存在读时间方法  
  10.         err = -EINVAL;  
  11.     else {  
  12.         memset(tm, 0, sizeof(struct rtc_time)); //初始化rtc_time结构体  
  13.         err = rtc->ops->read_time(rtc->dev.parent, tm);    //调用读时间方法  
  14.     }  
  15.     mutex_unlock(&rtc->ops_lock);  
  16.     return err;  
  17. }  
  18. EXPORT_SYMBOL_GPL(rtc_read_time);  

3.1.2 设置系统时间 rtc_set_time

[cpp] view plain copy
  1. int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)   //设置时间  
  2. {  
  3.     int err;  
  4.     err = rtc_valid_tm(tm); //校验时间的合理性  
  5.     if (err != 0)  
  6.         return err;  
  7.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  8.     if (err)  
  9.         return err;  
  10.     if (!rtc->ops)   //存在rtc操作函数集  
  11.         err = -ENODEV;  
  12.     else if (rtc->ops->set_time)  //存在设置时间方法  
  13.         err = rtc->ops->set_time(rtc->dev.parent, tm); //则调用设置时间方法  
  14.     else if (rtc->ops->set_mmss) {    //没有则判断是否存在设置时间mmss方法  
  15.         unsigned long secs;  
  16.         err = rtc_tm_to_time(tm, &secs);    //转换成“Gregorian”时间  
  17.         if (err == 0)  
  18.             err = rtc->ops->set_mmss(rtc->dev.parent, secs);   //则调用设置时间mmss方法  
  19.     }   
  20.     else  
  21.         err = -EINVAL;  
  22.     mutex_unlock(&rtc->ops_lock);  
  23.     return err;  
  24. }  
  25. EXPORT_SYMBOL_GPL(rtc_set_time);  

3.1.3 读取闹钟时间

[cpp] view plain copy
  1. int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)    //读取闹钟  
  2. {  
  3.     int err;  
  4.     struct rtc_time before, now;  
  5.     int first_time = 1;  
  6.     unsigned long t_now, t_alm;  
  7.     enum { none, day, month, year } missing = none;     //枚举可以这样写哦!  
  8.     unsigned days;  
  9.     err = rtc_read_time(rtc, &before);  //读取rtc时间before  
  10.     if (err < 0)  
  11.         return err;  
  12.     do {  
  13.         if (!first_time)    //重读的话,就更新before时间  
  14.             memcpy(&before, &now, sizeof(struct rtc_time));  
  15.         first_time = 0;  
  16.         err = rtc_read_alarm_internal(rtc, alarm);  //3.1.3.1 读取rtc闹钟  
  17.         if (err)  
  18.             return err;  
  19.         if (!alarm->enabled)  
  20.             return 0;  
  21.         if (rtc_valid_tm(&alarm->time) == 0) //检测rtc时间合理性  
  22.             return 0;  
  23.         err = rtc_read_time(rtc, &now); //再次读取rtc时间now   
  24.         if (err < 0)  
  25.             return err;  
  26.             //比较分,时,月,年没发生变化(这里忽略秒级),变化了就重读  
  27.     } while (before.tm_min != now.tm_min|| before.tm_hour  != now.tm_hour || before.tm_mon   != now.tm_mon || before.tm_year  != now.tm_year);  
  28.     //闹钟读取失败其时间域将会置为-1  
  29.     if (alarm->time.tm_sec == -1)  
  30.         alarm->time.tm_sec = now.tm_sec; //设置秒  
  31.     if (alarm->time.tm_min == -1)  
  32.         alarm->time.tm_min = now.tm_min; //设置分  
  33.     if (alarm->time.tm_hour == -1)  
  34.         alarm->time.tm_hour = now.tm_hour;   //设置时  
  35.     /* 结合后面ds1307 “3 读区闹钟ds1337_read_alarm” */  
  36.     if (alarm->time.tm_mday == -1) {  
  37.         alarm->time.tm_mday = now.tm_mday;   //设置日  
  38.         missing = day;          //没设置日期 (05:00:00)  
  39.     }  
  40.     if (alarm->time.tm_mon == -1) {    
  41.         alarm->time.tm_mon = now.tm_mon; //设置月  
  42.         if (missing == none)  
  43.             missing = month;    //没设置月份 (xx-xx-31 05:00:00)  
  44.     }  
  45.     if (alarm->time.tm_year == -1) {  
  46.         alarm->time.tm_year = now.tm_year;   //设置年  
  47.         if (missing == none)  
  48.             missing = year;     //没设置年份 (xx-2-29 05:00:00)  
  49.     }  
  50.     rtc_tm_to_time(&now, &t_now);   //时间转“Gregorian”时间  
  51.     rtc_tm_to_time(&alarm->time, &t_alm);    //时间转“Gregorian”时间  
  52.     if (t_now < t_alm)   //闹钟时间比现在时间晚,闹钟今天还会闹  
  53.         goto done;  
  54.     switch (missing) {  
  55.     case day:       //明天闹 (现在是星期一10点,闹钟是5点,那么就得设置成星期二5点)  
  56.         dev_dbg(&rtc->dev, "alarm rollover: %s\n""day");  
  57.         t_alm += 24 * 60 * 60;  //加多1天  
  58.         rtc_time_to_tm(t_alm, &alarm->time); //转成rtc时间  
  59.         break;  
  60.     case month:     //下个月明天闹 (如果星期一是31号了,那么星期二5点闹 还得调整下月份)  
  61.         dev_dbg(&rtc->dev, "alarm rollover: %s\n""month");  
  62.         do {  
  63.             if (alarm->time.tm_mon < 11)  //小于12月份  
  64.                 alarm->time.tm_mon++;        //月份+1  
  65.             else {  //(如果是12月31日了,那么还得调整年份)  
  66.                 alarm->time.tm_mon = 0;      //变成1月份  
  67.                 alarm->time.tm_year++;       //年份+1  
  68.             }  
  69.             days = rtc_month_days(alarm->time.tm_mon,alarm->time.tm_year);    //闹钟日期计算出该月的天数  
  70.         } while (days < alarm->time.tm_mday); //天数不对再调整(例如设置31号闹,下个月不一定有31号)  
  71.         break;  
  72.     case year:      //n年后闹(闰年2月29号闹的)  
  73.         dev_dbg(&rtc->dev, "alarm rollover: %s\n""year");  
  74.         do {  
  75.             alarm->time.tm_year++;   //年份+1  
  76.         } while (rtc_valid_tm(&alarm->time) != 0);  
  77.         break;  
  78.     default:  
  79.         dev_warn(&rtc->dev, "alarm rollover not handled\n");  
  80.     }  
  81. done:  
  82.     return 0;  
  83. }  
  84. EXPORT_SYMBOL_GPL(rtc_read_alarm);  

3.1.3.1 读取rtc闹钟

[cpp] view plain copy
  1. static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)    //读取内部时钟  
  2. {  
  3.     int err;  
  4.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  5.     if (err)  
  6.         return err;  
  7.     if (rtc->ops == NULL)    //存在rtc操作函数集  
  8.         err = -ENODEV;  
  9.     else if (!rtc->ops->read_alarm)   //存在读取闹钟方法  
  10.         err = -EINVAL;  
  11.     else {  
  12.         memset(alarm, 0, sizeof(struct rtc_wkalrm));    //初始化rtc_wkalrm结构体对象  
  13.         err = rtc->ops->read_alarm(rtc->dev.parent, alarm);    //调用读取闹钟方法  
  14.     }  
  15.     mutex_unlock(&rtc->ops_lock);  
  16.     return err;  
  17. }  

3.1.4 设置闹钟

[cpp] view plain copy
  1. int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) //设置闹钟  
  2. {  
  3.     int err;  
  4.     err = rtc_valid_tm(&alarm->time);    //检验时间合理性  
  5.     if (err != 0)  
  6.         return err;  
  7.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  8.     if (err)  
  9.         return err;  
  10.     if (!rtc->ops)   //存在rtc操作函数集  
  11.         err = -ENODEV;  
  12.     else if (!rtc->ops->set_alarm)    //不存在设置闹钟方法  
  13.         err = -EINVAL;  
  14.     else  
  15.         err = rtc->ops->set_alarm(rtc->dev.parent, alarm); //调用设置闹钟方法  
  16.     mutex_unlock(&rtc->ops_lock);  
  17.     return err;  
  18. }  
  19. EXPORT_SYMBOL_GPL(rtc_set_alarm);  

3.1.4 其他
对于RTC_WKALM_RD和RTC_WKALM_SET命令跟"3.1.3"和“3.1.4”差不多,两者都会调用rtc类操作函数的读/写闹钟方法
区别在于RTC_WKALM_RD和RTC_WKALM_SET命令带的参数是rtc_wkalrm结构体,另一组命令则是rtc_time结构体

中断禁用使能函数 都是调用设备驱动提供的对应的rtc类操作函数去完成

[cpp] view plain copy
  1. RTC_PIE_ON      rtc_irq_set_state(rtc, NULL, 1)       
  2. RTC_PIE_OFF     rtc_irq_set_state(rtc, NULL, 0)     rtc->ops->irq_set_state(rtc->dev.parent, enabled)  
  3.   
  4. RTC_AIE_ON      rtc_alarm_irq_enable(rtc, 1)          
  5. RTC_AIE_OFF     rtc_alarm_irq_enable(rtc, 0)        rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled)  
  6.   
  7. RTC_UIE_ON      rtc_update_irq_enable(rtc, 1)  
  8. RTC_UIE_OFF     rtc_update_irq_enable(rtc, 0)       rtc->ops->update_irq_enable(rtc->dev.parent, enabled)  


中断频率设置读取函数 都是调用设备驱动提供的对应的rtc类操作函数去完成

[cpp] view plain copy
  1. RTC_IRQP_SET    rtc_irq_set_freq(rtc, NULL, arg)    rtc->ops->irq_set_freq(rtc->dev.parent, freq)  
  2.   
  3. RTC_IRQP_READ   put_user(rtc->irq_freq, (unsigned long __user *)uarg)  

4.read方法

[cpp] view plain copy
  1. static ssize_t rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  2. {  
  3.     struct rtc_device *rtc = file->private_data; //获取rtc设备  
  4.     DECLARE_WAITQUEUE(wait, current);   //声明等待队列  
  5.     unsigned long data;  
  6.     ssize_t ret;  
  7.     if (count != sizeof(unsigned int) && count < sizeof(unsigned long))  //判断数据长度  
  8.         return -EINVAL;  
  9.     add_wait_queue(&rtc->irq_queue, &wait);  //添加进等待队列  
  10.     do {  
  11.         __set_current_state(TASK_INTERRUPTIBLE);    //设置任务可中断态  
  12.         spin_lock_irq(&rtc->irq_lock);  
  13.         data = rtc->irq_data;    //获取中断数据  
  14.         rtc->irq_data = 0;   //清除中断数据  
  15.         spin_unlock_irq(&rtc->irq_lock);  
  16.         if (data != 0) {    //数据不为0,传输正常退出while循环  
  17.             ret = 0;  
  18.             break;  
  19.         }  
  20.         if (file->f_flags & O_NONBLOCK) {    //设置文件标志  
  21.             ret = -EAGAIN;  
  22.             break;  
  23.         }  
  24.         if (signal_pending(current)) {  //挂起  
  25.             ret = -ERESTARTSYS;  
  26.             break;  
  27.         }  
  28.         schedule();  
  29.     } while (1);  
  30.     set_current_state(TASK_RUNNING);    //设置任务运行态  
  31.     remove_wait_queue(&rtc->irq_queue, &wait);   //移出等待队列  
  32.     if (ret == 0) {  
  33.         /* Check for any data updates */  
  34.         if (rtc->ops->read_callback)  //存在读数据回调函数  
  35.             data = rtc->ops->read_callback(rtc->dev.parent,data);  //调用读数据回调函数  
  36.         if (sizeof(int) != sizeof(long) && count == sizeof(unsigned int))  
  37.             ret = put_user(data, (unsigned int __user *)buf) ?:sizeof(unsigned int);    //上传数据到用户空间  
  38.         else  
  39.             ret = put_user(data, (unsigned long __user *)buf) ?:sizeof(unsigned long);  //上传数据到用户空间  
  40.     }  
  41.     return ret;  
  42. }  

读方法主要获取中断数据rtc->irq_data,调用rtc设备类的read_callback方法处理,并上传到用户空间,
rtc的中断数据在rtc_update_irq函数中填充,num参数是中断报告个数(存放于irq_data前8位),events代表中断事件
irq_data后8位由RTC_UF 0x10(更新中断)、RTC_AF 0x20(闹钟中断)、RTC_PF 0x40(周期中断)、RTC_IRQF 0x80
rtc_update_irq一般由设备驱动的中断处理例程或rtc_uie_task函数调用,并传递参数进来

[cpp] view plain copy
  1. void rtc_update_irq(struct rtc_device *rtc,unsigned long num, unsigned long events)  
  2. {  
  3.     unsigned long flags;  
  4.     spin_lock_irqsave(&rtc->irq_lock, flags);  
  5.     rtc->irq_data = (rtc->irq_data + (num << 8)) | events;  //设置中断数据  
  6.     spin_unlock_irqrestore(&rtc->irq_lock, flags);  
  7.     spin_lock_irqsave(&rtc->irq_task_lock, flags);  
  8.     if (rtc->irq_task)                                       //存在中断任务  
  9.         rtc->irq_task->func(rtc->irq_task->private_data);   //调用中断任务回调函数  
  10.     spin_unlock_irqrestore(&rtc->irq_task_lock, flags);  
  11.     wake_up_interruptible(&rtc->irq_queue);  //唤醒等待中断的队列  
  12.     kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);  //轮询机制  
  13. }  
  14. EXPORT_SYMBOL_GPL(rtc_update_irq);  

5.轮询poll方法

[cpp] view plain copy
  1. static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)  
  2. {  
  3.     struct rtc_device *rtc = file->private_data; //获取RTC设备  
  4.     unsigned long data;  
  5.     poll_wait(file, &rtc->irq_queue, wait);  //poll等待  
  6.     data = rtc->irq_data;    //获取中断数据  
  7.     return (data != 0) ? (POLLIN | POLLRDNORM) : 0;  
  8. }  

四.sysfs和procfs接口

1.sysfs接口
在rtc_init函数中,创建了设备类那么/sys/class/rtc节点存在
接着rtc_sysfs_init初始化设备类属性

[cpp] view plain copy
  1. void __init rtc_sysfs_init(struct class *rtc_class)  
  2. {  
  3.     rtc_class->dev_attrs = rtc_attrs;    //设置rtc设备类属性  
  4. }  

属性文件,cat对应的属性文件可以显示对应的数据信息

[cpp] view plain copy
  1. static struct device_attribute rtc_attrs[] = {  
  2.     __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),   //1.1.名字  
  3.     __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),   //1.2.日期  
  4.     __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),   //1.3.时间  
  5.     __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), //1.4.“Gregorian”时间 秒数  
  6.     __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,rtc_sysfs_set_max_user_freq), //1.5.最大频率  
  7.     __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), //时间同步  
  8.     { },  
  9. };  

1.1.名字

[cpp] view plain copy
  1. static ssize_trtc_sysfs_show_name(struct device *dev, struct device_attribute *attr,char *buf)  
  2. {  
  3.     return sprintf(buf, "%s\n", to_rtc_device(dev)->name);   //根据设备文件获取rtc设备,并打印其名字  
  4. }  

1.2.日期

[cpp] view plain copy
  1. static ssize_t rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr,char *buf)  
  2. {  
  3.     ssize_t retval;  
  4.     struct rtc_time tm;  
  5.     retval = rtc_read_time(to_rtc_device(dev), &tm);    //根据设备文件获取rtc设备,读取时间  
  6.     if (retval == 0) {  
  7.         retval = sprintf(buf, "%04d-%02d-%02d\n",tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); //打印  
  8.     }  
  9.     return retval;  
  10. }  

1.3.时间

[cpp] view plain copy
  1. static ssize_t rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr,char *buf)  
  2. {  
  3.     ssize_t retval;  
  4.     struct rtc_time tm;  
  5.     retval = rtc_read_time(to_rtc_device(dev), &tm);    //根据设备文件获取rtc设备,读取时间  
  6.     if (retval == 0) {  
  7.         retval = sprintf(buf, "%02d:%02d:%02d\n",tm.tm_hour, tm.tm_min, tm.tm_sec); //打印  
  8.     }  
  9.     return retval;  
  10. }  

1.4.“Gregorian”时间 秒数

[cpp] view plain copy
  1. static ssize_t rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr,char *buf)  
  2. {  
  3.     ssize_t retval;  
  4.     struct rtc_time tm;  
  5.     retval = rtc_read_time(to_rtc_device(dev), &tm);    //根据设备文件获取rtc设备,读取时间  
  6.     if (retval == 0) {  
  7.         unsigned long time;  
  8.         rtc_tm_to_time(&tm, &time);     //时间转“Gregorian”时间  
  9.         retval = sprintf(buf, "%lu\n", time);   //打印秒数  
  10.     }  
  11.     return retval;  
  12. }  

1.5 设置和获取最大用户频率

[cpp] view plain copy
  1. static ssize_t rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr,char *buf)  
  2. {  
  3.     return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);  //根据设备文件获取rtc设备,读取最大用户频率  
  4. }  
  5. static ssize_t rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr,const char *buf, size_t n)  
  6. {  
  7.     struct rtc_device *rtc = to_rtc_device(dev);    //根据设备文件获取rtc设备  
  8.     unsigned long val = simple_strtoul(buf, NULL, 0);   //截取要设置的最大频率值  
  9.     if (val >= 4096 || val == 0)  
  10.         return -EINVAL;  
  11.     rtc->max_user_freq = (int)val;   //设置用户最大使用频率  
  12.     return n;  
  13. }  

1.6 在rtc设备注册的时候调用了rtc_sysfs_add_device函数

[cpp] view plain copy
  1. void rtc_sysfs_add_device(struct rtc_device *rtc)  
  2. {  
  3.     int err;  
  4.     /* not all RTCs support both alarms and wakeup */  
  5.     if (!rtc_does_wakealarm(rtc))   //判断rtc是否支持唤醒  
  6.         return;  
  7.     err = device_create_file(&rtc->dev, &dev_attr_wakealarm);    //支持则创建属性文件  
  8.     if (err)  
  9.         dev_err(rtc->dev.parent,"failed to create alarm attribute, %d\n", err);  
  10. }  

2.procfs接口
procfs添加设备

[cpp] view plain copy
  1. void rtc_proc_add_device(struct rtc_device *rtc)  
  2. {  
  3.     if (rtc->id == 0)  
  4.         proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);   //生成"/proc/driver/rtc"  
  5. }  

捆绑了操作函数集rtc_proc_fops

[cpp] view plain copy
  1. static const struct file_operations rtc_proc_fops = {  
  2.     .open       = rtc_proc_open,  
  3.     .read       = seq_read,  
  4.     .llseek     = seq_lseek,  
  5.     .release    = rtc_proc_release,  
  6. };  

这里涉及到seq_file文件操作,下面简单的描述一下,不对seq_file做分析
在open方法中

[cpp] view plain copy
  1. static int rtc_proc_open(struct inode *inode, struct file *file)  
  2. {  
  3.     struct rtc_device *rtc = PDE(inode)->data;  
  4.     if (!try_module_get(THIS_MODULE))  
  5.         return -ENODEV;  
  6.     return single_open(file, rtc_proc_show, rtc);  
  7. }  

指定了显示函数rtc_proc_show

[cpp] view plain copy
  1. static int rtc_proc_show(struct seq_file *seq, void *offset)  
  2. {  
  3.     int err;  
  4.     struct rtc_device *rtc = seq->private;   //获取rtc设备  
  5.     const struct rtc_class_ops *ops = rtc->ops;  //获取rtc类操作函数集  
  6.     struct rtc_wkalrm alrm;  
  7.     struct rtc_time tm;  
  8.     err = rtc_read_time(rtc, &tm);  //读取时间  
  9.     if (err == 0) {  
  10.         seq_printf(seq,  
  11.             "rtc_time\t: %02d:%02d:%02d\n"      //打印时间  
  12.             "rtc_date\t: %04d-%02d-%02d\n",     //打印日期  
  13.             tm.tm_hour, tm.tm_min, tm.tm_sec,  
  14.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);  
  15.     }  
  16.     err = rtc_read_alarm(rtc, &alrm);   //读闹钟  
  17.     if (err == 0) {  
  18.         seq_printf(seq, "alrm_time\t: ");  
  19.         if ((unsigned int)alrm.time.tm_hour <= 24)  
  20.             seq_printf(seq, "%02d:", alrm.time.tm_hour);  
  21.         else  
  22.             seq_printf(seq, "**:");  
  23.         if ((unsigned int)alrm.time.tm_min <= 59)  
  24.             seq_printf(seq, "%02d:", alrm.time.tm_min);  
  25.         else  
  26.             seq_printf(seq, "**:");  
  27.         if ((unsigned int)alrm.time.tm_sec <= 59)  
  28.             seq_printf(seq, "%02d\n", alrm.time.tm_sec);  
  29.         else  
  30.             seq_printf(seq, "**\n");  
  31.         seq_printf(seq, "alrm_date\t: ");  
  32.         if ((unsigned int)alrm.time.tm_year <= 200)  
  33.             seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);  
  34.         else  
  35.             seq_printf(seq, "****-");  
  36.         if ((unsigned int)alrm.time.tm_mon <= 11)  
  37.             seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);  
  38.         else  
  39.             seq_printf(seq, "**-");  
  40.         if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)  
  41.             seq_printf(seq, "%02d\n", alrm.time.tm_mday);  
  42.         else  
  43.             seq_printf(seq, "**\n");  
  44.         seq_printf(seq, "alarm_IRQ\t: %s\n",alrm.enabled ? "yes" : "no");  
  45.         seq_printf(seq, "alrm_pending\t: %s\n",alrm.pending ? "yes" : "no");  
  46.     }  
  47.     seq_printf(seq, "24hr\t\t: yes\n"); //打印  
  48.     if (ops->proc)  
  49.         ops->proc(rtc->dev.parent, seq);  
  50.     return 0;  
  51. }  

cat一下显示
/proc/driver# cat rtc
rtc_time        : 01:25:57
rtc_date        : 2013-11-02
24hr            : yes

五.ds1307 rtc芯片设备驱动
ds1307由i2c总线控制在其初始化函数中注册了i2c设备驱动

[cpp] view plain copy
  1. static int __init ds1307_init(void)  
  2. {  
  3.     return i2c_add_driver(&ds1307_driver);  
  4. }  
  5. module_init(ds1307_init);  

i2c设备驱动结构体

[cpp] view plain copy
  1. static struct i2c_driver ds1307_driver = {  
  2.     .driver = {  
  3.         .name   = "rtc-ds1307",  
  4.         .owner  = THIS_MODULE,  
  5.     },  
  6.     .probe      = ds1307_probe,  
  7.     .remove     = __devexit_p(ds1307_remove),  
  8.     .id_table   = ds1307_id,  
  9. };  

当匹配到设备的时候调用probe方法既ds1307_probe
在其probe方法中调用了rtc_device_register注册rtc设备,并捆绑了ds13xx_rtc_ops操作函数集
ds1307->rtc = rtc_device_register(client->name, &client->dev,&ds13xx_rtc_ops, THIS_MODULE);
ds1307的rtc设备类操作函数集

[cpp] view plain copy
  1. static const struct rtc_class_ops ds13xx_rtc_ops = {  
  2.     .read_time  = ds1307_get_time,      //1 读取时间  
  3.     .set_time   = ds1307_set_time,               //2 设置时间  
  4.     .read_alarm = ds1337_read_alarm,    //3 读区闹钟  
  5.     .set_alarm  = ds1337_set_alarm, //4 设置闹钟  
  6.     .ioctl      = ds1307_ioctl,     //5.rtc独有控制  
  7. };  

1 读取时间ds1307_get_time

[cpp] view plain copy
  1. static int ds1307_get_time(struct device *dev, struct rtc_time *t)  
  2. {  
  3.     struct ds1307   *ds1307 = dev_get_drvdata(dev);  
  4.     int     tmp;  
  5.     /* read the RTC date and time registers all at once */  
  6.     tmp = ds1307->read_block_data(ds1307->client,ds1307->offset, 7, ds1307->regs);  //通过i2c总线读取ds1307的寄存器  
  7.     if (tmp != 7) {  
  8.         dev_err(dev, "%s error %d\n""read", tmp);  
  9.         return -EIO;  
  10.     }  
  11.     t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);    //读取秒寄存器    设置rtc时间-秒  
  12.     t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); //读取分寄存器    设置rtc时间-分  
  13.     tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;                  //读取时寄存器  
  14.     t->tm_hour = bcd2bin(tmp);                                   //设置rtc时间-时  
  15.     t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;   //读取星期寄存器   设置rtc时间-星期  
  16.     t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);       //读取日寄存器    设置rtc时间-日  
  17.     tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;             //读取月寄存器  
  18.     t->tm_mon = bcd2bin(tmp) - 1;                                //设置rtc时间-月  
  19.     /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */   //ds1307从20YY年计算起的不是19YY年,所以加一个世纪(好霸气!)  
  20.     t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;    //读取年寄存器    设置rtc时间-年  
  21.     return rtc_valid_tm(t); //检验时间合理性  
  22. }  

2 设置时间ds1307_set_time

[cpp] view plain copy
  1. static int ds1307_set_time(struct device *dev, struct rtc_time *t)  
  2. {  
  3.     struct ds1307   *ds1307 = dev_get_drvdata(dev);  
  4.     int     result;  
  5.     int     tmp;  
  6.     u8      *buf = ds1307->regs;  
  7.     buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);       //设置秒  
  8.     buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);        //设置分  
  9.     buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);      //设置时  
  10.     buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);  //设置星期  
  11.     buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);      //设置日  
  12.     buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);  //设置月  
  13.     /* assume 20YY not 19YY */  
  14.     tmp = t->tm_year - 100;  //同读方法一样,减去一个世纪   
  15.     buf[DS1307_REG_YEAR] = bin2bcd(tmp);            //设置年  
  16.     switch (ds1307->type) {  
  17.     case ds_1337:  
  18.     case ds_1339:  
  19.     case ds_3231:  
  20.         buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;  
  21.         break;  
  22.     case ds_1340:  
  23.         buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN| DS1340_BIT_CENTURY;  
  24.         break;  
  25.     default:  
  26.         break;  
  27.     }  
  28.     result = ds1307->write_block_data(ds1307->client,ds1307->offset, 7, buf);  //i2c总线写回ds1307寄存器  
  29.     if (result < 0) {  
  30.         dev_err(dev, "%s error %d\n""write", result);  
  31.         return result;  
  32.     }  
  33.     return 0;  
  34. }  

3 读区闹钟ds1337_read_alarm

[cpp] view plain copy
  1. static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)  
  2. {  
  3.     struct i2c_client       *client = to_i2c_client(dev);   //获取i2c客户端  
  4.     struct ds1307       *ds1307 = i2c_get_clientdata(client);  
  5.     int         ret;  
  6.     if (!test_bit(HAS_ALARM, &ds1307->flags))  
  7.         return -EINVAL;  
  8.     ret = ds1307->read_block_data(client,DS1339_REG_ALARM1_SECS, 9, ds1307->regs);    //i2c获取ds1307寄存器  
  9.     if (ret != 9) {  
  10.         dev_err(dev, "%s error %d\n""alarm read", ret);  
  11.         return -EIO;  
  12.     }  
  13.     t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); //秒  
  14.     t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f); //分  
  15.     t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f);    //小时  
  16.     t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f);    //日  
  17.     t->time.tm_mon = -1; //月份没得设 结合“前面 3.1.3 读取闹钟时间”看  
  18.     t->time.tm_year = -1;    //年份没得设  
  19.     t->time.tm_wday = -1;    //没得设  
  20.     t->time.tm_yday = -1;    //没得设  
  21.     t->time.tm_isdst = -1;   //没得设  
  22.     t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE);   //使能闹钟?  
  23.     t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I);    //使能挂起?  
  24.     return 0;  
  25. }  

4 设置闹钟

[cpp] view plain copy
  1. static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)  
  2. {  
  3.     struct i2c_client       *client = to_i2c_client(dev);   //获取i2c客户端  
  4.     struct ds1307       *ds1307 = i2c_get_clientdata(client);  
  5.     unsigned char       *buf = ds1307->regs;  
  6.     u8          control, status;  
  7.     int         ret;  
  8.     if (!test_bit(HAS_ALARM, &ds1307->flags))    //测试忙标志  
  9.         return -EINVAL;  
  10.     ret = ds1307->read_block_data(client,DS1339_REG_ALARM1_SECS, 9, buf);    //通过i2c总线读芯片的状态  
  11.     if (ret != 9) {  
  12.         dev_err(dev, "%s error %d\n""alarm write", ret);  
  13.         return -EIO;  
  14.     }  
  15.     control = ds1307->regs[7];  
  16.     status = ds1307->regs[8];    //保存状态标志  
  17.     buf[0] = bin2bcd(t->time.tm_sec);    //设置秒  
  18.     buf[1] = bin2bcd(t->time.tm_min);    //设置分  
  19.     buf[2] = bin2bcd(t->time.tm_hour);   //设置时  
  20.     buf[3] = bin2bcd(t->time.tm_mday);   //设置日  
  21.     buf[4] = 0;  
  22.     buf[5] = 0;  
  23.     buf[6] = 0;  
  24.     buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);  
  25.     if (t->enabled) {    //设置闹钟使能  
  26.         dev_dbg(dev, "alarm IRQ armed\n");  
  27.         buf[7] |= DS1337_BIT_A1IE;  /* only ALARM1 is used */  
  28.     }  
  29.     buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);  
  30.     ret = ds1307->write_block_data(client,DS1339_REG_ALARM1_SECS, 9, buf);   //通过i2c总线写入芯片  
  31.     if (ret < 0) {  
  32.         dev_err(dev, "can't set alarm time\n");  
  33.         return ret;  
  34.     }  
  35.   
  36.     return 0;  
  37. }  

5.rtc独有控制
ds1307支持闹钟中断的使能和禁用功能

[cpp] view plain copy
  1. static int ds1307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)  
  2. {  
  3.     struct i2c_client   *client = to_i2c_client(dev);  
  4.     struct ds1307       *ds1307 = i2c_get_clientdata(client);  
  5.     int         ret;  
  6.   
  7.     switch (cmd) {  
  8.     case RTC_AIE_OFF:  
  9.         if (!test_bit(HAS_ALARM, &ds1307->flags))  
  10.             return -ENOTTY;  
  11.         ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //通过i2c总线读取数据  
  12.         if (ret < 0) //判断能否控制  
  13.             return ret;  
  14.         ret &= ~DS1337_BIT_A1IE;  
  15.         ret = i2c_smbus_write_byte_data(client,DS1337_REG_CONTROL, ret);    //通过i2c总线写入数据  
  16.         if (ret < 0)  
  17.             return ret;  
  18.         break;  
  19.     case RTC_AIE_ON:  
  20.         if (!test_bit(HAS_ALARM, &ds1307->flags))  
  21.             return -ENOTTY;  
  22.         ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //通过i2c总线读取数据  
  23.         if (ret < 0) //判断能否控制  
  24.             return ret;  
  25.         ret |= DS1337_BIT_A1IE;  
  26.         ret = i2c_smbus_write_byte_data(client,DS1337_REG_CONTROL, ret);    //通过i2c总线写入数据  
  27.         if (ret < 0)  
  28.             return ret;  
  29.         break;  
  30.     default:  
  31.         return -ENOIOCTLCMD;  
  32.     }  
  33.     return 0;  
  34. }  

关于中断,在ds1307 i2c设备驱动的probe方法中
err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,ds1307->rtc->name, client);
申请了中断,中断处理例程是ds1307_irq

[cpp] view plain copy
  1. static irqreturn_t ds1307_irq(int irq, void *dev_id)  
  2. {  
  3.     struct i2c_client   *client = dev_id;  
  4.     struct ds1307       *ds1307 = i2c_get_clientdata(client);  
  5.     disable_irq_nosync(irq);  
  6.     schedule_work(&ds1307->work);    //调用ds1307->work  
  7.     return IRQ_HANDLED;  
  8. }  

在probe方法中也设置了
INIT_WORK(&ds1307->work, ds1307_work);
所以中断例程回去执行ds1307_work

[cpp] view plain copy
  1. static void ds1307_work(struct work_struct *work)  
  2. {  
  3.     struct ds1307       *ds1307;  
  4.     struct i2c_client   *client;  
  5.     struct mutex        *lock;  
  6.     int         stat, control;  
  7.     ds1307 = container_of(work, struct ds1307, work);  
  8.     client = ds1307->client;  
  9.     lock = &ds1307->rtc->ops_lock;  
  10.     mutex_lock(lock);  
  11.     stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); //i2c读取ds1307状态  
  12.     if (stat < 0)  
  13.         goto out;  
  14.     if (stat & DS1337_BIT_A1I) {  
  15.         stat &= ~DS1337_BIT_A1I;  
  16.         i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat);  
  17.         control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //i2c读取控制状态  
  18.         if (control < 0)   
  19.             goto out;  
  20.         control &= ~DS1337_BIT_A1IE;  
  21.         i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); //i2c写入控制命令  
  22.         //调用rtc_update_irq函数 设置了events为RTC_AF(闹钟中断)| RTC_IRQF  
  23.         rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);   //结合"三.rtc设备接口-4.read方法"理解  
  24.     }  
  25. out:  
  26.     if (test_bit(HAS_ALARM, &ds1307->flags))  
  27.         enable_irq(client->irq);  
  28.     mutex_unlock(lock);  
  29. }
0 0
原创粉丝点击