linux 平台设备rtc文件,Linux RTC驱动分析(三)----sys和proc文件系统

rtc-sysfs.c这个部分主要是有关sysfs的操作。在rtc_device_register函数中,rtc_sysfs_add_device(rtc);完成sys的操作。

void rtc_sysfs_add_device(struct rtc_device *rtc)

{

int err;

/* not all RTCs support both alarms and wakeup */

if (!rtc_does_wakealarm(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);

}

rtc_init函数中初始化sys。

void __init rtc_sysfs_init(struct class *rtc_class)

{

rtc_class->dev_attrs =rtc_attrs;

}

rtc_attrs设备属性组如下:

static struct device_attribute rtc_attrs[] = {

__ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),

__ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),

__ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),

__ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),

__ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,

rtc_sysfs_set_max_user_freq),

__ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),

{ },

};

这个属性组是在class.c的模块初始化函数中,由rtc_sysfs_init函数赋值给

rtc_class->dev_attrs的,

以后属于这个类的设备都会有这些属性。但是我们知道要想一个设备结构拥有一种属性,必须调用device_create_file,这样才会使这个属性出

现在sysfs相关设备目录里。但是在这里的代码中只是给这个类的dev_attrs域赋值了这个属性组指针,而没有调用

device_create_file。我原来以为是在rtc_device_resgister函数中由rtc_sysfs_add_device完成

这个工作,但是这个函数只是给设备添加了闹钟属性,并没有处理这个属性组。最后发现这个工作是由device_register来完成的:

device_register调用device_add

device_add调用device_add_attrs

device_add_attrs调用device_add_attributes

device_add_attributes调用device_create_file来完成设备的属性设置。

设置完属性后,在/sys/class/rtc/rtc(n)的目录下就会出现name、date、time、since_epoch、max_user_freq和hctosys等文件,用户读这些文件的时候就会调用相应的函数。如读取name文件,就会调用rtc_sysfs_show_name函数,这个函数也是在rtc-sysfs.c中实现的,作用是读取并显示时间。

/*

* NOTE:  RTC times displayed in sysfs use the RTC's timezone.  That's

* ideally UTC.  However, PCs that also boot to MS-Windows normally use

* the local time and change to match daylight savings time.  That affects

* attributes including date, time, since_epoch, and wakealarm.

*/

static ssize_t

rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr,

char *buf)

{

return sprintf(buf, "%s\n", to_rtc_device(dev)->name);

}

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);

if (retval == 0) {

retval = sprintf(buf, "%04d-%02d-%02d\n",

tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);

}

return retval;

}

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);

if (retval == 0) {

retval = sprintf(buf, "%02d:%02d:%02d\n",

tm.tm_hour, tm.tm_min, tm.tm_sec);

}

return retval;

}

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);

if (retval == 0) {

unsigned long time;

rtc_tm_to_time(&tm, &time);

retval = sprintf(buf, "%lu\n", time);

}

return retval;

}

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);

}

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);

unsigned long val = simple_strtoul(buf, NULL, 0);

if (val >= 4096 || val == 0)

return -EINVAL;

rtc->max_user_freq = (int)val;

return n;

}

static ssize_t

rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr,

char *buf)

{

#ifdef CONFIG_RTC_HCTOSYS_DEVICE

if (rtc_hctosys_ret == 0 &&

strcmp(dev_name(&to_rtc_device(dev)->dev),

CONFIG_RTC_HCTOSYS_DEVICE) == 0)

return sprintf(buf, "1\n");

else

#endif

return sprintf(buf, "0\n");

}

rtc-proc.c这个文件提供RTC的proc文件系统接口。proc文件系统是软件创建的文件系统,内核通过他向外界导出信息。

在第一份部分的rtc_device_register函数中调用rtc_proc_add_device(rtc);,函数主要功能就是增加proc文件系统的内容,该函数具体内容如下:

void rtc_proc_add_device(struct rtc_device *rtc)

{

if (rtc->id == 0)

proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);//rtc_proc_fops在下面讲述

}

他主要调用了proc_create_data。proc_create_data完成创建文件节点的作用,并将文件的操作函数与节点联系起来。调用这个函数后,在/proc/driver目录下就会有一个文件rtc,应用程序打开这个文件就会调用rtc_proc_open函数。

//以下函数在rtc_device_unregister()中调用

void rtc_proc_del_device(struct rtc_device *rtc)

{

if (rtc->id == 0)

remove_proc_entry("driver/rtc", NULL);

}

下面的每一个文件都绑定一个函数,当用户读取这个文件的时候,这些函数会向文件写入信息:

static const struct file_operations rtc_proc_fops = {

.open        = rtc_proc_open,

.read        = seq_read,

.llseek        = seq_lseek,

.release    = rtc_proc_release,

};

static int rtc_proc_open(struct inode *inode, struct file *file)

{

int ret;

struct rtc_device *rtc = PDE(inode)->data;

if (!try_module_get(THIS_MODULE))

return -ENODEV;

ret = single_open(file, rtc_proc_show, rtc);//rtc_proc_show接下来讲述

if (ret)

module_put(THIS_MODULE);

return ret;

}

我们知道一个proc的文件必须与一个操作函数组成一个proc入口项,这个文件才能正常工作。这个函数最主要作用就是调用

single_open,创建一个proc文件入口项,使其操作函数是rtc_proc_show,并初始化seq_file接口。

rtc_proc_show函数如下定义:

static int rtc_proc_show(struct seq_file *seq, void *offset)

{

int err;

struct rtc_device *rtc = seq->private;

const struct rtc_class_ops *ops = rtc->ops;

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, "update IRQ enabled\t: %s\n",

(rtc->uie_rtctimer.enabled) ? "yes" : "no");

seq_printf(seq, "periodic IRQ enabled\t: %s\n",

(rtc->pie_enabled) ? "yes" : "no");

seq_printf(seq, "periodic IRQ frequency\t: %d\n",

rtc->irq_freq);

seq_printf(seq, "max user IRQ frequency\t: %d\n",

rtc->max_user_freq);

}

seq_printf(seq, "24hr\t\t: yes\n");

if (ops->proc)

ops->proc(rtc->dev.parent, seq);

return 0;

}

这个函数就是最后给用户显示信息的函数了,可以看出他通过调用rtc_deivce中的操作函数读取时间、日期和一些其他的信息显示给用户。

RTC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。RTC新的驱动接口提供了更多的功能,使系统可以同时存在多个RTC。

/dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用RTC。RTC核心代码的组织方式值得学习,不同功能的代码放在不同的文件中,简单明了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值