一.描述rtc相关结构体
1.rtc设备
struct rtc_device //rtc设备 { struct device dev; //设备文件 struct module *owner; //所有者 int id; //次设备号 char name[RTC_DEVICE_NAME_SIZE]; //rtc设备名 const struct rtc_class_ops *ops; //rtc类操作函数集 struct mutex ops_lock; struct cdev char_dev; //字符设备 unsigned long flags; //忙标志 (RTC_DEV_BUSY) 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; //默认64 };
1.1 同时也定义了一个宏,通过设备文件查找rtc设备
#define to_rtc_device(d) container_of(d, struct rtc_device, dev)
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 *); //读时间 int (*set_time)(struct device *, struct rtc_time *); //设置时间 int (*read_alarm)(struct device *, struct rtc_wkalrm *); //读闹钟 int (*set_alarm)(struct device *, struct rtc_wkalrm *); //设闹钟 int (*proc)(struct device *, struct seq_file *); //proc接口 int (*set_mmss)(struct device *, unsigned long secs); //设置时间mmss int (*irq_set_state)(struct device *, int enabled); //设置中断状态 int (*irq_set_freq)(struct device *, int freq); //设置中断频率 int (*read_callback)(struct device *, int data); //读回调函数 int (*alarm_irq_enable)(struct device *, unsigned int enabled); //闹钟中断使能 int (*update_irq_enable)(struct device *, unsigned int enabled); //更新中断使能 };
这里有两种设置时间的方法set_time和set_mmss,看它们参数可以区别出set_time使用rtc时间来设置,
set_mmss是根据秒数来设置(“Gregorian”时间)
二.rtc架构
1.rtc设备初始化函数
void __init rtc_dev_init(void) //入口函数 { int err; err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); //动态分配rtc设备号 RTC_DEV_MAX=16个 if (err < 0) printk(KERN_ERR "%s: failed to allocate char dev region\n",__FILE__); }
2.rtc设备的注册
rtc注册由具体设备驱动调用,同时设备驱动必须提供rtc_class_ops操作函数集
struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner) { struct rtc_device *rtc; //rtc设备 int id, err; if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { //idr机制预分配 err = -ENOMEM; goto exit; } mutex_lock(&idr_lock); err = idr_get_new(&rtc_idr, NULL, &id); //通过idr机制获取id号 mutex_unlock(&idr_lock); if (err < 0) goto exit; id = id & MAX_ID_MASK; //id掩码过滤 rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); //分配rtc设备 if (rtc == NULL) { err = -ENOMEM; goto exit_idr; } rtc->id = id; //次设备号 rtc->ops = ops; //rtc类操作函数集 rtc->owner = owner; //所有者 rtc->max_user_freq = 64; //最大用户频率 rtc->dev.parent = dev; //设备父设备 rtc->dev.class = rtc_class; //2.1 设备类 rtc->dev.release = rtc_device_release; //设备释放方法 mutex_init(&rtc->ops_lock); spin_lock_init(&rtc->irq_lock); spin_lock_init(&rtc->irq_task_lock); init_waitqueue_head(&rtc->irq_queue); strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); //设置rtc设备名 dev_set_name(&rtc->dev, "rtc%d", id); //设置设备名 rtc_dev_prepare(rtc); //2.2 rtc设备准备 err = device_register(&rtc->dev); //注册设备文件 if (err) { put_device(&rtc->dev); goto exit_kfree; } rtc_dev_add_device(rtc); //2.3 rtc添加设备 rtc_sysfs_add_device(rtc); //sysfs添加设备文件 rtc_proc_add_device(rtc); //procfs添加设备文件 dev_info(dev, "rtc core: registered %s as %s\n",rtc->name, dev_name(&rtc->dev)); return rtc; exit_kfree: kfree(rtc); exit_idr: mutex_lock(&idr_lock); idr_remove(&rtc_idr, id); mutex_unlock(&idr_lock); exit: dev_err(dev, "rtc core: unable to register %s, err = %d\n",name, err); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(rtc_device_register);
2.1 设备类rtc_class
subsys_initcall(rtc_init); //rtc子系统初始化
设备类初始化
static int __init rtc_init(void) { rtc_class = class_create(THIS_MODULE, "rtc"); //创建设备类“/sys/class/rtc” if (IS_ERR(rtc_class)) { printk(KERN_ERR "%s: couldn't create class\n", __FILE__); return PTR_ERR(rtc_class); } rtc_class->suspend = rtc_suspend; //挂起 rtc_class->resume = rtc_resume; //唤醒 rtc_dev_init(); //这里发现 rtc设备初始化函数是在这里调用 rtc_sysfs_init(rtc_class); //sysfs接口 return 0; }
rtc子系统初始化:rtc类初始化->rtc设备初始化->注册rtc设备
2.2 rtc设备准备
void rtc_dev_prepare(struct rtc_device *rtc) { if (!rtc_devt) //主设备号是否分配 return; if (rtc->id >= RTC_DEV_MAX) { //判断次设备号是否>16,最多支持16个RTC pr_debug("%s: too many RTC devices\n", rtc->name); return; } rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); //设置rtc设备号 cdev_init(&rtc->char_dev, &rtc_dev_fops); //初始化字符设备 捆绑了字符设备操作函数集 rtc->char_dev.owner = rtc->owner; //模块所有者 }
2.3 rtc添加设备
void rtc_dev_add_device(struct rtc_device *rtc) { if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) //添加字符设备 printk(KERN_WARNING "%s: failed to add char device %d:%d\n",rtc->name, MAJOR(rtc_devt), rtc->id); else pr_debug("%s: dev (%d:%d)\n", rtc->name,MAJOR(rtc_devt), rtc->id); }
字符设备相关的初始化总结:
"1"分配设备号,"2.2"初始化字符设备(捆绑操作函数集),“2.3”添加字符设备
“2.1”初始化设备类 "2"注册设备文件
rtc字符设备操作函数集rtc_dev_fops是系统提供的,每次操作/dev/rtcXXX就会调用其操作函数集的方法
三.rtc设备接口
1.rtc字符设备操作函数集
static const struct file_operations rtc_dev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = rtc_dev_read, //读方法 .poll = rtc_dev_poll, //轮询 .unlocked_ioctl = rtc_dev_ioctl, //控制 .open = rtc_dev_open, //打开 .release = rtc_dev_release, //释放 .fasync = rtc_dev_fasync, //异步通知 };
2.open方法
static int rtc_dev_open(struct inode *inode, struct file *file) { int err; struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev); //获取对应的rtc设备 const struct rtc_class_ops *ops = rtc->ops; //获取对应的rtc设备类操作函数集 if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) //测试是否忙,不忙则设置忙标志 return -EBUSY; file->private_data = rtc; //将rtc设备作为文件的私有数据 err = ops->open ? ops->open(rtc->dev.parent) : 0; //存在open方法则调用其open方法 if (err == 0) { spin_lock_irq(&rtc->irq_lock); rtc->irq_data = 0; //中断数据清0 spin_unlock_irq(&rtc->irq_lock); return 0; } clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); //清除忙标志 return err; }
涉及两个操作函数集
a.rtc设备类操作函数集 rtc_class_ops --- 具体设备驱动提供
b.rtc字符设备操作函数集 rtc_dev_fops --- rtc子系统提供
3.ioctl方法
3.1控制命令定义
#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on 使能RTC闹钟中断*/ #define RTC_AIE_OFF _IO('p', 0x02) /* ... off 禁用RTC闹钟中断*/ #define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on 使能更新RTC中断*/ #define RTC_UIE_OFF _IO('p', 0x04) /* ... off 禁能更新RTC中断*/ #define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on 使能RTC周期中断*/ #define RTC_PIE_OFF _IO('p', 0x06) /* ... off 禁能RTC周期中断*/ #define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time 设置闹钟时间*/ #define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time 读取闹钟时间*/ #define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time) /* Read RTC time 读取时间与日期*/ #define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time) /* Set RTC time 设置时间与日期*/ #define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate 读取中断频率*/ #define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate 设置中断频率*/ #define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm) /* Set wakeup alarm 设置唤醒闹钟*/ #define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm) /* Get wakeup alarm 获取唤醒闹钟*/
命令带的参数结构体:
3.1.1 rtc时间 rtc_time
struct rtc_time { //rtc时间结构体 int tm_sec; //秒 int tm_min; //分 int tm_hour; //时 int tm_mday; //日 int tm_mon; //月 int tm_year; //年数(xxx-1900) int tm_wday; //星期几 int tm_yday; //一年的第几天 int tm_isdst; //夏令时 };
3.1.2 rtc闹钟 rtc_wkalrm
struct rtc_wkalrm { //rtc闹钟结构体 unsigned char enabled; /* 0 = alarm disabled, 1 = alarm enabled 闹钟使能开关*/ unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending 闹钟挂起*/ struct rtc_time time; /* time the alarm is set to 闹钟时间*/ };
3.2 rtc设备控制
static long rtc_dev_ioctl(struct file *file,unsigned int cmd, unsigned long arg) { int err = 0; struct rtc_device *rtc = file->private_data; //获取rtc设备 const struct rtc_class_ops *ops = rtc->ops; //获取rtc操作函数集 struct rtc_time tm; //rtc时间 struct rtc_wkalrm alarm; //rtc闹钟 void __user *uarg = (void __user *) arg; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; //根据命令判断调用任务是否有权限 switch (cmd) { case RTC_EPOCH_SET: case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) err = -EACCES; break; case RTC_IRQP_SET: if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) err = -EACCES; break; case RTC_PIE_ON: if (rtc->irq_freq > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) err = -EACCES; break; } if (err) goto done; /* 先调用设备驱动的类操作函数集结构体*/ if (ops->ioctl) { //rtc操作函数集存在控制方法 err = ops->ioctl(rtc->dev.parent, cmd, arg); //调用其控制方法 if (err != -ENOIOCTLCMD) { mutex_unlock(&rtc->ops_lock); return err; } } //______________________________________________________________________________________ switch (cmd) { case RTC_ALM_READ: //读取闹钟时间 mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm); //3.1.3 读取闹钟时间 if (err < 0) return err; if (copy_to_user(uarg, &alarm.time, sizeof(tm))) //复制到用户空间 err = -EFAULT; return err; case RTC_ALM_SET: //设置闹钟 mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm.time, uarg, sizeof(tm))) //拷贝到内核空间 return -EFAULT; //初始化清除部分属性 alarm.enabled = 0; alarm.pending = 0; alarm.time.tm_wday = -1; alarm.time.tm_yday = -1; alarm.time.tm_isdst = -1; { unsigned long now, then; err = rtc_read_time(rtc, &tm); //读取时间 if (err < 0) return err; rtc_tm_to_time(&tm, &now); //当前时间 转换成格里高里历法时间 alarm.time.tm_mday = tm.tm_mday; //设置日期 alarm.time.tm_mon = tm.tm_mon; //设置月 alarm.time.tm_year = tm.tm_year; //设置年 err = rtc_valid_tm(&alarm.time); //校验时间合理性 if (err < 0) return err; rtc_tm_to_time(&alarm.time, &then); //闹钟时间 转换成“Gregorian”时间 if (then < now) { //现在的时间已经过了闹钟时间,那么就设置明天闹 rtc_time_to_tm(now + 24 * 60 * 60, &tm); //“Gregorian”时间时间+1天 转rtc_time alarm.time.tm_mday = tm.tm_mday; //设置日期 alarm.time.tm_mon = tm.tm_mon; //设置月 alarm.time.tm_year = tm.tm_year; //设置年 } } return rtc_set_alarm(rtc, &alarm); //3.1.4 设置闹钟 case RTC_RD_TIME: //获取时间 mutex_unlock(&rtc->ops_lock); err = rtc_read_time(rtc, &tm); //3.1.1 获取时间 if (err < 0) return err; if (copy_to_user(uarg, &tm, sizeof(tm))) //复制到用户空间 err = -EFAULT; return err; case RTC_SET_TIME: //设置时间 mutex_unlock(&rtc->ops_lock); if (copy_from_user(&tm, uarg, sizeof(tm))) //复制到用户空间 return -EFAULT; return rtc_set_time(rtc, &tm); //3.1.2 设置系统时间 case RTC_PIE_ON: //使能周期中断 err = rtc_irq_set_state(rtc, NULL, 1); //使能周期中断 break; case RTC_PIE_OFF: //禁用周期中断 err = rtc_irq_set_state(rtc, NULL, 0); //禁用周期中断 break; case RTC_AIE_ON: //使能闹钟中断 mutex_unlock(&rtc->ops_lock); return rtc_alarm_irq_enable(rtc, 1); //使能闹钟中断 case RTC_AIE_OFF: //禁用闹钟中断 mutex_unlock(&rtc->ops_lock); return rtc_alarm_irq_enable(rtc, 0); //禁用闹钟中断 case RTC_UIE_ON: //使能更新中断 mutex_unlock(&rtc->ops_lock); return rtc_update_irq_enable(rtc, 1); //使能更新中断 case RTC_UIE_OFF: //禁用更新中断 mutex_unlock(&rtc->ops_lock); return rtc_update_irq_enable(rtc, 0); //禁用更新中断 case RTC_IRQP_SET: //设置中断频率 err = rtc_irq_set_freq(rtc, NULL, arg); //设置中断频率 break; case RTC_IRQP_READ: //读取中断频率 err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); //读取中断频率 break; case RTC_WKALM_SET: //设置唤醒闹钟 mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm, uarg, sizeof(alarm))) //从用户空间复制 return -EFAULT; return rtc_set_alarm(rtc, &alarm); //设置闹钟 case RTC_WKALM_RD: //读取唤醒闹钟 mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm); //读取闹钟 if (err < 0) return err; if (copy_to_user(uarg, &alarm, sizeof(alarm))) //复制到用户空间 err = -EFAULT; return err; default: err = -ENOTTY; break; } done: mutex_unlock(&rtc->ops_lock); return err; }
3.1.0 时间相关函数解析
A.api函数
int rtc_month_days(unsigned int month, unsigned int year) //计算某年某月的天数 int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) //计算某年某月某日是该年的第几日 int rtc_valid_tm(struct rtc_time *tm) //检测rtc时间的合理性 void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) //“Gregorian”时间转换成rtc时间 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
static const unsigned char rtc_days_in_month[] = { //每个月的天数 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
B.2 从年头1月1日到各个月的月头经过的日子数
static const unsigned short rtc_ydays[2][13] = { /*常年 1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月,1月*/ { 0 ,31 ,59 ,90 ,120,151,181,212,243,273 ,304 ,334,365 }, /*闰年情况*/ { 0 ,31 ,60 ,91 ,121,152,182,213,244,274 ,305 ,335,366 } };
B.3 判断是否闰年
static inline bool is_leap_year(unsigned int year) //判断是否闰年 { return (!(year % 4) && (year % 100)) || !(year % 400); }
B.4 计算自公元0年经过了多少个闰年
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
C 函数解析
C.1 rtc_month_days
int rtc_month_days(unsigned int month, unsigned int year) { //month月的天数 + 是否闰年?&&是否二月份? 都是旧加多1天 return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); } EXPORT_SYMBOL(rtc_month_days);
C.2 rtc_year_days
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) //日期计算 是一年中的第几日 { //日子数[year是否闰年?][第month月]+日-1 return rtc_ydays[is_leap_year(year)][month] + day-1; } EXPORT_SYMBOL(rtc_year_days);
C.3 rtc_valid_tm
int rtc_valid_tm(struct rtc_time *tm) //检测rtc时间的合理性 { if (tm->tm_year < 70 //年分小于1970年 || ((unsigned)tm->tm_mon) >= 12 //月份大于12月 || tm->tm_mday < 1 //日期小于1 || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) //日期大于当月最大日期 || ((unsigned)tm->tm_hour) >= 24 //时大于24 || ((unsigned)tm->tm_min) >= 60 //分大于60 || ((unsigned)tm->tm_sec) >= 60) //秒大于60 return -EINVAL; return 0; } EXPORT_SYMBOL(rtc_valid_tm);
C.4 rtc_time_to_tm
void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) //“Gregorian”时间转换rtc时间 { unsigned int month, year; int days; days = time / 86400; //总秒数/(24*60*60[1天的秒数])等于过去的日子数 time -= (unsigned int) days * 86400; //剩余不足一天的秒数 tm->tm_wday = (days + 4) % 7; //计算星期几(1970-01-01刚好是星期3所以+4) year = 1970 + days / 365; //计算现在是哪一年=1970+过去的日字数/365 days -= (year - 1970) * 365 + LEAPS_THRU_END_OF(year - 1) - LEAPS_THRU_END_OF(1970 - 1); //计算剩下不足一年的日子数,并调整闰年 if (days < 0) { //调整 year -= 1; days += 365 + is_leap_year(year); } tm->tm_year = year - 1900; //rtc时间是1900年到现在的年数 tm->tm_yday = days + 1; //一年中的第几天 for (month = 0; month < 11; month++) { //计算是几月几日 int newdays; newdays = days - rtc_month_days(month, year); //减每个月的天数 if (newdays < 0) break; days = newdays; } tm->tm_mon = month; //月份 tm->tm_mday = days + 1; //日期 tm->tm_hour = time / 3600; //小时 3600=60s*60m time -= tm->tm_hour * 3600; //剩下不足1小时的秒数 tm->tm_min = time / 60; //分钟 tm->tm_sec = time - tm->tm_min * 60; //剩下不足1分钟的秒数 } EXPORT_SYMBOL(rtc_time_to_tm);
C.5 rtc_tm_to_time
int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) //rtc时间转换成“Gregorian”时间 { *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec); return 0; } EXPORT_SYMBOL(rtc_tm_to_time);
调用mktime函数 加上1900得到真正的年数,(月份+1)调整到正常的年数tm_mon是[0~11]调整成[1~12]
unsigned long mktime(const unsigned int year0, const unsigned int mon0,const unsigned int day, const unsigned int hour,const unsigned int min, const unsigned int sec) { unsigned int mon = mon0, year = year0; /* 1..12 -> 11,12,1..10 */ if (0 >= (int) (mon -= 2)) { //判断是否过了2月份,同时mon减去2 mon += 12; //月份+12 year -= 1; //年份也调少1年 } //上面调整了月份排序,也等价于,忽略1,2月份过去的天数,新调整的月份排序参考D.4 return ((((unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) + // year*365 - 719499 // =总天数 )*24 + hour /* now have hours */ //日子数*24 + 剩余的小时数 =总小时数 )*60 + min /* now have minutes */ //总小时数*60 + 剩余分数 =总分钟数 )*60 + sec; /* finally seconds */ //总分钟数*60 + 剩余秒数 =总秒数 } 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
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) //读取时间 { int err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; if (!rtc->ops) //rtc操作函数集存在 err = -ENODEV; else if (!rtc->ops->read_time) //不存在读时间方法 err = -EINVAL; else { memset(tm, 0, sizeof(struct rtc_time)); //初始化rtc_time结构体 err = rtc->ops->read_time(rtc->dev.parent, tm); //调用读时间方法 } mutex_unlock(&rtc->ops_lock); return err; } EXPORT_SYMBOL_GPL(rtc_read_time);
3.1.2 设置系统时间 rtc_set_time
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) //设置时间 { int err; err = rtc_valid_tm(tm); //校验时间的合理性 if (err != 0) return err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; if (!rtc->ops) //存在rtc操作函数集 err = -ENODEV; else if (rtc->ops->set_time) //存在设置时间方法 err = rtc->ops->set_time(rtc->dev.parent, tm); //则调用设置时间方法 else if (rtc->ops->set_mmss) { //没有则判断是否存在设置时间mmss方法 unsigned long secs; err = rtc_tm_to_time(tm, &secs); //转换成“Gregorian”时间 if (err == 0) err = rtc->ops->set_mmss(rtc->dev.parent, secs); //则调用设置时间mmss方法 } else err = -EINVAL; mutex_unlock(&rtc->ops_lock); return err; } EXPORT_SYMBOL_GPL(rtc_set_time);
3.1.3 读取闹钟时间
int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) //读取闹钟 { int err; struct rtc_time before, now; int first_time = 1; unsigned long t_now, t_alm; enum { none, day, month, year } missing = none; //枚举可以这样写哦! unsigned days; err = rtc_read_time(rtc, &before); //读取rtc时间before if (err < 0) return err; do { if (!first_time) //重读的话,就更新before时间 memcpy(&before, &now, sizeof(struct rtc_time)); first_time = 0; err = rtc_read_alarm_internal(rtc, alarm); //3.1.3.1 读取rtc闹钟 if (err) return err; if (!alarm->enabled) return 0; if (rtc_valid_tm(&alarm->time) == 0) //检测rtc时间合理性 return 0; err = rtc_read_time(rtc, &now); //再次读取rtc时间now if (err < 0) return err; //比较分,时,月,年没发生变化(这里忽略秒级),变化了就重读 } 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); //闹钟读取失败其时间域将会置为-1 if (alarm->time.tm_sec == -1) alarm->time.tm_sec = now.tm_sec; //设置秒 if (alarm->time.tm_min == -1) alarm->time.tm_min = now.tm_min; //设置分 if (alarm->time.tm_hour == -1) alarm->time.tm_hour = now.tm_hour; //设置时 /* 结合后面ds1307 “3 读区闹钟ds1337_read_alarm” */ if (alarm->time.tm_mday == -1) { alarm->time.tm_mday = now.tm_mday; //设置日 missing = day; //没设置日期 (05:00:00) } if (alarm->time.tm_mon == -1) { alarm->time.tm_mon = now.tm_mon; //设置月 if (missing == none) missing = month; //没设置月份 (xx-xx-31 05:00:00) } if (alarm->time.tm_year == -1) { alarm->time.tm_year = now.tm_year; //设置年 if (missing == none) missing = year; //没设置年份 (xx-2-29 05:00:00) } rtc_tm_to_time(&now, &t_now); //时间转“Gregorian”时间 rtc_tm_to_time(&alarm->time, &t_alm); //时间转“Gregorian”时间 if (t_now < t_alm) //闹钟时间比现在时间晚,闹钟今天还会闹 goto done; switch (missing) { case day: //明天闹 (现在是星期一10点,闹钟是5点,那么就得设置成星期二5点) dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); t_alm += 24 * 60 * 60; //加多1天 rtc_time_to_tm(t_alm, &alarm->time); //转成rtc时间 break; case month: //下个月明天闹 (如果星期一是31号了,那么星期二5点闹 还得调整下月份) dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); do { if (alarm->time.tm_mon < 11) //小于12月份 alarm->time.tm_mon++; //月份+1 else { //(如果是12月31日了,那么还得调整年份) alarm->time.tm_mon = 0; //变成1月份 alarm->time.tm_year++; //年份+1 } days = rtc_month_days(alarm->time.tm_mon,alarm->time.tm_year); //闹钟日期计算出该月的天数 } while (days < alarm->time.tm_mday); //天数不对再调整(例如设置31号闹,下个月不一定有31号) break; case year: //n年后闹(闰年2月29号闹的) dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); do { alarm->time.tm_year++; //年份+1 } while (rtc_valid_tm(&alarm->time) != 0); break; default: dev_warn(&rtc->dev, "alarm rollover not handled\n"); } done: return 0; } EXPORT_SYMBOL_GPL(rtc_read_alarm);
3.1.3.1 读取rtc闹钟
static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm) //读取内部时钟 { int err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; if (rtc->ops == NULL) //存在rtc操作函数集 err = -ENODEV; else if (!rtc->ops->read_alarm) //存在读取闹钟方法 err = -EINVAL; else { memset(alarm, 0, sizeof(struct rtc_wkalrm)); //初始化rtc_wkalrm结构体对象 err = rtc->ops->read_alarm(rtc->dev.parent, alarm); //调用读取闹钟方法 } mutex_unlock(&rtc->ops_lock); return err; }
3.1.4 设置闹钟
int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) //设置闹钟 { int err; err = rtc_valid_tm(&alarm->time); //检验时间合理性 if (err != 0) return err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; if (!rtc->ops) //存在rtc操作函数集 err = -ENODEV; else if (!rtc->ops->set_alarm) //不存在设置闹钟方法 err = -EINVAL; else err = rtc->ops->set_alarm(rtc->dev.parent, alarm); //调用设置闹钟方法 mutex_unlock(&rtc->ops_lock); return err; } 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类操作函数去完成
RTC_PIE_ON rtc_irq_set_state(rtc, NULL, 1) RTC_PIE_OFF rtc_irq_set_state(rtc, NULL, 0) rtc->ops->irq_set_state(rtc->dev.parent, enabled) RTC_AIE_ON rtc_alarm_irq_enable(rtc, 1) RTC_AIE_OFF rtc_alarm_irq_enable(rtc, 0) rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled) RTC_UIE_ON rtc_update_irq_enable(rtc, 1) RTC_UIE_OFF rtc_update_irq_enable(rtc, 0) rtc->ops->update_irq_enable(rtc->dev.parent, enabled)
中断频率设置读取函数 都是调用设备驱动提供的对应的rtc类操作函数去完成
RTC_IRQP_SET rtc_irq_set_freq(rtc, NULL, arg) rtc->ops->irq_set_freq(rtc->dev.parent, freq) RTC_IRQP_READ put_user(rtc->irq_freq, (unsigned long __user *)uarg)
4.read方法
static ssize_t rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct rtc_device *rtc = file->private_data; //获取rtc设备 DECLARE_WAITQUEUE(wait, current); //声明等待队列 unsigned long data; ssize_t ret; if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) //判断数据长度 return -EINVAL; add_wait_queue(&rtc->irq_queue, &wait); //添加进等待队列 do { __set_current_state(TASK_INTERRUPTIBLE); //设置任务可中断态 spin_lock_irq(&rtc->irq_lock); data = rtc->irq_data; //获取中断数据 rtc->irq_data = 0; //清除中断数据 spin_unlock_irq(&rtc->irq_lock); if (data != 0) { //数据不为0,传输正常退出while循环 ret = 0; break; } if (file->f_flags & O_NONBLOCK) { //设置文件标志 ret = -EAGAIN; break; } if (signal_pending(current)) { //挂起 ret = -ERESTARTSYS; break; } schedule(); } while (1); set_current_state(TASK_RUNNING); //设置任务运行态 remove_wait_queue(&rtc->irq_queue, &wait); //移出等待队列 if (ret == 0) { /* Check for any data updates */ if (rtc->ops->read_callback) //存在读数据回调函数 data = rtc->ops->read_callback(rtc->dev.parent,data); //调用读数据回调函数 if (sizeof(int) != sizeof(long) && count == sizeof(unsigned int)) ret = put_user(data, (unsigned int __user *)buf) ?:sizeof(unsigned int); //上传数据到用户空间 else ret = put_user(data, (unsigned long __user *)buf) ?:sizeof(unsigned long); //上传数据到用户空间 } return ret; }
读方法主要获取中断数据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函数调用,并传递参数进来
void rtc_update_irq(struct rtc_device *rtc,unsigned long num, unsigned long events) { unsigned long flags; spin_lock_irqsave(&rtc->irq_lock, flags); rtc->irq_data = (rtc->irq_data + (num << 8)) | events; //设置中断数据 spin_unlock_irqrestore(&rtc->irq_lock, flags); spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task) //存在中断任务 rtc->irq_task->func(rtc->irq_task->private_data); //调用中断任务回调函数 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); wake_up_interruptible(&rtc->irq_queue); //唤醒等待中断的队列 kill_fasync(&rtc->async_queue, SIGIO, POLL_IN); //轮询机制 } EXPORT_SYMBOL_GPL(rtc_update_irq);
5.轮询poll方法
static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) { struct rtc_device *rtc = file->private_data; //获取RTC设备 unsigned long data; poll_wait(file, &rtc->irq_queue, wait); //poll等待 data = rtc->irq_data; //获取中断数据 return (data != 0) ? (POLLIN | POLLRDNORM) : 0; }
四.sysfs和procfs接口
1.sysfs接口
在rtc_init函数中,创建了设备类那么/sys/class/rtc节点存在
接着rtc_sysfs_init初始化设备类属性
void __init rtc_sysfs_init(struct class *rtc_class) { rtc_class->dev_attrs = rtc_attrs; //设置rtc设备类属性 }
属性文件,cat对应的属性文件可以显示对应的数据信息
static struct device_attribute rtc_attrs[] = { __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), //1.1.名字 __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), //1.2.日期 __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL), //1.3.时间 __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), //1.4.“Gregorian”时间 秒数 __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,rtc_sysfs_set_max_user_freq), //1.5.最大频率 __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), //时间同步 { }, };
1.1.名字
static ssize_trtc_sysfs_show_name(struct device *dev, struct device_attribute *attr,char *buf) { return sprintf(buf, "%s\n", to_rtc_device(dev)->name); //根据设备文件获取rtc设备,并打印其名字 }
1.2.日期
static ssize_t rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr,char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); //根据设备文件获取rtc设备,读取时间 if (retval == 0) { retval = sprintf(buf, "%04d-%02d-%02d\n",tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); //打印 } return retval; }
1.3.时间
static ssize_t rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr,char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); //根据设备文件获取rtc设备,读取时间 if (retval == 0) { retval = sprintf(buf, "%02d:%02d:%02d\n",tm.tm_hour, tm.tm_min, tm.tm_sec); //打印 } return retval; }
1.4.“Gregorian”时间 秒数
static ssize_t rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr,char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); //根据设备文件获取rtc设备,读取时间 if (retval == 0) { unsigned long time; rtc_tm_to_time(&tm, &time); //时间转“Gregorian”时间 retval = sprintf(buf, "%lu\n", time); //打印秒数 } return retval; }
1.5 设置和获取最大用户频率
static ssize_t rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr,char *buf) { return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); //根据设备文件获取rtc设备,读取最大用户频率 } static ssize_t rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr,const char *buf, size_t n) { struct rtc_device *rtc = to_rtc_device(dev); //根据设备文件获取rtc设备 unsigned long val = simple_strtoul(buf, NULL, 0); //截取要设置的最大频率值 if (val >= 4096 || val == 0) return -EINVAL; rtc->max_user_freq = (int)val; //设置用户最大使用频率 return n; }
1.6 在rtc设备注册的时候调用了rtc_sysfs_add_device函数
void rtc_sysfs_add_device(struct rtc_device *rtc) { int err; /* not all RTCs support both alarms and wakeup */ if (!rtc_does_wakealarm(rtc)) //判断rtc是否支持唤醒 return; err = device_create_file(&rtc->dev, &dev_attr_wakealarm); //支持则创建属性文件 if (err) dev_err(rtc->dev.parent,"failed to create alarm attribute, %d\n", err); }
2.procfs接口
procfs添加设备
void rtc_proc_add_device(struct rtc_device *rtc) { if (rtc->id == 0) proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); //生成"/proc/driver/rtc" }
捆绑了操作函数集rtc_proc_fops
static const struct file_operations rtc_proc_fops = { .open = rtc_proc_open, .read = seq_read, .llseek = seq_lseek, .release = rtc_proc_release, };
这里涉及到seq_file文件操作,下面简单的描述一下,不对seq_file做分析
在open方法中
static int rtc_proc_open(struct inode *inode, struct file *file) { struct rtc_device *rtc = PDE(inode)->data; if (!try_module_get(THIS_MODULE)) return -ENODEV; return single_open(file, rtc_proc_show, rtc); }
指定了显示函数rtc_proc_show
static int rtc_proc_show(struct seq_file *seq, void *offset) { int err; struct rtc_device *rtc = seq->private; //获取rtc设备 const struct rtc_class_ops *ops = rtc->ops; //获取rtc类操作函数集 struct rtc_wkalrm alrm; struct rtc_time tm; err = rtc_read_time(rtc, &tm); //读取时间 if (err == 0) { seq_printf(seq, "rtc_time\t: %02d:%02d:%02d\n" //打印时间 "rtc_date\t: %04d-%02d-%02d\n", //打印日期 tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); } err = rtc_read_alarm(rtc, &alrm); //读闹钟 if (err == 0) { seq_printf(seq, "alrm_time\t: "); if ((unsigned int)alrm.time.tm_hour <= 24) seq_printf(seq, "%02d:", alrm.time.tm_hour); else seq_printf(seq, "**:"); if ((unsigned int)alrm.time.tm_min <= 59) seq_printf(seq, "%02d:", alrm.time.tm_min); else seq_printf(seq, "**:"); if ((unsigned int)alrm.time.tm_sec <= 59) seq_printf(seq, "%02d\n", alrm.time.tm_sec); else seq_printf(seq, "**\n"); seq_printf(seq, "alrm_date\t: "); if ((unsigned int)alrm.time.tm_year <= 200) seq_printf(seq, "%04d-", alrm.time.tm_year + 1900); else seq_printf(seq, "****-"); if ((unsigned int)alrm.time.tm_mon <= 11) seq_printf(seq, "%02d-", alrm.time.tm_mon + 1); else seq_printf(seq, "**-"); if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31) seq_printf(seq, "%02d\n", alrm.time.tm_mday); else seq_printf(seq, "**\n"); seq_printf(seq, "alarm_IRQ\t: %s\n",alrm.enabled ? "yes" : "no"); seq_printf(seq, "alrm_pending\t: %s\n",alrm.pending ? "yes" : "no"); } seq_printf(seq, "24hr\t\t: yes\n"); //打印 if (ops->proc) ops->proc(rtc->dev.parent, seq); return 0; }
cat一下显示
/proc/driver# cat rtc
rtc_time : 01:25:57
rtc_date : 2013-11-02
24hr : yes
五.ds1307 rtc芯片设备驱动
ds1307由i2c总线控制在其初始化函数中注册了i2c设备驱动
static int __init ds1307_init(void) { return i2c_add_driver(&ds1307_driver); } module_init(ds1307_init);
i2c设备驱动结构体
static struct i2c_driver ds1307_driver = { .driver = { .name = "rtc-ds1307", .owner = THIS_MODULE, }, .probe = ds1307_probe, .remove = __devexit_p(ds1307_remove), .id_table = ds1307_id, };
当匹配到设备的时候调用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设备类操作函数集
static const struct rtc_class_ops ds13xx_rtc_ops = { .read_time = ds1307_get_time, //1 读取时间 .set_time = ds1307_set_time, //2 设置时间 .read_alarm = ds1337_read_alarm, //3 读区闹钟 .set_alarm = ds1337_set_alarm, //4 设置闹钟 .ioctl = ds1307_ioctl, //5.rtc独有控制 };
1 读取时间ds1307_get_time
static int ds1307_get_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp; /* read the RTC date and time registers all at once */ tmp = ds1307->read_block_data(ds1307->client,ds1307->offset, 7, ds1307->regs); //通过i2c总线读取ds1307的寄存器 if (tmp != 7) { dev_err(dev, "%s error %d\n", "read", tmp); return -EIO; } t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f); //读取秒寄存器 设置rtc时间-秒 t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); //读取分寄存器 设置rtc时间-分 tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f; //读取时寄存器 t->tm_hour = bcd2bin(tmp); //设置rtc时间-时 t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1; //读取星期寄存器 设置rtc时间-星期 t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f); //读取日寄存器 设置rtc时间-日 tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f; //读取月寄存器 t->tm_mon = bcd2bin(tmp) - 1; //设置rtc时间-月 /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */ //ds1307从20YY年计算起的不是19YY年,所以加一个世纪(好霸气!) t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100; //读取年寄存器 设置rtc时间-年 return rtc_valid_tm(t); //检验时间合理性 }
2 设置时间ds1307_set_time
static int ds1307_set_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int result; int tmp; u8 *buf = ds1307->regs; buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec); //设置秒 buf[DS1307_REG_MIN] = bin2bcd(t->tm_min); //设置分 buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); //设置时 buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); //设置星期 buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); //设置日 buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); //设置月 /* assume 20YY not 19YY */ tmp = t->tm_year - 100; //同读方法一样,减去一个世纪 buf[DS1307_REG_YEAR] = bin2bcd(tmp); //设置年 switch (ds1307->type) { case ds_1337: case ds_1339: case ds_3231: buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; break; case ds_1340: buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN| DS1340_BIT_CENTURY; break; default: break; } result = ds1307->write_block_data(ds1307->client,ds1307->offset, 7, buf); //i2c总线写回ds1307寄存器 if (result < 0) { dev_err(dev, "%s error %d\n", "write", result); return result; } return 0; }
3 读区闹钟ds1337_read_alarm
static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); //获取i2c客户端 struct ds1307 *ds1307 = i2c_get_clientdata(client); int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; ret = ds1307->read_block_data(client,DS1339_REG_ALARM1_SECS, 9, ds1307->regs); //i2c获取ds1307寄存器 if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm read", ret); return -EIO; } t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); //秒 t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f); //分 t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f); //小时 t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f); //日 t->time.tm_mon = -1; //月份没得设 结合“前面 3.1.3 读取闹钟时间”看 t->time.tm_year = -1; //年份没得设 t->time.tm_wday = -1; //没得设 t->time.tm_yday = -1; //没得设 t->time.tm_isdst = -1; //没得设 t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE); //使能闹钟? t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I); //使能挂起? return 0; }
4 设置闹钟
static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); //获取i2c客户端 struct ds1307 *ds1307 = i2c_get_clientdata(client); unsigned char *buf = ds1307->regs; u8 control, status; int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) //测试忙标志 return -EINVAL; ret = ds1307->read_block_data(client,DS1339_REG_ALARM1_SECS, 9, buf); //通过i2c总线读芯片的状态 if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm write", ret); return -EIO; } control = ds1307->regs[7]; status = ds1307->regs[8]; //保存状态标志 buf[0] = bin2bcd(t->time.tm_sec); //设置秒 buf[1] = bin2bcd(t->time.tm_min); //设置分 buf[2] = bin2bcd(t->time.tm_hour); //设置时 buf[3] = bin2bcd(t->time.tm_mday); //设置日 buf[4] = 0; buf[5] = 0; buf[6] = 0; buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); if (t->enabled) { //设置闹钟使能 dev_dbg(dev, "alarm IRQ armed\n"); buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ } buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); ret = ds1307->write_block_data(client,DS1339_REG_ALARM1_SECS, 9, buf); //通过i2c总线写入芯片 if (ret < 0) { dev_err(dev, "can't set alarm time\n"); return ret; } return 0; }
5.rtc独有控制
ds1307支持闹钟中断的使能和禁用功能
static int ds1307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); int ret; switch (cmd) { case RTC_AIE_OFF: if (!test_bit(HAS_ALARM, &ds1307->flags)) return -ENOTTY; ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //通过i2c总线读取数据 if (ret < 0) //判断能否控制 return ret; ret &= ~DS1337_BIT_A1IE; ret = i2c_smbus_write_byte_data(client,DS1337_REG_CONTROL, ret); //通过i2c总线写入数据 if (ret < 0) return ret; break; case RTC_AIE_ON: if (!test_bit(HAS_ALARM, &ds1307->flags)) return -ENOTTY; ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //通过i2c总线读取数据 if (ret < 0) //判断能否控制 return ret; ret |= DS1337_BIT_A1IE; ret = i2c_smbus_write_byte_data(client,DS1337_REG_CONTROL, ret); //通过i2c总线写入数据 if (ret < 0) return ret; break; default: return -ENOIOCTLCMD; } return 0; }
关于中断,在ds1307 i2c设备驱动的probe方法中
err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,ds1307->rtc->name, client);
申请了中断,中断处理例程是ds1307_irq
static irqreturn_t ds1307_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; struct ds1307 *ds1307 = i2c_get_clientdata(client); disable_irq_nosync(irq); schedule_work(&ds1307->work); //调用ds1307->work return IRQ_HANDLED; }
在probe方法中也设置了
INIT_WORK(&ds1307->work, ds1307_work);
所以中断例程回去执行ds1307_work
static void ds1307_work(struct work_struct *work) { struct ds1307 *ds1307; struct i2c_client *client; struct mutex *lock; int stat, control; ds1307 = container_of(work, struct ds1307, work); client = ds1307->client; lock = &ds1307->rtc->ops_lock; mutex_lock(lock); stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); //i2c读取ds1307状态 if (stat < 0) goto out; if (stat & DS1337_BIT_A1I) { stat &= ~DS1337_BIT_A1I; i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat); control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); //i2c读取控制状态 if (control < 0) goto out; control &= ~DS1337_BIT_A1IE; i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); //i2c写入控制命令 //调用rtc_update_irq函数 设置了events为RTC_AF(闹钟中断)| RTC_IRQF rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); //结合"三.rtc设备接口-4.read方法"理解 } out: if (test_bit(HAS_ALARM, &ds1307->flags)) enable_irq(client->irq); mutex_unlock(lock); }