歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux RTC 驅動模型分析

Linux RTC 驅動模型分析

日期:2017/2/28 16:05:42   编辑:Linux教程
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. int id;
  6. char name[RTC_DEVICE_NAME_SIZE];
  7. const struct rtc_class_ops *ops;
  8. struct mutex ops_lock;
  9. struct cdev char_dev;
  10. unsigned long flags;
  11. unsigned long irq_data;
  12. spinlock_t irq_lock;
  13. wait_queue_head_t irq_queue;
  14. struct fasync_struct *async_queue;
  15. struct rtc_task *irq_task;
  16. spinlock_t irq_task_lock;
  17. int irq_freq;
  18. int max_user_freq;
  19. #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
  20. struct work_struct uie_task;
  21. struct timer_list uie_timer;
  22. /* Those fields are protected by rtc->irq_lock */
  23. unsigned int oldsecs;
  24. unsigned int uie_irq_active:1;
  25. unsigned int stop_uie_polling:1;
  26. unsigned int uie_task_active:1;
  27. unsigned int uie_timer_active:1;
  28. #endif
  29. };
這個結構是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. if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
  8. err = -ENOMEM;
  9. goto exit;
  10. }
  11. mutex_lock(&idr_lock);
  12. err = idr_get_new(&rtc_idr, NULL, &id);
  13. mutex_unlock(&idr_lock);
  14. /*--------------------(1)---------------------*/
  15. if (err < 0)
  16. goto exit;
  17. id = id & MAX_ID_MASK;
  18. rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
  19. if (rtc == NULL) {
  20. err = -ENOMEM;
  21. goto exit_idr;
  22. }
  23. rtc->id = id;
  24. rtc->ops = ops;
  25. rtc->owner = owner;
  26. rtc->max_user_freq = 64;
  27. rtc->dev.parent = dev;
  28. rtc->dev.class = rtc_class;
  29. rtc->dev.release = rtc_device_release;
  30. mutex_init(&rtc->ops_lock);
  31. spin_lock_init(&rtc->irq_lock);
  32. spin_lock_init(&rtc->irq_task_lock);
  33. init_waitqueue_head(&rtc->irq_queue);
  34. strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
  35. dev_set_name(&rtc->dev, "rtc%d", id);
  36. /*-------------------(2)--------------------*/
  37. rtc_dev_prepare(rtc);
  38. err = device_register(&rtc->dev);
  39. if (err)
  40. goto exit_kfree;
  41. /*-------------------(3)--------------------*/
  42. rtc_dev_add_device(rtc);
  43. rtc_sysfs_add_device(rtc);
  44. rtc_proc_add_device(rtc);
  45. dev_info(dev, "rtc core: registered %s as %s\n",
  46. rtc->name, dev_name(&rtc->dev));
  47. /*-------------------(4)--------------------*/
  48. return rtc;
  49. exit_kfree:
  50. kfree(rtc);
  51. exit_idr:
  52. mutex_lock(&idr_lock);
  53. idr_remove(&rtc_idr, id);
  54. mutex_unlock(&idr_lock);
  55. exit:
  56. dev_err(dev, "rtc core: unable to register %s, err = %d\n",
  57. name, err);
  58. return ERR_PTR(err);
  59. }
(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