Linux時間系統之RTC時間

1、概述

不知道有沒有「時間系統」的說法,咱們暫且把它做爲Linux中和時間相關的內容的統稱吧。
Linux時間有兩個,系統時間(Wall Time),RTC時間
系統時間(WT):由Linux系統軟件維持的時間,好比command date
[java] view plain copyjava

  1. $ date  
  2. 2017年 02月 25日 星期六 16:58:10 CST  


獲取到的就是系統時間。
RTC時間:這個時間來自咱們設備上的RTC芯片,經過command hwclock 能夠讀取:
[java] view plain copypython

  1. # hwclock -r  --> root權限才能夠運行  
  2. 20170225日 星期六 170157秒  -0.906462 seconds  


咱們經過man查看hwclock的介紹:
[python] view plain copylinux

  1. hwclock - query or set the hardware clock (RTC)  


接下來,經過代碼看下二者的關係。app

2、WT時間和RTC時間

WT時間來自於RTC時間,流程是:
[python] view plain copy框架

  1. 上電-->RTC驅動加載-->從RTC同步時間到WT時間  


上代碼:
[objc] view plain copyasync

  1. hctosys.c (drivers\rtc)  
  2. static int __init rtc_hctosys(void)  
  3. {  
  4.     struct timespec tv = {  
  5.         .tv_nsec = NSEC_PER_SEC >> 1,  
  6.     };  
  7.       
  8.     err = rtc_read_time(rtc, &tm);  
  9.     err = do_settimeofday(&tv);  
  10.     dev_info(rtc->dev.parent,  
  11.         "setting system clock to "  
  12.         "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",  
  13.         tm.tm_year + 1900tm.tm_mon + 1tm.tm_mday,  
  14.         tm.tm_hourtm.tm_mintm.tm_sec,  
  15.         (unsigned int) tv.tv_sec);  
  16. }  
  17. late_initcall(rtc_hctosys);  


late_initcall說明系統在啓動流程的後面纔會調用該函數去同步時間。關於late_initcall可見最後PS。
接下來從底層到上層進行梳理。函數

3、RTC時間框架

框架如圖:
post

:圖中hwclock.c筆誤,實爲hctosys.cui

  1. Hardware:提供時間信息(time&alarm),經過必定的接口(好比I2C)和
  2. RTC Driver:進行交互。Driver完成硬件的訪問功能,提供訪問接口,以驅動的形式駐留在系統。註冊方式由
  3. class.c:文件提供。驅動註冊成功後會構建rtc_device結構體表徵的rtc設備,並把rtc芯片的操做方式存放到rtc設備的ops成員中
  4. interface.c:文件屏蔽硬件相關的細節,向上提供統一的獲取/設置時間或Alarm的接口
  5. rtc-lib.c:文件提供通用的時間操做函數,如rtc_time_to_tm、rtc_valid_tm等
  6. rtc-dev.c:文件在/dev/目錄下建立設備節點供應用層訪問,如open、read、ioctl等,訪問方式填充到file_operations結構體中
  7. hctosys.c/rtc-sys.c/rtc-proc.c:看名字就知道其做用


接下來從驅動一層一層看下spa

4、RTC驅動

驅動主要工做是填充rtc_class_ops結構體,結構體描述了RTC芯片可以提供的全部操做方式:
[objc] view plain copy

  1. struct rtc_class_ops {  
  2.     int (*open)(struct device *);  
  3.     void (*release)(struct device *);  
  4.     int (*ioctl)(struct device *, unsigned int, unsigned long);  
  5.     int (*read_time)(struct device *, struct rtc_time *);  
  6.     int (*set_time)(struct device *, struct rtc_time *);  
  7.     int (*read_alarm)(struct device *, struct rtc_wkalrm *);  
  8.     int (*set_alarm)(struct device *, struct rtc_wkalrm *);  
  9.     int (*proc)(struct device *, struct seq_file *);  
  10.     int (*set_mmss)(struct device *, unsigned long secs);  
  11.     int (*read_callback)(struct device *, int data);  
  12.     int (*alarm_irq_enable)(struct device *, unsigned int enabled);  
  13. };  



實現:
[objc] view plain copy

  1. static const struct rtc_class_ops test_rtc_ops = {  
  2.     .read_time    = test_rtc_read_time,  
  3.     .set_time    = test_rtc_set_time,  
  4.     .read_alarm    = test_rtc_read_alarm,  
  5.     .set_alarm    = test_rtc_set_alarm,  
  6.     .ioctl         = test_rtc_ioctl,  
  7.     .proc        = test_rtc_proc  
  8. };  


註冊:
[objc] view plain copy

  1. rtc_device_register(name, dev, &test_rtc_ops, THIS_MODULE);  

 

成功的話log:

[python] view plain copy

  1. [    1.531114] test_rtc_init Enter.  
  2. [    1.531126] bus: 'i2c': add driver test_rtc  
  3. [    1.531189] test_rtc_probe Enter.  
  4. [    1.533990] test_rtc_read_time Enter.  
  5. [    1.534527] test_rtc_read_time Exit.  
  6. [    1.534537] test_rtc_read_alarm Enter.  
  7. [    1.534546] test_rtc_read_alarm Exit.  
  8. [    1.534556] test_rtc_read_time Enter.  
  9. [    1.535083] test_rtc_read_time Exit.  
  10. [    1.535237] test_rtc 2-0051: rtc core: registered test_rtc as rtc0  
  11. [    1.535250] test_rtc_probe Exit.  

 

 

5、class.c和RTC驅動註冊

class.c文件在RTC驅動註冊以前開始獲得運行:
[objc] view plain copy

  1. static int __init rtc_init(void)  
  2. {  
  3.     rtc_class = class_create(THIS_MODULE, "rtc");  
  4.     rtc_class->suspend = rtc_suspend;  
  5.     rtc_class->resume = rtc_resume;  
  6.     rtc_dev_init();  
  7.     rtc_sysfs_init(rtc_class);  
  8.     return 0;  
  9. }  
  10. subsys_initcall(rtc_init);  


完成:

  • 一、建立名爲rtc的class
  • 二、提供PM相關接口suspend/resume
  • 三、rtc_dev_init():動態申請/dev/rtcN的設備號
  • 四、rtc_sysfs_init():rtc類具備的device_attribute屬性


接着看RTC驅動註冊:
[objc] view plain copy

  1. struct rtc_device *rtc_device_register(const charchar *name, struct device *dev,  
  2.                     const struct rtc_class_ops *ops,  
  3.                     struct module *owner)  
  4. {  
  5.     struct rtc_device *rtc;  
  6.     struct rtc_wkalrm alrm;  
  7.     int id, err;  
  8.       
  9.     // 一、Linux支持多個RTC設備,因此須要爲每個設備分配一個ID  
  10.     // 對應與/dev/rtc0,/dev/rtc1,,,/dev/rtcN  
  11.     id = ida_simple_get(&rtc_ida, 00, GFP_KERNEL);  
  12.   
  13.     // 二、建立rtc_device設備(對象)並執行初始化  
  14.     rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);  
  15.     rtc->id = id;  
  16.     rtc->ops = ops; // 2.1 對應RTC驅動填充的test_rtc_ops  
  17.     rtc->owner = owner;  
  18.     rtc->irq_freq = 1;  
  19.     rtc->max_user_freq = 64;  
  20.     rtc->dev.parent = dev;  
  21.     rtc->dev.class = rtc_class;// 2.2 rtc_init()建立的rtc_class  
  22.     rtc->dev.release = rtc_device_release;  
  23.   
  24.     // 2.3 rtc設備中相關鎖、等待隊列的初始化  
  25.     mutex_init(&rtc->ops_lock);  
  26.     spin_lock_init(&rtc->irq_lock);  
  27.     spin_lock_init(&rtc->irq_task_lock);  
  28.     init_waitqueue_head(&rtc->irq_queue);  
  29.   
  30.     // 2.4 Init timerqueue:咱們都知道,手機等都是能夠設置多個鬧鐘的  
  31.     timerqueue_init_head(&rtc->timerqueue);  
  32.     INIT_WORK(&rtc->irqwork, rtc_timer_do_work);  
  33.     // 2.5 Init aie timer:alarm interrupt enable,RTC鬧鐘中斷  
  34.     rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (voidvoid *)rtc);  
  35.     // 2.6 Init uie timer:update interrupt,RTC更新中斷  
  36.     rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (voidvoid *)rtc);  
  37.     /* Init pie timer:periodic interrupt,RTC週期性中斷 */  
  38.     hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  
  39.     rtc->pie_timer.function = rtc_pie_update_irq;  
  40.     rtc->pie_enabled = 0;  
  41.   
  42.     /* Check to see if there is an ALARM already set in hw */  
  43.     err = __rtc_read_alarm(rtc, &alrm);  
  44.   
  45.     // 三、若是RTC芯片中設置了有效的Alarm,則初始化:加入到rtc->timerqueue隊列中  
  46.     if (!err && !rtc_valid_tm(&alrm.time))  
  47.         rtc_initialize_alarm(rtc, &alrm);  
  48.   
  49.     // 四、根據name參數設置rtc的name域  
  50.     strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);  
  51.     // 五、設置rtc的dev成員中的name域  
  52.     dev_set_name(&rtc->dev, "rtc%d"id);  
  53.   
  54.     // 六、/dev/rtc0的rtc做爲字符設備進行初始化  
  55.     // rtc_dev_prepare-->cdev_init(&rtc->char_dev, &rtc_dev_fops);  
  56.     rtc_dev_prepare(rtc);  
  57.   
  58.     // 七、添加rtc設備到系統  
  59.     err = device_register(&rtc->dev);  
  60.   
  61.     // 八、rtc設備做爲字符設備添加到系統  
  62.     // rtc_dev_add_devicec-->dev_add(&rtc->char_dev, rtc->dev.devt, 1)  
  63.     // 而後就存在/dev/rtc0了  
  64.     rtc_dev_add_device(rtc);  
  65.     rtc_sysfs_add_device(rtc);  
  66.     // 九、/proc/rtc  
  67.     rtc_proc_add_device(rtc);  
  68.   
  69.     dev_info(dev, "rtc core: registered %s as %s\n",  
  70.             rtc->name, dev_name(&rtc->dev));  
  71.   
  72.     return rtc;  
  73. }  


有了/dev/rtc0後,應用層就能夠經過open/read/ioctl操做RTC設備了,對應與內核的file_operations:
[objc] view plain copy

  1. static const struct file_operations rtc_dev_fops = {  
  2.     .owner        = THIS_MODULE,  
  3.     .llseek        = no_llseek,  
  4.     .read        = rtc_dev_read,  
  5.     .poll        = rtc_dev_poll,  
  6.     .unlocked_ioctl    = rtc_dev_ioctl,  
  7.     .open        = rtc_dev_open,  
  8.     .release    = rtc_dev_release,  
  9.     .fasync        = rtc_dev_fasync,  
  10. };  

 

6、硬件抽象層

硬件抽象,即屏蔽具體的硬件細節,爲上層用戶提供統一的調用接口,使用者無需關心這些接口是怎麼實現的。
以RTC訪問爲例,抽象的實現位於interface.c文件,其實現基於class.c中建立的rtc_device設備。
實現原理,以rtc_set_time爲例:
[objc] view plain copy

  1. int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)  
  2. {  
  3.     int err;  
  4.     // 一、參數檢測  
  5.     err = rtc_valid_tm(tm);  
  6.   
  7.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  8.     if (err)  
  9.         return err;  
  10.   
  11.     // 二、調用rtc_device中ops結構體的函數指針  
  12.     // ops結構體的函數指針已經在RTC驅動中被賦值  
  13.     if (!rtc->ops)  
  14.         err = -ENODEV;  
  15.     else if (rtc->ops->set_time)  
  16.         err = rtc->ops->set_time(rtc->dev.parenttm);  
  17.     mutex_unlock(&rtc->ops_lock);  
  18.     /* A timer might have just expired */  
  19.     schedule_work(&rtc->irqwork);  
  20.     return err;  
  21. }  

 

7、rtc在文件系統中的呈現

一、rtc在sysfs

以前曾創建過名爲rtc的class:

 

[python] view plain copy

  1. rtc_class = class_create(THIS_MODULE, "rtc");  

 

查看之:

[python] view plain copy

  1. # ls /sys/class/rtc/                                      
  2. rtc0  
  3. # ls -l /sys/class/rtc/                                   
  4. lrwxrwxrwx root     root              2014-01-02 16:51 rtc0 -> ../../devices/ff660000.i2c/i2c-2/2-0051/rtc/rtc0  


咱們系統中只有一個RTC,因此編號爲rtc0。
同時發現rtc0文件爲指向/sys/devices/ff660000.i2c/i2c-2/2-0051/rtc/rtc0的符號連接,RTC芯片是I2C接口,因此rtc0掛載在I2C的總線上,總線控制器地址ff660000,控制器編號爲2,RTC芯片做爲slave端地址爲0x51。
rtc0設備屬性

 

 

[objc] view plain copy

  1. void __init rtc_sysfs_init(struct classclass *rtc_class)  
  2. {  
  3.     rtc_class->dev_attrs = rtc_attrs;  
  4. }  
  5. static struct device_attribute rtc_attrs[] = {  
  6.     __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),  
  7.     __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),  
  8.     __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),  
  9.     __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),  
  10.     __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,  
  11.             rtc_sysfs_set_max_user_freq),  
  12.     __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),  
  13.     { },  
  14. };  

 


查看之:

 

[python] view plain copy

  1. ls -l /sys/class/rtc/rtc0/                              
  2. -r--r--r-- root     root         4096 2014-01-02 16:51 date  
  3. -r--r--r-- root     root         4096 2014-01-02 16:51 dev  
  4. lrwxrwxrwx root     root              2014-01-02 16:51 device -> ../../../2-0051  
  5. -r--r--r-- root     root         4096 2014-01-02 16:51 hctosys  
  6. -rw-r--r-- root     root         4096 2014-01-02 16:51 max_user_freq  
  7. -r--r--r-- root     root         4096 2014-01-02 16:51 name  
  8. drwxr-xr-x root     root              2014-01-02 16:48 power  
  9. -r--r--r-- root     root         4096 2014-01-02 16:51 since_epoch  
  10. lrwxrwxrwx root     root              2014-01-02 16:51 subsystem -> ../../../../../../class/rtc  
  11. -r--r--r-- root     root         4096 2014-01-02 16:51 time  
  12. -rw-r--r-- root     root         4096 2014-01-02 16:48 uevent  
  13. -rw-r--r-- root     root         4096 2014-01-02 16:51 wakealarm  

 

二、rtc在proc

以前曾rtc0設備加入到了/proc

 

[python] view plain copy

  1. rtc_device_register  
  2. --->rtc_proc_add_device(rtc);  
  3.   
  4. void rtc_proc_add_device(struct rtc_device *rtc)  
  5. {  
  6.     if (is_rtc_hctosys(rtc))  
  7.         proc_create_data("driver/rtc"0, NULL, &rtc_proc_fops, rtc);  
  8. }  


查看之:

 

 

[python] view plain copy

  1. # cat /proc/driver/rtc                                         
  2. rtc_time    : 17:19:53  
  3. rtc_date    : 2014-01-02  
  4. alrm_time   : 00:00:00  
  5. alrm_date   : 1970-01-01  
  6. alarm_IRQ   : no  
  7. alrm_pending    : no  
  8. update IRQ enabled  : no  
  9. periodic IRQ enabled    : no  
  10. periodic IRQ frequency  : 1  
  11. max user IRQ frequency  : 64  
  12. 24hr        : yes  


信息來源:

 

 

[python] view plain copy

  1. rtc_proc_fops  
  2.     -->rtc_proc_open  
  3.         -->rtc_proc_show  

 

 



PS
關於late_initcall

kernel中__init類型函數都位於.init.text段中,對應的在.initcall.init段中保存相應的函數指針。系統在啓動過程當中,根據定義在段中的等級值(0~7)從低到高依次執行。定義:

 

[objc] view plain copy

  1. init.h (include\linux)  
  2.   
  3. #define pure_initcall(fn)        __define_initcall(fn, 0)  
  4.   
  5. #define core_initcall(fn)        __define_initcall(fn, 1)  
  6. #define core_initcall_sync(fn)        __define_initcall(fn, 1s)  
  7. #define postcore_initcall(fn)        __define_initcall(fn, 2)  
  8. #define postcore_initcall_sync(fn)    __define_initcall(fn, 2s)  
  9. #define arch_initcall(fn)        __define_initcall(fn, 3)  
  10. #define arch_initcall_sync(fn)        __define_initcall(fn, 3s)  
  11. #define subsys_initcall(fn)        __define_initcall(fn, 4)  
  12. #define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)  
  13. #define fs_initcall(fn)            __define_initcall(fn, 5)  
  14. #define fs_initcall_sync(fn)        __define_initcall(fn, 5s)  
  15. #define rootfs_initcall(fn)        __define_initcall(fn, rootfs)  
  16. #define device_initcall(fn)        __define_initcall(fn, 6)  
  17. #define device_initcall_sync(fn)    __define_initcall(fn, 6s)  
  18. #define late_initcall(fn)        __define_initcall(fn, 7)  
  19. #define late_initcall_sync(fn)        __define_initcall(fn, 7s)  

 

http://blog.csdn.net/u013686019/article/details/57126940

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。 https://blog.csdn.net/u013686019/article/details/57126940

我的分類: Linux內核

相關文章
相關標籤/搜索