RTC也是一个字符设备,打开,读写,就是打开时钟,设置和读时钟。这里以S3C2440为例,S3C2440里面自带有RTC模块,可以直接操作寄存器来打开,写,读等操作。其驱动在rtc_s3c.c里面:
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
int ret;
pr_debug("%s: probe=%p\n", __func__, pdev);
/* find the IRQs */
s3c_rtc_tickno = platform_get_irq(pdev, 1);
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");
return -ENOENT;
}
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");
return -ENOENT;
}
pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
s3c_rtc_tickno, s3c_rtc_alarmno);
/* get the memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");
return -ENOENT;
}
s3c_rtc_mem = request_mem_region(res->start,
res->end-res->start+1,
pdev->name);
if (s3c_rtc_mem == NULL) {
dev_err(&pdev->dev, "failed to reserve memory region\n");
ret = -ENOENT;
goto err_nores;
}
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto err_nomap;
}
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
pr_debug("s3c2410_rtc: RTCCON=%02x\n",
readb(s3c_rtc_base + S3C2410_RTCCON));
s3c_rtc_setfreq(&pdev->dev, 1);
device_init_wakeup(&pdev->dev, 1);
/* register RTC and exit */
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
THIS_MODULE);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}
rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc);
return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);
iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores:
return ret;
}
前面获取plat信息(中断号,寄存器基地址等),通过设置寄存器,使能RTC,设置好频率,最后调用:
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
THIS_MODULE);
这里传入的s3c_rtcops放的是平台实际去操作RTC的操作函数,后面再分析。
看这个函数,注册RTC设备:
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
*
* rtc_device_unregister() must be called when the class device is no
* longer needed.
*
* Returns the pointer to the new struct class device.
*/
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;
int id, err;
if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
err = -ENOMEM;
goto exit;
}
mutex_lock(&idr_lock);
err = idr_get_new(&rtc_idr, NULL, &id);
mutex_unlock(&idr_lock);
if (err < 0)
goto exit;
id = id & MAX_ID_MASK;
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
err = -ENOMEM;
goto exit_idr;
}
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
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);
dev_set_name(&rtc->dev, "rtc%d", id);
rtc_dev_prepare(rtc);
err = device_register(&rtc->dev);
if (err)
goto exit_kfree;
rtc_dev_add_device(rtc);
rtc_sysfs_add_device(rtc);
rtc_proc_add_device(rtc);
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);
}
可以看到这个函数就是注册一个RTC字符设备,并且创建设备节点(dev/rtc):
s3c_rtc_init
platform_driver_register
s3c_rtc_probe
rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE)
rtc_dev_prepare
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc_dev_add_device
cdev_add
上面cdev_init可以知道,这个字符设备提供应用的操作函数:
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,
};
应用open的时候:
app: open("/dev/rtc0");
-------------------------------------------
kernel: sys_open
rtc_dev_fops.open
rtc_dev_open
// 根据次设备号找到以前用"rtc_device_register"注册的rtc_device
struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);
const struct rtc_class_ops *ops = rtc->ops;
file->private_data = rtc; //rtc设备数据设置进file私有数据
err = ops->open ? ops->open(rtc->dev.parent) : 0;
应用读的时候用的iotrl:
app: ioctl(fd, RTC_RD_TIME,...)
-------------------------------------------
kernel: sys_ioctl
rtc_dev_fops.ioctl
rtc_dev_ioctl
struct rtc_device *rtc = file->private_data; //获取rtc设备数据
rtc_read_time(rtc, &tm);
err = rtc->ops->read_time(rtc->dev.parent, tm);
s3c_rtc_gettime
从上面open,ioctl可以看出,他们实际上是调用的都是rtc里面的ops操作:
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.irq_set_freq = s3c_rtc_setfreq,
.irq_set_state = s3c_rtc_setpie,
.proc = s3c_rtc_proc,
};
这个结构体实际上就是对RTC硬件操作,跟平台有关的,其他的部分都是内核已经完成了一个好的驱动框架,我们换平台主要就是实现这个结构体,将其注册到:
rtc_device_register就可以了,rtc_device_register就是内核的RTC驱动层。
RTC测试:
ls /dev/rtc* -l
date /* 显示系统时间 */
date 123015402011.30 /* 设置系统时间 date [MMDDhhmm[[CC]YY][.ss]] */
hwclock -w /* 把系统时间写入RTC */
断电,重启,执行date
————————————————————————————————————————————————————————————————————————————
RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间。RTC因为是电池供电的,所以掉电后时间不丢失。Linux内核把RTC用作“离线”的时间与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期,作为基准值。在运行期间内核完全抛开RTC,以软件的形式维护系统的当前时间与日期,并在需要时将时间回写RTC芯片。另外如果RTC提供了IRQ中断并且可以定时,那么RTC还可以作为内核睡眠时唤醒内核的闹钟。应用程序可以用RTC提供的周期中断做一些周期的任务。 linux有两种rtc驱动的接口,一个是老的接口,专门用在PC机上的。另外一钟新接口是基于linux设备驱动程序的。这个新的接口创建了一个RTC驱动模型,实现了RTC的大部分基本功能。而底层驱动无须考虑一些功能的实现,只需将自己注册的RTC核心中,其他工作由RTC核心来完成。下面分析RTC新接口的驱动模型。
一. 驱动模型结构与RTC核心有关的文件有:
/drivers/rtc/class.c 这个文件向linux设备模型核心注册了一个类RTC,然后向驱动程序提供了注册/注销接口
/drivers/rtc/rtc-dev.c 这个文件定义了基本的设备文件操作函数,如:open,read等
/drivers/rtc/interface.c 顾名思义,这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数
/drivers/rtc/rtc-sysfs.c 与sysfs有关
/drivers/rtc/rtc-proc.c 与proc文件系统有关
/include/linux/rtc.h 定义了与RTC有关的数据结构
RTC驱动模型结构如下图:
1. struct rtc_device 结构
- struct rtc_device
- {
- struct device dev;
- struct module *owner;
- int id;
- char name[RTC_DEVICE_NAME_SIZE];
- const struct rtc_class_ops *ops;
- struct mutex ops_lock;
- struct cdev char_dev;
- unsigned long flags;
- 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;
- #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
- struct work_struct uie_task;
- struct timer_list uie_timer;
- /* Those fields are protected by rtc->irq_lock */
- unsigned int oldsecs;
- unsigned int uie_irq_active:1;
- unsigned int stop_uie_polling:1;
- unsigned int uie_task_active:1;
- unsigned int uie_timer_active:1;
- #endif
- };
2. struct rtc_class_ops 结构
- 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 *);
- int (*set_mmss)(struct device *, unsigned long secs);
- 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);
- };
3. struct rtc_time 结构
- 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;
- };
三. class.c
1. 模块初始化函数:rtc_init
- static int __init rtc_init(void)
- {
- rtc_class = class_create(THIS_MODULE, "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_sysfs_init(rtc_class);
- return 0;
- }
2. 为底层驱动提供接口:rtc_device_register,rtc_device_unregister
- 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;
- int id, err;
- if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
- err = -ENOMEM;
- goto exit;
- }
- mutex_lock(&idr_lock);
- err = idr_get_new(&rtc_idr, NULL, &id);
- mutex_unlock(&idr_lock);
- /*--------------------(1)---------------------*/
- if (err < 0)
- goto exit;
- id = id & MAX_ID_MASK;
- rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
- if (rtc == NULL) {
- err = -ENOMEM;
- goto exit_idr;
- }
- rtc->id = id;
- rtc->ops = ops;
- rtc->owner = owner;
- rtc->max_user_freq = 64;
- rtc->dev.parent = dev;
- rtc->dev.class = rtc_class;
- 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);
- dev_set_name(&rtc->dev, "rtc%d", id);
- /*-------------------(2)--------------------*/
- rtc_dev_prepare(rtc);
- err = device_register(&rtc->dev);
- if (err)
- goto exit_kfree;
- /*-------------------(3)--------------------*/
- rtc_dev_add_device(rtc);
- rtc_sysfs_add_device(rtc);
- rtc_proc_add_device(rtc);
- dev_info(dev, "rtc core: registered %s as %s\n",
- rtc->name, dev_name(&rtc->dev));
- /*-------------------(4)--------------------*/
- 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);
- }
(2):分配了一个rtc_device的结构--rtc,并且初始化了相关的成员:id, rtc_class_ops等等。
(3):首先调用rtc_dev_prepare(在rtc-dev.c中定义)。因为RTC设备本质来讲还是字符设备,所以这里初始化了字符设备相关的结构:设备号以及文件操作。然后调用device_register将设备注册到linux设备模型核心。这样在模块加载的时候,udev daemon就会自动为我们创建设备文件rtc(n)。
(4):先后调用rtc_dev_add_device,rtc_sysfs_add_device,rtc_proc_add_device三个函数。rtc_dev_add_device注册字符设备,rtc_sysfs_add_device只是为设备添加了一个闹钟属性,rtc_proc_add_device 创建proc文件系统接口。
四. rtc-dev.c
rtc-dev.c 初始化了一个file_operations结构--rtc_dev_fops,并定义了这些操作函数。
1. rtc_dev_fops 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,
- ;
- rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
- struct rtc_device *rtc = file->private_data;
- 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) {
- 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;
- }
五. interface.c
interface.c里的所有函数的实现都对应于rtc-dev.c 中ioctl相应的命令。对应关系如下:
RTC_ALM_READ rtc_read_alarm 读取闹钟时间
RTC_ALM_SET rtc_set_alarm 设置闹钟时间
RTC_RD_TIME rtc_read_time 读取时间与日期
RTC_SET_TIME rtc_set_time 设置时间与日期
RTC_PIE_ON RTC_PIE_OFF rtc_irq_set_state 开关RTC全局中断的函数
RTC_AIE_ON RTC_AIE_OFF rtc_alarm_irq_enable 使能禁止RTC闹钟中断
RTC_UIE_OFF RTC_UIE_ON rtc_update_irq_enable 使能禁止RTC更新中断
RTC_IRQP_SET rtc_irq_set_freq 设置中断的频率
以上就是所有ioctl的命令与实现的对应关系。其中如果不涉及中断的话,有两个命令需要我们特别关心一下,就是RTC_RD_TIME与RTC_SET_TIME。因为RTC最基本的功能就是提供时间与日期。这两个命令恰恰是获取时间和设置时间。下面分析一下这两个命令的实现,也就是rtc_set_alarm与rtc_read_time函数的实现:
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)
- err = -ENODEV;
- else if (!rtc->ops->read_time)
- err = -EINVAL;
- else {
- memset(tm, 0, sizeof(struct rtc_time));
- err = rtc->ops->read_time(rtc->dev.parent, tm);
- }
- mutex_unlock(&rtc->ops_lock);
- return err;
- }
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)
- err = -ENODEV;
- else if (rtc->ops->set_time)
- err = rtc->ops->set_time(rtc->dev.parent, tm);
- else if (rtc->ops->set_mmss) {
- unsigned long secs;
- err = rtc_tm_to_time(tm, &secs);
- if (err == 0)
- err = rtc->ops->set_mmss(rtc->dev.parent, secs);
- } else
- err = -EINVAL;
- mutex_unlock(&rtc->ops_lock);
- return err;
- }
六. rtc-sysfs.c 部分
这个部分主要是有关sysfs的操作。rtc-sysfs.c中定义了这样一个设备属性组,如下:
- 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等文件,用户读这些文件的时候就会调用相应的函数。如读取name文件,就会调用rtc_sysfs_show_name函数,这个函数也是在rtc-sysfs.c中实现的,作用是读取并显示时间。
七. rtc-proc.c这个文件提供RTC的proc文件系统接口。proc文件系统是软件创建的文件系统,内核通过他向外界导出信息,下面的每一个文件都绑定一个函数,当用户读取这个文件的时候,这个函数会向文件写入信息。rtc-proc.c中初始化了一个文件操作:
- static const struct file_operations rtc_proc_fops = {
- .open = rtc_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = rtc_proc_release,
- };
- void rtc_proc_add_device(struct rtc_device *rtc)
- {
- if (rtc->id == 0)
- proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
- }
- 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);
- }
- 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, "24hr\t\t: yes\n");
- if (ops->proc)
- ops->proc(rtc->dev.parent, seq);
- return 0;
- }
六. 总结
RTC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。RTC新的驱动接口提供了更多的功能,使系统可以同时存在多个RTC。/dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用RTC,RTC核心虽然表面上看上去很简单,但是还是涉及到很多知识,有些东西书上讲的还是不够详细,还需要通过分析代码加深理解。 另外RTC核心代码的组织方式也值得学习,不同功能的代码放在不同的文件中,简单明了。