RTC驱动系统分析
来源:互联网 发布:c语言中的error 编辑:程序博客网 时间:2024/06/03 21:33
一. RTC及驱动简介
RTC即real time clock实时时钟,主要用于为操作系统提供可靠的时间;当系统处于断电 的情况下,RTC记录操作系统时间,并可在电池供电情况下继续正常工作,当系统正常启动后,系统可从RTC读取时间信息,来确保断电后时间运行连续性。
目前,很多CPU中都已集成RTC系统,且有许多独立的外接RTC芯片可用于实现RTC功能;
在内核中RTC驱动可分为两层,一层为于硬件无关的抽象层,主要用于管理RTC设备、设备节点,属性节点注册及操作;另一层为与硬件相关的RTC低层驱动层;抽象层程序在/drivers/rtc目录下,主要涉及下面几个文件:
class.c 用于管理和注册RTC设备结构、sysfs、procfs及RTC类;
rtc-dev.c 用于注册和管理RTC设备节点,为用户空间提供devfs操作接口,主要操作有rtc_read,rtc_ioctl;
rtc-proc.c 用于管理rtc的procfs属性节点,提供一些中断状态、标志查询;
rtc-sysfs.c 用于管理rtc设备的sysfs属性,如获取RTC设备名字、日期、时间等属性信息;
interface.c 为rtc-dev.c 和RTC低层驱动提供操作接口;
RTC系统结构示意图如图1所示;
图1 RTC系统结构图
如图1所示,RTC 设备驱动通过rtc_device_register接口向linux系统注册RTC设备,并成生proc、sys目录下的属性文件;当用户通过/dev/rtcx设备节点或proc、sysfs接口来操作RTC设备时,都需要先通过interface接口才能访问真实的设备驱动。
二. 部分数据结构分析
1. RTC设备结构
struct rtc_device
{
struct device dev;
struct module *owner;
int id; 设备编号
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops; rtc设备低层操作接口;
struct mutex ops_lock;
struct cdev char_dev; RTC字符型设备结构;
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;
struct timerqueue_head timerqueue;
struct rtc_timer aie_timer; 报警中断定时器;
struct rtc_timer uie_rtctimer; 更新中断定时器;
struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */ 周期中断高精度定时器;
int pie_enabled; 周期中断使能标志;
struct work_struct irqwork;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 内部仿真update interrupt时所需;
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. 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 *); 读取RTC时间;
int (*set_time)(struct device *, struct rtc_time *); 设置RTC时间;
int (*read_alarm)(struct device *, struct rtc_wkalrm *); 读取RTC报警时间;
int (*set_alarm)(struct device *, struct rtc_wkalrm *); 设置RTC报警时间;
int (*proc)(struct device *, struct seq_file *); 用于提供procfs查询rtc状态接口;
int (*set_mmss)(struct device *, unsigned long secs); 设置以S为单位RTC时间接口;
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled); 中断使能接口;
};
三.RTC底层驱动实现
1.用户程序想要读取rtc的时间,就需要利用/dev/rtc的设备文件进行操作
#include <stdio.h> #include <stdlib.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <time.h> int main(void) { int fd, retval; struct rtc_time rtc_tm; time_t timep; struct tm *p; fd = open("/dev/rtc", O_RDONLY); if (fd == -1) { perror("/dev/rtc"); exit(errno); } /* Read the RTC time/date */ retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); if (retval == -1) { perror("ioctl"); exit(errno); } close(fd); }
2.底层通过rtc-dev.c和interface.c完成驱动的open和ioctl
$ vi driver/rtc/rtc-dev.c static const struct file_operations rtc_dev_fops = { ||| rtc_uie_timer .owner = THIS_MODULE, ||| clear_uie .llseek = no_llseek, ||| set_uie .read = rtc_dev_read, ||| rtc_dev_update_irq_enable_emul .poll = rtc_dev_poll, ||| rtc_dev_read .unlocked_ioctl = rtc_dev_ioctl, ||| rtc_dev_poll .open = rtc_dev_open, ||| rtc_dev_ioctl .release = rtc_dev_release, ||| rtc_dev_fasync .fasync = rtc_dev_fasync, ||| rtc_dev_release}; static int rtc_dev_open(struct inode *inode, struct file *file) || { ||- function int err; ||| rtc_dev_open struct rtc_device *rtc = container_of(inode->i_cdev, ||| rtc_uie_task struct rtc_device, char_dev); ||| rtc_uie_timer const struct rtc_class_ops *ops = rtc->ops; ||| clear_uie ||| set_uie if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) ||| rtc_dev_update_irq_enable_emul return -EBUSY; ||| rtc_dev_read ||| rtc_dev_poll file->private_data = rtc; ||| rtc_dev_ioctl ||| rtc_dev_fasync err = ops->open ? ops->open(rtc->dev.parent) : 0; ||| rtc_dev_release if (err == 0) { ||| rtc_dev_prepare spin_lock_irq(&rtc->irq_lock); ||| rtc_dev_add_device rtc->irq_data = 0; ||| rtc_dev_del_device spin_unlock_irq(&rtc->irq_lock); ||| rtc_dev_init ||| rtc_dev_exit return 0; | } | ~ | ~ /* something has gone wrong */ | ~ clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); | ~ return err; | ~ }static long rtc_dev_ioctl(struct file *file, ||| rtc_uie_timer unsigned int cmd, unsigned long arg) ||| clear_uie{ ||| set_uie int err = 0; ||| rtc_dev_update_irq_enable_emul struct rtc_device *rtc = file->private_data; ||| rtc_dev_read const struct rtc_class_ops *ops = rtc->ops; ||| rtc_dev_poll struct rtc_time tm; ||| rtc_dev_ioctl struct rtc_wkalrm alarm; ||| rtc_dev_fasync void __user *uarg = (void __user *) arg; ||| rtc_dev_release ||| rtc_dev_prepare err = mutex_lock_interruptible(&rtc->ops_lock); ||| rtc_dev_add_device if (err) ||| rtc_dev_del_device return err; ||| rtc_dev_init ||| rtc_dev_exit /* check that the calling task has appropriate permissions | * for certain ioctls. doing this check here is useful | ~ * to avoid duplicate code in each driver. | ~ */ | ~ switch (cmd) { case RTC_RD_TIME: ||| clear_uie mutex_unlock(&rtc->ops_lock); ||| set_uie ||| rtc_dev_update_irq_enable_emul err = rtc_read_time(rtc, &tm); ||| rtc_dev_read if (err < 0) ||| rtc_dev_poll return err; ||| rtc_dev_ioctl ||| rtc_dev_fasync if (copy_to_user(uarg, &tm, sizeof(tm))) ||| rtc_dev_release err = -EFAULT; ||| rtc_dev_prepare return err; }}rtc-dev直接获取rtc的ops来操作open,而ioctl调用了interface.c里面的rtc_read_time接口$ vi driver/rtc/interface.cstatic int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) ||| __rtc_read_time{ ||| rtc_read_time int err; ||| rtc_set_time if (!rtc->ops) ||| rtc_set_mmss err = -ENODEV; ||| rtc_read_alarm_internal else if (!rtc->ops->read_time) ||| __rtc_read_alarm err = -EINVAL; ||| rtc_read_alarm else { ||| ___rtc_set_alarm memset(tm, 0, sizeof(struct rtc_time)); ||| __rtc_set_alarm err = rtc->ops->read_time(rtc->dev.parent, tm); ||| rtc_set_alarm } ||| rtc_initialize_alarm return err; ||| rtc_alarm_irq_enable} ||| rtc_update_irq_enable ||| rtc_handle_legacy_irqint rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) ||| rtc_aie_update_irq{ ||| rtc_uie_update_irq int err; ||| rtc_pie_update_irq ||| rtc_update_irq err = mutex_lock_interruptible(&rtc->ops_lock); ||| __rtc_match if (err) ||| rtc_class_open return err; ||| rtc_class_close ||| rtc_irq_register err = __rtc_read_time(rtc, tm); ||| rtc_irq_unregister mutex_unlock(&rtc->ops_lock); ||| rtc_update_hrtimer return err; ||| rtc_irq_set_state}
最终程序是调用rtc-s3c.c里面的read方法static struct platform_driver s3c_rtc_driver = { .probe = s3c_rtc_probe, .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .id_table = s3c_rtc_driver_ids, .driver = { .name = "s3c-rtc", .owner = THIS_MODULE, }, }; static const struct rtc_class_ops s3c_rtcops = { | ~ .read_time = s3c_rtc_gettime, | ~ .set_time = s3c_rtc_settime, | ~ .read_alarm = s3c_rtc_getalarm, | ~ .set_alarm = s3c_rtc_setalarm, | ~ .alarm_irq_enable = s3c_rtc_setaie, | ~ }; static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) ||| wake_en{ ||| s3c_rtc_cpu_type unsigned int have_retried = 0; ||| s3c_rtcops void __iomem *base = s3c_rtc_base; ||| s3c_rtc_driver_ids ||| s3c_rtc_driver retry_get_time: ||| banner rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); ||| s3c_rtc_init rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); ||| s3c_rtc_exit rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); || rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); ||- function rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); ||| s3c_rtc_alarmirq rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC); ||| s3c_rtc_setaie ||| s3c_rtc_gettime /* the only way to work out wether the system was mid-update ||| s3c_rtc_settime * when we read it is to check the second counter, and if it ||| s3c_rtc_getalarm * is zero, then we re-try the entire read ||| s3c_rtc_setalarm */ ||| s3c_rtc_enable ||| s3c_rtc_remove if (rtc_tm->tm_sec == 0 && !have_retried) { ||| s3c_rtc_probe have_retried = 1; ||| s3c_rtc_suspend goto retry_get_time; ||| s3c_rtc_resume } ||| s3c_rtc_init ||| s3c_rtc_exit rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); | rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); | ~ rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); | ~ rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); | ~ rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); | ~ rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); | ~ | ~ rtc_tm->tm_year += 100; | ~ | ~ pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", | ~ 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, | ~ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); | ~ printk("%s() %d-%d-%d %d:%d:%d\n", __FUNCTION__, | ~ 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, | ~ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); | ~ | ~ rtc_tm->tm_mon -= 1; | ~ | ~ return rtc_valid_tm(rtc_tm); | ~ }
参考资料:http://blog.csdn.net/fanqipin/article/details/8089995
http://www.embedu.org/Column/Column468.htm
http://www.linuxidc.com/Linux/2013-08/88964.htm
http://blog.csdn.net/crycheng/article/details/7802502
https://yq.aliyun.com/articles/8396
http://blog.csdn.net/qq_29350001/article/details/51691783
- RTC驱动系统分析
- rtc驱动
- RTC驱动
- rtc驱动
- rtc驱动
- RTC驱动
- RTC驱动
- RTC驱动-2450
- RTC驱动-2450
- RTC驱动移植
- RTC驱动移植
- Linux下rtc驱动
- WINCE的RTC驱动
- linux RTC驱动移植
- WINCE的RTC驱动
- 2410 RTC 驱动解读
- platfrom RTC驱动分析
- 图解linux RTC 驱动
- C# XmlSerializer
- python网络编程之udp
- VS生成事件 Pre/Post-Build Event
- XML解析
- Mac下VSCode导入c语言头文件警告
- RTC驱动系统分析
- 原生js实现下拉级联操作+通过disabled对下拉框不使用
- mapReduce使用分布式缓存机制
- Gamma校正原理及实现
- Linux-基础命令与文件系统认识(一)
- 第一篇blog
- spring学习----线程池
- 使用python进行whois查询并存入数据库
- Jenkins 日程表配置