當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > linux下的rtc機(jī)制
Linux的RTC驅(qū)動(dòng)相對(duì)還是比較簡(jiǎn)單的,可以將它作為一個(gè)普通的字符型設(shè)備,或者一個(gè)misc設(shè)備,也可以是一個(gè)平臺(tái)設(shè)備,這都沒(méi)有關(guān)系,主要還是對(duì)rtc_ops這個(gè)文件操作結(jié)構(gòu)體中的成員填充,這里主要涉及到兩個(gè)方面比較重要:
1. 在Linux中有硬件時(shí)鐘與系統(tǒng)時(shí)鐘等兩種時(shí)鐘。硬件時(shí)鐘是指主機(jī)板上的時(shí)鐘設(shè)備,也就是通?稍贐IOS畫(huà)面設(shè)定的時(shí)鐘。系統(tǒng)時(shí)鐘則是指kernel中的時(shí)鐘。當(dāng)Linux啟動(dòng)時(shí),系統(tǒng)時(shí)鐘會(huì)去讀取硬件時(shí)鐘的設(shè)定,之后系統(tǒng)時(shí)鐘即獨(dú)立運(yùn)作。所有Linux相關(guān)指令與函數(shù)都是讀取系統(tǒng)時(shí)鐘的設(shè)定。
系統(tǒng)時(shí)鐘的設(shè)定就是我們常用的date命令,而我們寫(xiě)的RTC驅(qū)動(dòng)就是為硬件時(shí)鐘服務(wù)的,它有屬于自己的命令hwclock,因此使用date命令是不可能調(diào)用到我們的驅(qū)動(dòng)的(在這點(diǎn)上開(kāi)始把我郁悶到了,寫(xiě)完驅(qū)動(dòng)之后,傻傻的用date指令來(lái)測(cè)試,當(dāng)然結(jié)果是什么都沒(méi)有),我們可以通過(guò)hwclock的一些指令來(lái)實(shí)現(xiàn)更新rtc時(shí)鐘——也就是系統(tǒng)時(shí)鐘和硬件時(shí)鐘的交互。
hwclock –r顯示硬件時(shí)鐘與日期
hwclock –s將系統(tǒng)時(shí)鐘調(diào)整為與目前的硬件時(shí)鐘一致。
hwclock –w將硬件時(shí)鐘調(diào)整為與目前的系統(tǒng)時(shí)鐘一致。
用戶(hù)輸入 hwclck -s // 這個(gè)命令是 把硬件的時(shí)鐘更新到系統(tǒng)中去
這個(gè)命令會(huì)調(diào)用busybox源碼中的hwclock.c,在目錄util-linux/hwclock.c 中的 ->int hwclock_main(int argc UNUSED_PARAM, char **argv)
后面的參數(shù)會(huì)以argv形式接收
opt = getopt32(argv, "lurswtf:", &rtcname);
會(huì)把a(bǔ)rgv的參數(shù)進(jìn)行轉(zhuǎn)換 l -> 0x01
u -> 0x02
r -> 0x04
s -> 0x08
w -> 0x10
t -> 0x20
f -> 0x40
: -> 0x80
#define HWCLOCK_OPT_LOCALTIME 0x01
#define HWCLOCK_OPT_UTC 0x02
#define HWCLOCK_OPT_SHOW 0x04 // 等價(jià) hwclock –r 顯示硬件的時(shí)間
#define HWCLOCK_OPT_HCTOSYS 0x08 // 等價(jià) hwclock –s 硬件時(shí)間更新到系統(tǒng)時(shí)間
#define HWCLOCK_OPT_SYSTOHC 0x10 // 等價(jià) hwclock –w 系統(tǒng)時(shí)間更新到硬件時(shí)間
#define HWCLOCK_OPT_SYSTZ 0x20
#define HWCLOCK_OPT_RTCFILE 0x40
if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) // 判斷是是否是 hwclock –l -u
utc = (opt & HWCLOCK_OPT_UTC);
if (opt & HWCLOCK_OPT_HCTOSYS) // 判斷是是否是 hwclock –s
to_sys_clock(&rtcname, utc);
else if (opt & HWCLOCK_OPT_SYSTOHC) // 判斷是是否是 hwclock –w
from_sys_clock(&rtcname, utc);
else
/* default HWCLOCK_OPT_SHOW */ // 默認(rèn)是 hwclock –r
show_clock(&rtcname, utc);
hwclock –s ->to_sys_clock-> read_rtc(pp_rtcname, NULL, utc); // 讀rtc的時(shí)間
-> fd = rtc_xopen(pp_rtcname, O_RDONLY)
*default_rtc = "/dev/rtc";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/rtc0";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/misc/rtc";
// 會(huì)打開(kāi)這幾個(gè)默認(rèn)的文件
-> rtc_read_tm(&tm_time, fd);
-> xioctl(fd, RTC_RD_TIME, ptm);
// 讀系統(tǒng)的時(shí)間
-> settimeofday(&tv, &tz)) // 設(shè)置系統(tǒng)的時(shí)間
hwclock –w ->from_sys_clock(&rtcname, utc); -> rtc = rtc_xopen(pp_rtcname, O_WRONLY);
*default_rtc = "/dev/rtc";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/rtc0";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/misc/rtc";
// 會(huì)打開(kāi)這幾個(gè)默認(rèn)的文件
-> gettimeofday(&tv, NULL); // 獲得系統(tǒng)時(shí)間
-> xioctl(rtc, RTC_SET_TIME, &tm_time); // 設(shè)置系統(tǒng)時(shí)間
-> close(rtc) //
hwclock –r -> show_clock(const char **pp_rtcname, int utc)
-> time_t t = read_rtc(pp_rtcname, &sys_tv, utc);
->*default_rtc = "/dev/rtc";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/rtc0";
rtc = open(*default_rtc, flags);
*default_rtc = "/dev/misc/rtc";
// 會(huì)打開(kāi)這幾個(gè)默認(rèn)的文件
-> fd = rtc_xopen(pp_rtcname, O_RDONLY);
-> rtc_read_tm(&tm_time, fd);
-> xioctl(fd, RTC_RD_TIME, ptm);
以上可以知道, 讀時(shí)間使用ioctl進(jìn)行的,使用兩個(gè)命令
RTC_RD_TIME // 讀時(shí)間
RTC_SET_TIME // 設(shè)置時(shí)間
第二部分: rtc驅(qū)動(dòng)架構(gòu)分析
rtc的驅(qū)動(dòng)架構(gòu)是基于platform機(jī)制實(shí)現(xiàn)的,platform_device是由設(shè)備樹(shù)生成
rtc的設(shè)備樹(shù)如下:
rtc@10070000
{
compatible = "samsung,s3c6410-rtc";
reg = < 0x10070000 0x100>;
interrupts = < 0 44 0>, <0 45 0>;
clocks = < &clock 346>;
clock-names = "rtc";
status = "disabled";
};
這個(gè)設(shè)備樹(shù)會(huì)生成platform_device
驅(qū)動(dòng)在driver /rtc/rtc-s3c.c 文件中定義
static struct platform_driver s3c_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.id_table = s3c_rtc_driver_ids,
.driver = {
.name = "s3c-rtc",
.owner = THIS_MODULE,
.pm = &s3c_rtc_pm_ops,
.of_match_table = of_match_ptr(s3c_rtc_dt_match),
},
};
module_platform_driver(s3c_rtc_driver);
實(shí)現(xiàn)了platform_driver s3c_rtc_driver的注冊(cè)
設(shè)備樹(shù)中的compatible = "samsung,s3c6410-rtc";和驅(qū)動(dòng)中的 .compatible = "samsung,s3c6410-rtc",名稱(chēng)一直時(shí),會(huì)執(zhí)行驅(qū)動(dòng)的probe函數(shù)-> s3c_rtc_probe
在probe函數(shù)中注冊(cè)一個(gè)字符設(shè)備驅(qū)動(dòng)
rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
-> rtc = rtc_device_register(name, dev, ops, owner);
-> of_id = of_alias_get_id(dev->of_node, "rtc"); // 注冊(cè)一個(gè)rtc的類(lèi)
-> rtc_dev_prepare(rtc);
-> cdev_init(&rtc->char_dev, &rtc_dev_fops);
-> rtc_dev_add_device(rtc);
-> if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
-> rtc_sysfs_add_device(rtc); // 等價(jià)于 mknod /dev/rtc c 254 0
-> s3c_rtc_gettime(NULL, &rtc_tm); // 獲得系統(tǒng)的時(shí)間
以上是一個(gè)字符設(shè)備的創(chuàng)建。
注冊(cè)了一個(gè)cdev結(jié)構(gòu)體和file_operations結(jié)構(gòu)體rtc_dev_fops
rtc_dev_fops實(shí)現(xiàn)如下:
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,
};
由以上可知 : 應(yīng)用程序調(diào)用ioctl 驅(qū)動(dòng)也調(diào)用ioctl
hwclock –w -> xioctl(fd, RTC_RD_TIME, ptm); -> rtc_dev_ioctl(命令) (用戶(hù)空間) (內(nèi)核空間)
-> case RTC_RD_TIME:rtc_read_time(rtc, &tm);
if (copy_to_user(uarg, &tm, sizeof(tm)))
由以上可知 : 應(yīng)用程序調(diào)用ioctl 驅(qū)動(dòng)也調(diào)用ioctl
hwclock –s -> xioctl(fd, RTC_SET_TIME, ptm); -> rtc_dev_ioctl(命令) (用戶(hù)空間) (內(nèi)核空間)
-> case RTC_SET_TIME:
if (copy_from_user(&tm, uarg, sizeof(tm)))
return rtc_set_time(rtc, &tm);
hwclock –r -> xioctl(fd, RTC_RD_TIME , ptm); -> rtc_dev_ioctl(命令) (用戶(hù)空間) (內(nèi)核空間)
-> case RTC_RD_TIME: rtc_read_time(rtc, &tm);
if (copy_to_user(uarg, &tm, sizeof(tm)))