rtc节点

rtc-sysfs文件主要的操作就是在sys下创建rtc的属性节点,可以方便用户方便快捷的访问,查找问题。下来大概看看sys下的rtc节点,有个直观的认识。
[root@test ~]# cat /sys/class/rtc/rtc0/
date           hctosys        power/         time
dev            max_user_freq  since_epoch    uevent
device/        name           subsystem/     wakealarm 

这是手机上rtc的节点属性,可以看到手机上只有一个rtc0设备。也可以查看/dev/rtc0设备

[root@test ~]# ls -l /dev/rtc0
crw-rw----    1 root     root      254,   0 Jan  1  1970 /dev/rtc0

可以看到rtc的主设备号是254, 次设备号是0。这些信息也可以在/proc/devices下看到。

[root@test ~]# cat /proc/devices
Character devices:1 mem2 pty...
254 rtc

也可以看到rtc的主设备号是254, 这都是通过上一节说的rtc-dev.c中注册得到的。

rtc-sysfs.c分析

void __init rtc_sysfs_init(struct class *rtc_class)
{rtc_class->dev_groups = rtc_groups;
}

设置rtc的设备组属性,rtc_groups是一个attribute_group的结构体。这个函数会在class.c中rtc_init中调用到,关于rtc_group会在后面说到。

static inline int rtc_does_wakealarm(struct rtc_device *rtc)
{if (!device_can_wakeup(rtc->dev.parent))                 //用来判断是否具有wakeup的能力return 0; return rtc->ops->set_alarm != NULL;                      //用来判断是否具有alarm的能力
}

该函数是用来检测rtc是否支持wakeup功能和alarm功能。 wakeup的能力就是能唤醒suspend-to-RAM/suspend-to-disk设备。wakeup的能力是通过如下代码:

static inline bool device_can_wakeup(struct device *dev)
{return dev->power.can_wakeup;
}

也就是判断can_wakeup是否为true,至于rtc是否支持就需要看对应的rtc驱动是否实现该功能。

比如驱动: rtc-ds1305.c中就调用如下的代码设置wakeup的能力。
device_set_wakeup_capable(&spi->dev, 1);
也可以通过如下方式判断是否支持wakeup功能:
root@test:/ # cat /sys/class/rtc/rtc0/device/power/wakeup
enabled

显示enabled就代表此rtc支持 wakeup功能,也就是说有唤醒suspend/standby的系统或者设备。
而对于rtc是否支持alarm功能,就通过驱动的ops操作函数集合看set_alarm有没有实现就ok。
如果rtc即支持wakeup功能也支持alarm功能,则:
void rtc_sysfs_add_device(struct rtc_device *rtc)
{int err;/* not all RTCs support both alarms and wakeup */if (!rtc_does_wakealarm(rtc))                                     //检测是否支持wakeup和alarm功能return;err = device_create_file(&rtc->dev, &dev_attr_wakealarm);         //创建wakealarm属性if (err)dev_err(rtc->dev.parent,"failed to create alarm attribute, %d\n", err);
}

如果rtc都支持wakup和alarm功能,就创建wakealarm属性节点。否则不创建。

接下来分析wakealarm属性的show和store函数。
static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR, rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);

这里出现了DEVICE_ATTR,有必要说一下这个宏定义。

<kernel/include/liunux/device.h>
---------------------------------------------------------------------------------
#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define __ATTR(_name, _mode, _show, _store) {               \.attr = {.name = __stringify(_name),             \.mode = VERIFY_OCTAL_PERMISSIONS(_mode) },        \.show  = _show,                       \.store = _store,                      \
}

以上就是DEVICE_ATTR的宏定义,则按照定义将wakealarm的属性展开,如下:
 struct device_attribute dev_attr_wakealarm {.name   =   wakealarm,.mode   =   S_IRUGO | S_IWUSR,.show =   rtc_sysfs_show_wakealarm,.store    =   rtc_sysfs_set_wakealarm,}

上面的属性可以知道,wakealarm的属性为可读可写的,当cat wakealarm的时候最终调用show函数,echo的时候最终调用strore函数。

root@test:/ # cat /sys/class/rtc/rtc0/wakealarm
root@test:/ # 

当读wakealarm的时候,没有任何值,说明目前没有设备alarm。

也可以通过cat /proc/driver/rtc获得更多的信息:
rtc_time    : 07:07:46
rtc_date    : 2012-01-01
alrm_time   : 00:00:00
alrm_date   : 1970-01-01
alarm_IRQ   : no
alrm_pending    : no
update IRQ enabled  : no
periodic IRQ enabled    : no
periodic IRQ frequency  : 1
max user IRQ frequency  : 64
24hr        : yes

可以看到alarm_IRQ是no, 当设置正确的alarm值后就会变为yes的。接下来设置当前的时间之后的100s

root@test:/ # echo +100 > /sys/class/rtc/rtc0/wakealarm
root@test:/ # cat /proc/driver/rtc
rtc_time    : 07:09:32
rtc_date    : 2012-01-01
alrm_time   : 07:11:05
alrm_date   : 2012-01-01
alarm_IRQ   : yes
alrm_pending    : no
update IRQ enabled  : no
periodic IRQ enabled    : no
periodic IRQ frequency  : 1
max user IRQ frequency  : 64
24hr        : yes

可以看到alrm_time变为当前时间+100s了,同时alarm_IRQ也变为yes。

同时再次cat wakealarm,即可获得值。
root@test:/ # cat /sys/class/rtc/rtc0/wakealarm
1325401865

此值是unix的时间戳,必须要转换为UTC时间,可以通过如下的网址转换,http://tool.chinaz.com/Tools/unixtime.aspx
可以看到转换后的时间是2012/1/1 15:11:5,为什么感觉和alrm_time对不上呢? 那是因为北京在东八区,相差8个小时,15-8=7则就是alrm_time。
当cat wakealarm有值的时候,再次echo值进如wakealarm的时候就会出现设备忙,所以再次设备时候必须清除以前的设置。
root@test:/ # cat /sys/class/rtc/rtc0/wakealarm
1325401865
root@test:/ # echo +100 > /sys/class/rtc/rtc0/wakealarm
sh: echo: write error: Device or resource busy

那如何就可以清空wakealarm的值,可以通过echo 0 > wakealarm就可以清空

root@test:/ # cat /sys/class/rtc/rtc0/wakealarm
1325401865
root@test:/ # echo 0 > /sys/class/rtc/rtc0/wakealarm
root@test:/ # cat /sys/class/rtc/rtc0/wakealarm
root@test:/ #

在知道了上述的设置之后,再来看代码,在看代码之前先看一下必要的数据结构。

struct rtc_time {int tm_sec;        //秒int tm_min;        //分钟int tm_hour;       //小时int tm_mday;       //一月中的第几天int tm_mon;        //月份int tm_year;       //年份int tm_wday;       //周int tm_yday;       //一年中的第几天int tm_isdst;      //夏令时标识符
};/** This data structure is inspired by the EFI (v0.92) wakeup* alarm API.*/
struct rtc_wkalrm {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 */
};

先分析store函数,当之后往里面写值的之后,才可以read出来。

static ssize_t
rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, const char *buf, size_t n)
{ssize_t retval;unsigned long now, alarm;unsigned long push = 0;struct rtc_wkalrm alm;struct rtc_device *rtc = to_rtc_device(dev);char *buf_ptr;int adjust = 0;/* Only request alarms that trigger in the future.  Disable them* by writing another time, e.g. 0 meaning Jan 1 1970 UTC.                 //设置时间必须是在将来*/retval = rtc_read_time(rtc, &alm.time);                                    //读取当前时间if (retval < 0)return retval;rtc_tm_to_time(&alm.time, &now);                                            //将当前的时间转化为从1970来经历的秒数buf_ptr = (char *)buf;if (*buf_ptr == '+') {                     //如果按照我们上面的设置,echo +100 > wakealarm, 则buf就是+100, 然后解析bufbuf_ptr++;if (*buf_ptr == '=') {buf_ptr++;push = 1;} elseadjust = 1;                   //执行到这里}alarm = simple_strtoul(buf_ptr, NULL, 0);     //将“100”转化为数字if (adjust) {alarm += now;                         //alarm就是当前时间+100}if (alarm > now || push) {/* Avoid accidentally clobbering active alarms; we can't* entirely prevent that here, without even the minimal* locking from the /dev/rtcN api.*/retval = rtc_read_alarm(rtc, &alm);        //读取alarm时间if (retval < 0)return retval;if (alm.enabled) {                         //第一次是没有使能的,如果第二次设置的话if (push) {rtc_tm_to_time(&alm.time, &push);alarm += push;} elsereturn -EBUSY;               //就会出现设备忙,在上面已经演示过了} else if (push)return -EINVAL;alm.enabled = 1;                             //先使能} else {alm.enabled = 0;/* Provide a valid future alarm time.  Linux isn't EFI,* this time won't be ignored when disabling the alarm.*/alarm = now + 300;}rtc_time_to_tm(alarm, &alm.time);                      //又将秒数设置为农历时间格式retval = rtc_set_alarm(rtc, &alm);                      //设置alarm时间return (retval < 0) ? retval : n;
}

下面分析read操作,最终调用show函数。

static ssize_t rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr, char *buf)
{ssize_t retval;unsigned long alarm;struct rtc_wkalrm alm;/* Don't show disabled alarms.  For uniformity, RTC alarms are* conceptually one-shot, even though some common RTCs (on PCs)* don't actually work that way.** NOTE: RTC implementations where the alarm doesn't match an* exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC* alarms after they trigger, to ensure one-shot semantics.*/retval = rtc_read_alarm(to_rtc_device(dev), &alm);                         //读取alarm的值if (retval == 0 && alm.enabled) {                                          //如果enable了,然后显示rtc_tm_to_time(&alm.time, &alarm);retval = sprintf(buf, "%lu\n", alarm);}return retval;

分析完wakealarm节点之后,还有一系列节点是rtc共有的,如下:

static struct attribute *rtc_attrs[] = {&dev_attr_name.attr,&dev_attr_date.attr,&dev_attr_time.attr,&dev_attr_since_epoch.attr,&dev_attr_max_user_freq.attr,&dev_attr_hctosys.attr,NULL,
};
ATTRIBUTE_GROUPS(rtc);

在这里需要将ATTRIBUTE_GROOUP(rtc)展开,展开之后就是:

static const struct attribute_group rtc_group = {.attrs = rtc_attrs,
}
static const struct attribute_group *rtc_groups[]={&rtc_group,null
}

而rtc_groups就是在rtc_sysfs_init赋值给dev_groups的。 在device_add_attrs函数中会添加这些属性,如下:

static int device_add_attrs(struct device *dev)
{struct class *class = dev->class;const struct device_type *type = dev->type;int error;if (class) {error = device_add_groups(dev, class->dev_groups);if (error)return error;}

明白上述的创建原理之后,再依次看每个节点的意思。
static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
}
static DEVICE_ATTR_RO(name);

只读属性,显示rtc设备的名称,在驱动中会有该rtc对应的名称。

static ssize_t date_show(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);if (retval == 0) {retval = sprintf(buf, "%04d-%02d-%02d\n",tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);}return retval;
}
static DEVICE_ATTR_RO(date);static ssize_t  time_show(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);if (retval == 0) {retval = sprintf(buf, "%02d:%02d:%02d\n",tm.tm_hour, tm.tm_min, tm.tm_sec);}return retval;
}
static DEVICE_ATTR_RO(time);

上述的两个只读属性,一个是当前的时间,一个是当前的日期,不做过多解释。

static ssize_t since_epoch_show(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);if (retval == 0) {unsigned long time;rtc_tm_to_time(&tm, &time);retval = sprintf(buf, "%lu\n", time);}return retval;
}
static DEVICE_ATTR_RO(since_epoch);

只读属性,该属性的值表示当前的时间转换为自1970年来的秒数。

static ssize_t max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
}static ssize_t max_user_freq_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t n)
{struct rtc_device *rtc = to_rtc_device(dev);unsigned long val = simple_strtoul(buf, NULL, 0);if (val >= 4096 || val == 0)return -EINVAL;rtc->max_user_freq = (int)val;return n;
}
static DEVICE_ATTR_RW(max_user_freq);

可读可写属性,show函数是读取最大的freq, store是设置最大的频率,不能超过4096.

/*** rtc_sysfs_show_hctosys - indicate if the given RTC set the system time** Returns 1 if the system clock was set by this RTC at the last* boot or resume event.*/
static ssize_t hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
{
#ifdef CONFIG_RTC_HCTOSYS_DEVICEif (rtc_hctosys_ret == 0 &&strcmp(dev_name(&to_rtc_device(dev)->dev),CONFIG_RTC_HCTOSYS_DEVICE) == 0)return sprintf(buf, "1\n");else
#endifreturn sprintf(buf, "0\n");
}
static DEVICE_ATTR_RO(hctosys);

只读属性,如果返回1代表系统的clock最近一次使用rtc设置。返回0代表没有。

Linux RTC驱动模型分析之rtc-sysfs.c相关推荐

  1. linux RTC 驱动模型分析

    linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...

  2. LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析

    上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device ...

  3. linux MISC 驱动模型分析

    linux MISC 驱动模型分析 阅读led驱动程序的代码的时候,没有发现ldd3中提到的各种字符设备注册函数,而是发现了一个misc_register函数,这说明led设备是作为杂项设备出现在内核 ...

  4. Linux RTC驱动模型分析之rtc-sysfs.c【转】

    转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...

  5. linux platform 驱动模型分析

    一. 概述     platform设备和驱动与linux设备模型密切相关.platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构.它的主要作用就是管理系统的外设资源,比如io ...

  6. linux i2c adapter 增加设备_LINUX设备驱动模型分析之四 设备模块相关(DEVICE)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> <LINUX设备驱动模型分析之三 ...

  7. linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...

  8. Linux设备驱动模型一 sysfs

    Linux设备驱动模型一 sysfs 1 Linux设备模型 Linux 2.5的内核引入了一种新的设备模型,目的是对计算机上的所有设备进行统一的管理. 它包含以下基础结构: 类型 说明 设备Devi ...

  9. linux 内核驱动模型,linux设备驱动模型架构分析 一

    linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...

  10. char添加一个字符_LINUX字符设备驱动模型分析(起始篇)

    在前面几个模块的介绍中,我们主要以vfs为起始,完成了sysfs.设备-总线-驱动模型.platform设备驱动模型.i2c设备驱动模型.spi设备驱动模型的分析.在对这些模块进行分析的时候,我们或多 ...

最新文章

  1. OKR和绩效管理如何一起工作?
  2. 一文带你读懂Python的5大特点与8大应用方向!
  3. 分享18个常用的网站性能测试工具
  4. 【算法漫画】:什么是红黑树?(整合版)
  5. 【报错笔记】在maven项目中jsp页面使用window.location.href给controller传参时参数过长所以路径无法跳转至controller
  6. matlab五子棋_应用 | 五子棋游戏——没人能在我的程序里打败我
  7. datatable如何生成级联数据_如何把Excel表数据批量生成条形码
  8. 【实战】烂泥:一次纠结的系统安装
  9. Docker 安装 MySQL5.7
  10. 关于mysql的几个问题
  11. React Native RSA加密
  12. FTTP/FTTH理想解决方案(组图)
  13. word转换成pdf转换器2015绿色版
  14. 强大视频电影播放软件——乐鱼影音盒!
  15. 中国羽绒服市场深度调查研究报告
  16. luogu P5064 [Ynoi2014] 等这场战争结束之后
  17. 基于Java Socket的局域网聊天系统
  18. iOS 手动实现KVO / iOS KVO底层原理
  19. 2019迅雷校园招聘!后端工程师岗位两次技术面题目总结和解析
  20. MAC干净卸载IDEA

热门文章

  1. 中文字体压缩器-解决css引入的字体太大的问题
  2. mysql批量插入优化
  3. IOS中通知中心NSNotificationCenter应用总结
  4. croppic 图片裁剪
  5. 用微软makecert.exe生成一个自签名的证书
  6. POJ 2135 Farm Tour
  7. Spring Cloud Alibaba系列教程-03-搭建生产可用的Nacos集群
  8. 向英雄联盟生涯致敬,三十行Python代码爬取LOL官网英雄皮肤图片
  9. IAR编译仿真时提示“__vector_table symbol not found”
  10. windows server 2003产生的 Minidmp蓝屏文件分析求助