首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

深入解析linux停rtc架构

2013-10-27 
深入解析linux下rtc架构一.描述rtc相关结构体1.rtc设备struct rtc_device//rtc设备{struct device dev//

深入解析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);//设置时间mmssint (*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个RTCpr_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);elsepr_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;//中断数据清0spin_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_timealarm.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月]+日-1return 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)//秒大于60return -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+过去的日字数/365days -= (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*60mtime -= 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减去2mon += 12;//月份+12 year -= 1;//年份也调少1年}//上面调整了月份排序,也等价于,忽略1,2月份过去的天数,新调整的月份排序参考D.4return ((((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方法} elseerr = -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时间beforeif (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);//闹钟读取失败其时间域将会置为-1if (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++;//月份+1else {//(如果是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;elseerr = 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_ONrtc_irq_set_state(rtc, NULL, 1)RTC_PIE_OFFrtc_irq_set_state(rtc, NULL, 0)rtc->ops->irq_set_state(rtc->dev.parent, enabled)RTC_AIE_ONrtc_alarm_irq_enable(rtc, 1)RTC_AIE_OFFrtc_alarm_irq_enable(rtc, 0)rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled)RTC_UIE_ONrtc_update_irq_enable(rtc, 1)RTC_UIE_OFFrtc_update_irq_enable(rtc, 0)rtc->ops->update_irq_enable(rtc->dev.parent, enabled)


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

RTC_IRQP_SETrtc_irq_set_freq(rtc, NULL, arg)rtc->ops->irq_set_freq(rtc->dev.parent, freq)RTC_IRQP_READput_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);//上传数据到用户空间elseret = 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);elseseq_printf(seq, "**:");if ((unsigned int)alrm.time.tm_min <= 59)seq_printf(seq, "%02d:", alrm.time.tm_min);elseseq_printf(seq, "**:");if ((unsigned int)alrm.time.tm_sec <= 59)seq_printf(seq, "%02d\n", alrm.time.tm_sec);elseseq_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);elseseq_printf(seq, "****-");if ((unsigned int)alrm.time.tm_mon <= 11)seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);elseseq_printf(seq, "**-");if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)seq_printf(seq, "%02d\n", alrm.time.tm_mday);elseseq_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);inttmp;/* 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);intresult;inttmp;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);intret;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;u8control, status;intret;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);intret;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->workreturn 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;intstat, 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_IRQFrtc_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);}






















 

 

 

 

 

 

 

 

 

 

 

热点排行