深入解析linux下rtc架構

一.描述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);
}






















 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章