學(xué)號(hào):19021211263
linux中的rtc驅(qū)動(dòng)位于
drivers/rtc
下,里面包含了許多開(kāi)發(fā)平臺(tái)的RTC驅(qū)動(dòng)荤崇,我們這里是以S3C24xx為主,所以它的RTC驅(qū)動(dòng)為rtc-s3c.c
1削饵、入口函數(shù)s3c_rtc_init分析
進(jìn)入./drivers/rtc/rtc-s3c.c诀豁,找到入口函數(shù),如下所示:
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,//.probe函數(shù)
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};
static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
static int __init s3c_rtc_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410_rtcdrv);
}
module_init(s3c_rtc_init);
這里注冊(cè)了一個(gè)“s3c2410-rtc”名稱(chēng)的平臺(tái)設(shè)備驅(qū)動(dòng)
而“s3c2410-rtc”的平臺(tái)設(shè)備,在./arch/arm/plat-s3c24xx/devs.c
里定義了,只不過(guò)這里沒(méi)有注冊(cè),如下所示:
/* RTC */
static struct resource s3c_rtc_resource[] = {
[0] = {
.start = S3C24XX_PA_RTC,//RTC寄存器地址
.end = S3C24XX_PA_RTC + 0xff,
.flags = IORESOURCE_MEM,//內(nèi)存資源
},
[1] = {
.start = IRQ_RTC,//RTC鬧鐘中斷
.end = IRQ_RTC,
.flags = IORESOURCE_IRQ,//IRQ資源
},
[2] = {
.start = IRQ_TICK,//RTC時(shí)鐘節(jié)拍中斷
.end = IRQ_TICK,
.flags = IORESOURCE_IRQ//IRQ資源
}
};
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
EXPORT_SYMBOL(s3c_device_rtc);
當(dāng)內(nèi)核匹配到有與它名稱(chēng)同名的平臺(tái)設(shè)備,就會(huì)調(diào)用.probe函數(shù),接下來(lái)我們便進(jìn)入s3c2410_rtcdrv->probe函數(shù)(即s3c_rtc_probe函數(shù))中看看,做了什么:
static int s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc; //rtc設(shè)備結(jié)構(gòu)體
struct resource *res;
int ret;
s3c_rtc_tickno = platform_get_irq(pdev, 1);//獲取IRQ_TICK節(jié)拍中斷資源
s3c_rtc_alarmno = platform_get_irq(pdev, 0);//獲取IRQ_RTC鬧鐘中斷資源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//獲取內(nèi)存資源
s3c_rtc_mem = request_mem_region(res->start,res->end-res->start+1,pdev->name);//申請(qǐng)內(nèi)存資源
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);//對(duì)內(nèi)存進(jìn)行重映射
s3c_rtc_enable(pdev, 1);//設(shè)置硬件相關(guān)設(shè)置,使能RTC寄存器
s3c_rtc_setfreq(s3c_rtc_freq);//設(shè)置TICONT寄存器辜窑,使能節(jié)拍中斷,設(shè)置節(jié)拍計(jì)數(shù)值
/*1.注冊(cè)RTC設(shè)備*/
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc);
return 0;
}
顯然最終會(huì)調(diào)用rtc_device_register()函數(shù)來(lái)向內(nèi)核注冊(cè)rtc_device設(shè)備,注冊(cè)成功會(huì)返回一個(gè)已注冊(cè)好的rtc_device,而s3c_rtcops是一個(gè)rtc_class_ops結(jié)構(gòu)體,里面就是保存如何操作這個(gè)rtc設(shè)備的函數(shù),比如讀寫(xiě)RTC時(shí)間,讀寫(xiě)鬧鐘時(shí)間等,注冊(cè)后,會(huì)保存在rtc_device->ops里躺盛。
rtc_device_register()函數(shù)在drivers/rtc/Class.c
文件內(nèi)被定義。Class.c文件主要定義了RTC子系統(tǒng),而內(nèi)核初始化,便會(huì)進(jìn)入Class.c彩库。
再?gòu)腃lass.c中的初始化函數(shù)看:
先進(jìn)入rtc_init()在創(chuàng)建了相關(guān)的類(lèi)之后會(huì)調(diào)用rtc_dev_init()肤无,在rtc_dev_init()中會(huì)通過(guò)alloc_chrdev_region()函數(shù)來(lái)注冊(cè)字符設(shè)備:
err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");// RTC_DEV_MAX=16,表示只注冊(cè)0~15個(gè)次設(shè)備號(hào),設(shè)備編號(hào)保存在rtc_devt中
2、rtc_device_register()函數(shù)分析
Class.c
中的alloc_chrdev_region()函數(shù)和./arch/arm/plat-s3c24xx/devs.c
中通過(guò)rtc_device_register()函數(shù)注冊(cè)RTC設(shè)備,會(huì)有什么關(guān)系骇钦?
接下來(lái)便來(lái)看rtc_device_register(),代碼如下:
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;//定義一個(gè)rtc_device結(jié)構(gòu)體
... ...
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); //分配rtc_device結(jié)構(gòu)體為全局變量
/*設(shè)置rtc_device*/
rtc->id = id;
rtc->ops = ops;//將s3c_rtcops保存在rtc_device->ops里
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
rtc->dev.release = rtc_device_release;
... ...
rtc_dev_prepare(rtc); //1.做提前準(zhǔn)備,初始化cdev結(jié)構(gòu)體
... ...
rtc_dev_add_device(rtc);//2.在/dev下創(chuàng)建rtc相關(guān)文件,將cdev添加到系統(tǒng)中
rtc_sysfs_add_device(rtc);//在/sysfs下創(chuàng)建rtc相關(guān)文件
rtc_proc_add_device(rtc); //在/proc下創(chuàng)建rtc相關(guān)文件
... ...
return rtc;
}
上面的rtc_dev_prepare(rtc)和rtc_dev_add_device(rtc)主要做了以下兩件事(位于./drivers/rtc/rtc-dev.c
):
-
rtc_dev_prepare(rtc)中:
cdev_init(&rtc->char_dev, &rtc_dev_fops);//綁定file_operations
-
rtc_dev_add_device(rtc)中:
cdev_add(&rtc->char_dev, rtc->dev.devt, 1);//注冊(cè)rtc->char_dev字符設(shè)備,添加一個(gè)從設(shè)備到系統(tǒng)中
顯然這里就是利用新方法注冊(cè)字符設(shè)備
.probe函數(shù)總結(jié)
所以“s3c2410-rtc”平臺(tái)設(shè)備驅(qū)動(dòng)的.probe主要做了以下幾件事:
1.設(shè)置RTC相關(guān)寄存器
2.分配rtc_device結(jié)構(gòu)體
-
3.設(shè)置rtc_device結(jié)構(gòu)體
- 3.1 通過(guò)rtc_device_register函數(shù)宛渐,將struct rtc_class_ops s3c_rtcops放入rtc_device->ops,實(shí)現(xiàn)對(duì)RTC讀寫(xiě)時(shí)間等操作
4.注冊(cè)rtc->char_dev字符設(shè)備眯搭,通過(guò)cdev_init函數(shù)將該字符設(shè)備的操作結(jié)構(gòu)體設(shè)為:struct file_operations rtc_dev_fops
3窥翩、file_operations結(jié)構(gòu)體分析
綜上所述,rtc->char_dev字符設(shè)備中綁定的file_operations結(jié)構(gòu)體rtc_dev_fops為:
static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
};
3.1鳞仙、open函數(shù)
當(dāng)應(yīng)用層open(”/dev/rtcXX”)時(shí),就會(huì)調(diào)用rtc_dev_fops->rtc_dev_open(),我們來(lái)看看如何open的:
static int rtc_dev_open(struct inode *inode, struct file *file)
{
struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);//獲取對(duì)應(yīng)的rtc_device結(jié)構(gòu)體
const struct rtc_class_ops *ops = rtc->ops;//最終等于s3c_rtcops
file->private_data = rtc;//設(shè)置file結(jié)構(gòu)體的私有成員等于rtc_device,再次執(zhí)行ioctl等函數(shù)時(shí),直接就可以提取file->private_data即可
err = ops->open ? ops->open(rtc->dev.parent) : 0;//調(diào)用s3c_rtcops->open
mutex_unlock(&rtc->char_lock);
return err;
}
container_of:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) //ptr:返回的type類(lèi)型的結(jié)構(gòu)體首地址 //type:該結(jié)構(gòu)體類(lèi)型 //member:結(jié)構(gòu)體中的某一個(gè)成員
通過(guò)一個(gè)結(jié)構(gòu)變量中一個(gè)成員的地址member找到這個(gè)結(jié)構(gòu)體變量的首地址ptr寇蚊。
顯然最終還是調(diào)用rtc_device下的s3c_rtcops->open函數(shù):
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.ioctl = s3c_rtc_ioctl,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
};
即s3c_rtc_open()函數(shù),而s3c_rtc_open()函數(shù)里主要是申請(qǐng)了兩個(gè)中斷棍好,一個(gè)鬧鐘中斷仗岸,一個(gè)計(jì)時(shí)中斷:
static int s3c_rtc_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
int ret;
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);//申請(qǐng)鬧鐘中斷
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
return ret;
}
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);//申請(qǐng)計(jì)時(shí)中斷
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
goto tick_err;
}
return ret;
tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev);
return ret;
}
3.2、ioctl函數(shù)
當(dāng)我們應(yīng)用層open后借笙,使用 ioctl(int fd, unsigned long cmd, ...)時(shí)扒怖,就會(huì)調(diào)用rtc_dev_fops-> rtc_dev_ioctl ():
static int rtc_dev_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
struct rtc_device *rtc = file->private_data;//提取rtc_device
void __user *uarg = (void __user *) arg;
... ...
switch (cmd) {
case RTC_EPOCH_SET:
case RTC_SET_TIME://設(shè)置時(shí)間
if (!capable(CAP_SYS_TIME))
return -EACCES;
break;
case RTC_IRQP_SET://改變中斷觸發(fā)速度
... ...
... ...}
switch (cmd) {
case RTC_ALM_READ://讀鬧鐘時(shí)間
err = rtc_read_alarm(rtc, &alarm);//調(diào)用s3c_rtcops-> read_alarm
if (err < 0)
return err;
if (copy_to_user(uarg, &alarm.time, sizeof(tm)))//長(zhǎng)傳時(shí)間數(shù)據(jù)
return -EFAULT;
break;
case RTC_ALM_SET://設(shè)置鬧鐘時(shí)間 , 調(diào)用s3c_rtcops-> set_alarm
... ...
case RTC_RD_TIME://讀RTC時(shí)間, 調(diào)用s3c_rtcops-> read_alarm
err = rtc_read_time(rtc, &tm);
... ...
case RTC_SET_TIME://寫(xiě)RTC時(shí)間,調(diào)用s3c_rtcops-> set_time
... ...
case RTC_IRQP_SET://改變中斷觸發(fā)頻率,調(diào)用s3c_rtcops-> irq_set_freq
... ...
}
這里我們假設(shè)是讀RTC時(shí)間,即會(huì)進(jìn)入rtc_read_time函數(shù):
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
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);//最終調(diào)用該函數(shù)
}
mutex_unlock(&rtc->ops_lock);
return err;
}
從上面可以看出业稼,rtc->ops->read_time即為rtc_device結(jié)構(gòu)體下的ops成員s3c_rtcops下的read_time函數(shù)姚垃,即s3c_rtc_getalarm函數(shù)
調(diào)用了半天,最終還是調(diào)用s3c_rtcops下的成員函數(shù)
繼續(xù)分析s3c_rtc_getalarm函數(shù)盼忌,看看如何讀出時(shí)間:
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
unsigned int have_retried = 0;
void __iomem *base = s3c_rtc_base;//獲取RTC相關(guān)寄存器基地址
retry_get_time:
/*獲取年,月,日,時(shí),分,秒寄存器*/
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
/* 判斷秒寄存器中是0积糯,則表示過(guò)去了一分鐘,那么小時(shí)谦纱,天看成,月,等寄存器中的值都可能已經(jīng)變化跨嘉,需要重新讀取這些寄存器的值*/
if (rtc_tm->tm_sec == 0 && !have_retried) {
have_retried = 1;
goto retry_get_time;
}
/*將獲取的寄存器值,轉(zhuǎn)換為真正的時(shí)間數(shù)據(jù)*/
BCD_TO_BIN(rtc_tm->tm_sec);
BCD_TO_BIN(rtc_tm->tm_min);
BCD_TO_BIN(rtc_tm->tm_hour);
BCD_TO_BIN(rtc_tm->tm_mday);
BCD_TO_BIN(rtc_tm->tm_mon);
BCD_TO_BIN(rtc_tm->tm_year);
rtc_tm->tm_year += 100;//存儲(chǔ)器中存放的是從1900年開(kāi)始的時(shí)間川慌,所以加上100
rtc_tm->tm_mon -= 1;
return 0;
}
同樣, 在s3c_rtc_gettime函數(shù)下(即s3c_rtcops-> set_time()函數(shù)),也是向相關(guān)寄存器寫(xiě)入RTC時(shí)間
總結(jié)
- rtc_device->char_dev :字符設(shè)備,與應(yīng)用層、以及更底層的函數(shù)打交道
- rtc_device->ops ∶沃亍:更底層的操作函數(shù)兑燥,直接操作硬件相關(guān)的寄存器,被rtc_device->char_dev調(diào)用
4琴拧、修改內(nèi)核
我們單板上使用ls /dev/rtc*
,找不到該字符設(shè)備, 因?yàn)閮?nèi)核里只定義了s3c_device_rtc這個(gè)RTC平臺(tái)設(shè)備,沒(méi)有注冊(cè),所以平臺(tái)驅(qū)動(dòng)沒(méi)有被匹配上,接下來(lái)我們來(lái)修改內(nèi)核里的注冊(cè)數(shù)組
4.1進(jìn)入arch/arm/plat-s3c24xx/Common-smdk.c
如下所示,在smdk_devs[]里,添加RTC的平臺(tái)設(shè)備即可,當(dāng)內(nèi)核啟動(dòng)時(shí),就會(huì)調(diào)用該數(shù)組,將里面的platform_device統(tǒng)統(tǒng)注冊(cè)一遍
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
&smdk_led4,
&smdk_led5,
&smdk_led6,
&smdk_led7,
&s3c_device_rtc,//加入這一行
#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
&s3c_device_dm9k,
#endif
#ifdef CONFIG_SERIAL_EXTEND_S3C24xx
&s3c_device_8250,
#endif
#ifdef CONFIG_TOUCHSCREEN_S3C2410
&s3c_device_ts,
#endif
};
然后將Common-smdk.c代替虛擬機(jī)的內(nèi)核目錄下的Common-smdk.c,重新make uImage編譯內(nèi)核即可
5降瞳、測(cè)試運(yùn)行
啟動(dòng)后,如下所示, 使用ls /dev/rtc*
,就找到了rtc0這個(gè)字符設(shè)備
# ls /dev/rtc*
/dev/rtc0
#
5.1、設(shè)置RTC時(shí)間
在linux里有兩個(gè)時(shí)鐘:
硬件時(shí)鐘(2440里寄存器的時(shí)鐘)蚓胸、系統(tǒng)時(shí)鐘(內(nèi)核中的時(shí)鐘)
所以有兩個(gè)不同的命令: date命令挣饥、hwclock命令
5.2、date命令
輸入date查看系統(tǒng)時(shí)鐘:
# date
wed Nov 3 14:50:24 UTC 2021
如果覺(jué)得不方便也可以指定格式顯示日期沛膳,需要在字符串前面加”+”
如下所示,輸入了 date "+ %Y/%m/%d %H:%M:%S"
# date
wed Nov 3 14:54:30 UTC 2021
#
#
# data "+ %Y/%m/%d %H:%M:%S"
2021/11/03 14:54:33
- %M:表示秒
- %m:表示月
- %Y:表示年,當(dāng)只需要最后兩位數(shù)字,輸入%y即可
date命令設(shè)置時(shí)間格式如下:
date 月日時(shí)分年.秒
如下所示,輸入date 111515292017.20扔枫,即可設(shè)置好系統(tǒng)時(shí)鐘
# date 111515292017.20
wed Nov 15 15:29:20 UTC 2017
#
5.3、hwclock命令
常用參數(shù)如下所示
- -r, --show 讀取并打印硬件時(shí)鐘(read hardware clock and print result )
- -s, --hctosys 將硬件時(shí)鐘同步到系統(tǒng)時(shí)鐘(set the system time from the hardware clock )
- -w, --systohc 將系統(tǒng)時(shí)鐘同步到硬件時(shí)鐘(set the hardware clock to the current system time )
如下所示,使用hwclock -w锹安,即可同步硬件時(shí)鐘
# hwclock -r
Wed Nov 3 15:20:46 2021 0.000000 seconds 未同步之前的時(shí)間
# hwclock -w
# hwclock -r
Wed Nov 15 15:30:06 2017 0.000000 seconds 同步后的時(shí)間
#
然后重啟后,使用date命令,看到時(shí)間正常
更多有趣內(nèi)容歡迎訪問(wèn)我的個(gè)人博客短荐。