|
rtc-sysfs.c這個部分主要是有關(guān)sysfs的操作。在rtc_device_register函數(shù)中,rtc_sysfs_add_device(rtc);完成sys的操作。 void rtc_sysfs_add_device(struct rtc_device *rtc) { int err; /* not all RTCs support both alarms and wakeup */ if (!rtc_does_wakealarm(rtc)) return; err = device_create_file(&rtc->dev, &dev_attr_wakealarm); if (err) dev_err(rtc->dev.parent, "failed to create alarm attribute, %d\n", err); } rtc_init函數(shù)中初始化sys。 void __init rtc_sysfs_init(struct class *rtc_class) { rtc_class->dev_attrs = rtc_attrs; } rtc_attrs設(shè)備屬性組如下: 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的模塊初始化函數(shù)中,由rtc_sysfs_init函數(shù)賦值給 rtc_class->dev_attrs的, 以后屬于這個類的設(shè)備都會有這些屬性。但是我們知道要想一個設(shè)備結(jié)構(gòu)擁有一種屬性,必須調(diào)用device_create_file,這樣才會使這個屬性出現(xiàn)在sysfs相關(guān)設(shè)備目錄里。但是在這里的代碼中只是給這個類的dev_attrs域賦值了這個屬性組指針,而沒有調(diào)用 device_create_file。我原來以為是在rtc_device_resgister函數(shù)中由rtc_sysfs_add_device完成這個工作,但是這個函數(shù)只是給設(shè)備添加了鬧鐘屬性,并沒有處理這個屬性組。最后發(fā)現(xiàn)這個工作是由device_register來完成的: device_register調(diào)用device_add device_add調(diào)用device_add_attrs device_add_attrs調(diào)用device_add_attributes device_add_attributes調(diào)用device_create_file來完成設(shè)備的屬性設(shè)置。 設(shè)置完屬性后,在/sys/class/rtc/rtc(n)的目錄下就會出現(xiàn)name、date、time、since_epoch、max_user_freq和hctosys等文件,用戶讀這些文件的時候就會調(diào)用相應(yīng)的函數(shù)。如讀取name文件,就會調(diào)用rtc_sysfs_show_name函數(shù),這個函數(shù)也是在rtc-sysfs.c中實現(xiàn)的,作用是讀取并顯示時間。 /** NOTE: RTC times displayed in sysfs use the RTC's timezone. That's * ideally UTC. However, PCs that also boot to MS-Windows normally use * the local time and change to match daylight savings time. That affects * attributes including date, time, since_epoch, and wakealarm. */ static ssize_t rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", to_rtc_device(dev)->name); } static ssize_t rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); if (retval == 0) { retval = sprintf(buf, "%04d-%02d-%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); } return retval; } static ssize_t rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); if (retval == 0) { retval = sprintf(buf, "%02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); } return retval; } static ssize_t rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t retval; struct rtc_time tm; retval = rtc_read_time(to_rtc_device(dev), &tm); if (retval == 0) { unsigned long time; rtc_tm_to_time(&tm, &time); retval = sprintf(buf, "%lu\n", time); } return retval; } static ssize_t rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq); } static ssize_t rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct rtc_device *rtc = to_rtc_device(dev); unsigned long val = simple_strtoul(buf, NULL, 0); if (val >= 4096 || val == 0) return -EINVAL; rtc->max_user_freq = (int)val; return n; } static ssize_t rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, char *buf) { #ifdef CONFIG_RTC_HCTOSYS_DEVICE if (rtc_hctosys_ret == 0 && strcmp(dev_name(&to_rtc_device(dev)->dev), CONFIG_RTC_HCTOSYS_DEVICE) == 0) return sprintf(buf, "1\n"); else #endif return sprintf(buf, "0\n"); } rtc-proc.c這個文件提供RTC的proc文件系統(tǒng)接口。proc文件系統(tǒng)是軟件創(chuàng)建的文件系統(tǒng),內(nèi)核通過他向外界導(dǎo)出信息。 在第一份部分的rtc_device_register函數(shù)中調(diào)用rtc_proc_add_device(rtc);,函數(shù)主要功能就是增加proc文件系統(tǒng)的內(nèi)容,該函數(shù)具體內(nèi)容如下: void rtc_proc_add_device(struct rtc_device *rtc) { if (rtc->id == 0) proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); //rtc_proc_fops在下面講述 } 他主要調(diào)用了proc_create_data。proc_create_data完成創(chuàng)建文件節(jié)點的作用,并將文件的操作函數(shù)與節(jié)點聯(lián)系起來。調(diào)用這個函數(shù)后,在/proc/driver目錄下就會有一個文件rtc,應(yīng)用程序打開這個文件就會調(diào)用rtc_proc_open函數(shù)。 //以下函數(shù)在rtc_device_unregister()中調(diào)用 void rtc_proc_del_device(struct rtc_device *rtc) { if (rtc->id == 0) remove_proc_entry("driver/rtc", NULL); } 下面的每一個文件都綁定一個函數(shù),當(dāng)用戶讀取這個文件的時候,這些函數(shù)會向文件寫入信息: static const struct file_operations rtc_proc_fops = { .open = rtc_proc_open, .read = seq_read, .llseek = seq_lseek, .release = rtc_proc_release, }; static int rtc_proc_open(struct inode *inode, struct file *file) { int ret; struct rtc_device *rtc = PDE(inode)->data; if (!try_module_get(THIS_MODULE)) return -ENODEV; ret = single_open(file, rtc_proc_show, rtc); //rtc_proc_show接下來講述 if (ret) module_put(THIS_MODULE); return ret; } 我們知道一個proc的文件必須與一個操作函數(shù)組成一個proc入口項,這個文件才能正常工作。這個函數(shù)最主要作用就是調(diào)用single_open,創(chuàng)建一個proc文件入口項,使其操作函數(shù)是rtc_proc_show,并初始化seq_file接口。rtc_proc_show函數(shù)如下定義: 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, "update IRQ enabled\t: %s\n", (rtc->uie_rtctimer.enabled) ? "yes" : "no"); seq_printf(seq, "periodic IRQ enabled\t: %s\n", (rtc->pie_enabled) ? "yes" : "no"); seq_printf(seq, "periodic IRQ frequency\t: %d\n", rtc->irq_freq); seq_printf(seq, "max user IRQ frequency\t: %d\n", rtc->max_user_freq); } seq_printf(seq, "24hr\t\t: yes\n"); if (ops->proc) ops->proc(rtc->dev.parent, seq); return 0; } 這個函數(shù)就是最后給用戶顯示信息的函數(shù)了,可以看出他通過調(diào)用rtc_deivce中的操作函數(shù)讀取時間、日期和一些其他的信息顯示給用戶。 RTC核心使底層硬件對用戶來說是透明的,并且減少了編寫驅(qū)動程序的工作量。RTC新的驅(qū)動接口提供了更多的功能,使系統(tǒng)可以同時存在多個RTC。 /dev,sysfs,proc這三種機(jī)制的實現(xiàn)使得應(yīng)用程序能靈活的使用RTC。RTC核心代碼的組織方式值得學(xué)習(xí),不同功能的代碼放在不同的文件中,簡單明了。 |
|
|