歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux教程

Linux RTC 驅動模型分析

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 結構
 
  1. struct rtc_device  
  2. {  
  3.     struct device dev;  
  4.     struct module *owner;  
  5.   
  6.     int id;  
  7.     char name[RTC_DEVICE_NAME_SIZE];  
  8.   
  9.     const struct rtc_class_ops *ops;  
  10.     struct mutex ops_lock;  
  11.   
  12.     struct cdev char_dev;  
  13.     unsigned long flags;  
  14.   
  15.     unsigned long irq_data;  
  16.     spinlock_t irq_lock;  
  17.     wait_queue_head_t irq_queue;  
  18.     struct fasync_struct *async_queue;  
  19.   
  20.     struct rtc_task *irq_task;  
  21.     spinlock_t irq_task_lock;  
  22.     int irq_freq;  
  23.     int max_user_freq;  
  24. #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL   
  25.     struct work_struct uie_task;  
  26.     struct timer_list uie_timer;  
  27.     /* Those fields are protected by rtc->irq_lock */  
  28.     unsigned int oldsecs;  
  29.     unsigned int uie_irq_active:1;  
  30.     unsigned int stop_uie_polling:1;  
  31.     unsigned int uie_task_active:1;  
  32.     unsigned int uie_timer_active:1;  
  33. #endif   
  34. };  
        這個結構是RTC驅動程序的基本數據結構,但是他不像其他核心的基本結構一樣,驅動程序以他為參數調用注冊函數注冊到核心。這個結構是由注冊函數返回給驅動程序的。
  2. struct rtc_class_ops 結構
  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 (*irq_set_state)(struct device *, int enabled);  
  12.     int (*irq_set_freq)(struct device *, int freq);  
  13.     int (*read_callback)(struct device *, int data);  
  14.     int (*alarm_irq_enable)(struct device *, unsigned int enabled);  
  15.     int (*update_irq_enable)(struct device *, unsigned int enabled);  
  16. };  
        這個結構是RTC驅動程序要實現的基本操作函數,注意這裡的操作不是文件操作。驅動程序通過初始化這樣一個結構,將自己實現的函數與RTC核心聯系起來。這裡面的大部分函數都要驅動程序來實現。而且這些函數都是操作底層硬件的,屬於最底層的函數。
  3. struct rtc_time 結構
 
  1. struct rtc_time {  
  2.     int tm_sec;  
  3.     int tm_min;  
  4.     int tm_hour;  
  5.     int tm_mday;  
  6.     int tm_mon;  
  7.     int tm_year;  
  8.     int tm_wday;  
  9.     int tm_yday;  
  10.     int tm_isdst;  
  11. };  
        代表了時間與日期,從RTC設備讀回的時間和日期就保存在這個結構體中
三. class.c 
  1. 模塊初始化函數:rtc_init  
  1. static int __init rtc_init(void)  
  2. {  
  3.     rtc_class = class_create(THIS_MODULE, "rtc");  
  4.     if (IS_ERR(rtc_class)) {  
  5.         printk(KERN_ERR "%s: couldn't create class\n", __FILE__);  
  6.         return PTR_ERR(rtc_class);  
  7.     }  
  8.     rtc_class->suspend = rtc_suspend;  
  9.     rtc_class->resume = rtc_resume;  
  10.     rtc_dev_init();  
  11.     rtc_sysfs_init(rtc_class);  
  12.     return 0;  
  13. }  
        rtc_init首先調用class_create創建了一個類--rtc。我們知道類是一個設備的高層視圖,他抽象出了底層的實現細節。類的作用就是向用戶空間提供設備的信息,驅動程序不需要直接處理類。然後初始化類結構的相應成員,rtc_suspend,rtc_resume這兩個函數也是在class.c中實現的。接下來調用rtc_dev_init(),這個函數為RTC設備動態分配設備號,保存在rtc_devt中。最後調用rtc_sysfs_init,初始化rtc_class的屬性。
  2. 為底層驅動提供接口:rtc_device_register,rtc_device_unregister
 
  1. struct rtc_device *rtc_device_register(const char *name, struct device *dev,  
  2.                     const struct rtc_class_ops *ops,  
  3.                     struct module *owner)  
  4. {  
  5.     struct rtc_device *rtc;  
  6.     int id, err;  
  7.   
  8.     if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {  
  9.         err = -ENOMEM;  
  10.         goto exit;  
  11.     }  
  12.   
  13.     mutex_lock(&idr_lock);  
  14.     err = idr_get_new(&rtc_idr, NULL, &id);  
  15.     mutex_unlock(&idr_lock);  
  16. /*--------------------(1)---------------------*/  
  17.     if (err < 0)  
  18.         goto exit;  
  19.     id = id & MAX_ID_MASK;  
  20.     rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);  
  21.     if (rtc == NULL) {  
  22.         err = -ENOMEM;  
  23.         goto exit_idr;  
  24.     }  
  25.   
  26.     rtc->id = id;  
  27.     rtc->ops = ops;  
  28.     rtc->owner = owner;  
  29.     rtc->max_user_freq = 64;  
  30.     rtc->dev.parent = dev;  
  31.     rtc->dev.class = rtc_class;  
  32.     rtc->dev.release = rtc_device_release;  
  33.   
  34.     mutex_init(&rtc->ops_lock);  
  35.     spin_lock_init(&rtc->irq_lock);  
  36.     spin_lock_init(&rtc->irq_task_lock);  
  37.     init_waitqueue_head(&rtc->irq_queue);  
  38.   
  39.     strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);  
  40.     dev_set_name(&rtc->dev, "rtc%d", id);  
  41. /*-------------------(2)--------------------*/  
  42.     rtc_dev_prepare(rtc);  
  43.   
  44.   
  45.     err = device_register(&rtc->dev);  
  46.     if (err)  
  47.         goto exit_kfree;  
  48. /*-------------------(3)--------------------*/  
  49.     rtc_dev_add_device(rtc);  
  50.     rtc_sysfs_add_device(rtc);  
  51.     rtc_proc_add_device(rtc);  
  52.   
  53.     dev_info(dev, "rtc core: registered %s as %s\n",  
  54.             rtc->name, dev_name(&rtc->dev));  
  55. /*-------------------(4)--------------------*/  
  56.     return rtc;  
  57.   
  58. exit_kfree:  
  59.     kfree(rtc);  
  60. exit_idr:  
  61.     mutex_lock(&idr_lock);  
  62.     idr_remove(&rtc_idr, id);  
  63.     mutex_unlock(&idr_lock);  
  64. exit:  
  65.     dev_err(dev, "rtc core: unable to register %s, err = %d\n",  
  66.             name, err);  
  67.     return ERR_PTR(err);  
  68. }  
    (1):處理一個idr的結構,idr在linux內核中指的就是整數ID管理機制,從本質上來說,idr是一種將整數ID號和特定指針關聯在一起的機制。這個機制最早是在2003年2月加入內核的,當時是作為POSIX定時器的一個補丁。現在,在內核的很多地方都可以找到idr的身影。詳細實現請參照相關內核代碼。這裡從內核中獲取一個idr結構,並與id相關聯。
    (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文件系統接口。
Copyright © Linux教程網 All Rights Reserved