一. 驅動模型結構
與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核心代碼的組織方式也值得學習,不同功能的代碼放在不同的文件中,簡單明瞭。
原文鏈接:http://blog.csdn.net/yaozhenguo2006/article/details/6824970