您的位置:首页 > 运维架构 > 网站架构

深入解析linux下rtc架构

2015-07-16 15:32 621 查看
转载来自: 深入解析linux下rtc架构 | 学步园 +http://www.xuebuyuan.com/1722840.html

一.描述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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: