RTC(real time clock)實(shí)時時鐘,主要作用是給Linux系統(tǒng)提供時間。RTC因?yàn)槭请姵?a target="_blank">供電的,所以掉電后時間不丟失。Linux內(nèi)核把RTC用作“離線”的時間與日期維護(hù)器。當(dāng)Linux內(nèi)核啟動時,它從RTC中讀取時間與日期,作為基準(zhǔn)值。在運(yùn)行期間內(nèi)核完全拋開RTC,以軟件的形式維護(hù)系統(tǒng)的當(dāng)前時間與日期,并在需要時將時間回寫RTC芯片。另外如果RTC提供了IRQ中斷并且可以定時,那么RTC還可以作為內(nèi)核睡眠時喚醒內(nèi)核的鬧鐘。應(yīng)用程序可以用RTC提供的周期中斷做一些周期的任務(wù)。?linux有兩種rtc驅(qū)動的接口,一個是老的接口,專門用在PC機(jī)上的。另外一鐘新接口是基于linux設(shè)備驅(qū)動程序的。這個新的接口創(chuàng)建了一個RTC驅(qū)動模型,實(shí)現(xiàn)了RTC的大部分基本功能。而底層驅(qū)動無須考慮一些功能的實(shí)現(xiàn),只需將自己注冊的RTC核心中,其他工作由RTC核心來完成。下面分析RTC新接口的驅(qū)動模型。
一. 驅(qū)動模型結(jié)構(gòu)
? ? ? ? 與RTC核心有關(guān)的文件有:
? ? ? ? /drivers/rtc/class.c ? ? ? ? ?這個文件向linux設(shè)備模型核心注冊了一個類RTC,然后向驅(qū)動程序提供了注冊/注銷接口
? ? ? ? /drivers/rtc/rtc-dev.c ? ? ? 這個文件定義了基本的設(shè)備文件操作函數(shù),如:open,read等
? ? ? ? /drivers/rtc/interface.c ? ? 顧名思義,這個文件主要提供了用戶程序與RTC驅(qū)動的接口函數(shù),用戶程序一般通過ioctl與RTC驅(qū)動交互,這里定義了每個ioctl命令需要調(diào)用的函數(shù)
? ? ? ? /drivers/rtc/rtc-sysfs.c ? ? 與sysfs有關(guān)
? ? ? ? /drivers/rtc/rtc-proc.c ? ? ?與proc文件系統(tǒng)有關(guān)
? ? ? ? /include/linux/rtc.h ? ? ? ? 定義了與RTC有關(guān)的數(shù)據(jù)結(jié)構(gòu)
RTC驅(qū)動模型結(jié)構(gòu)如下圖:
二. 基本數(shù)據(jù)結(jié)構(gòu)
? 1. struct rtc_device 結(jié)構(gòu)
[cpp]?view plain?copy
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??
};??
這個結(jié)構(gòu)是RTC驅(qū)動程序的基本數(shù)據(jù)結(jié)構(gòu),但是他不像其他核心的基本結(jié)構(gòu)一樣,驅(qū)動程序以他為參數(shù)調(diào)用注冊函數(shù)注冊到核心。這個結(jié)構(gòu)是由注冊函數(shù)返回給驅(qū)動程序的。
? 2. struct rtc_class_ops 結(jié)構(gòu)
[cpp]?view plain?copy
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);??
};??
這個結(jié)構(gòu)是RTC驅(qū)動程序要實(shí)現(xiàn)的基本操作函數(shù),注意這里的操作不是文件操作。驅(qū)動程序通過初始化這樣一個結(jié)構(gòu),將自己實(shí)現(xiàn)的函數(shù)與RTC核心聯(lián)系起來。這里面的大部分函數(shù)都要驅(qū)動程序來實(shí)現(xiàn)。而且這些函數(shù)都是操作底層硬件的,屬于最底層的函數(shù)。
? 3. struct rtc_time 結(jié)構(gòu)
[cpp]?view plain?copy
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;??
};??
代表了時間與日期,從RTC設(shè)備讀回的時間和日期就保存在這個結(jié)構(gòu)體中
三. class.c?
? 1. 模塊初始化函數(shù):rtc_init
[cpp]?view plain?copy
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;??
}??
rtc_init首先調(diào)用class_create創(chuàng)建了一個類--rtc。我們知道類是一個設(shè)備的高層視圖,他抽象出了底層的實(shí)現(xiàn)細(xì)節(jié)。類的作用就是向用戶空間提供設(shè)備的信息,驅(qū)動程序不需要直接處理類。然后初始化類結(jié)構(gòu)的相應(yīng)成員,rtc_suspend,rtc_resume這兩個函數(shù)也是在class.c中實(shí)現(xiàn)的。接下來調(diào)用rtc_dev_init(),這個函數(shù)為RTC設(shè)備動態(tài)分配設(shè)備號,保存在rtc_devt中。最后調(diào)用rtc_sysfs_init,初始化rtc_class的屬性。
? 2. 為底層驅(qū)動提供接口:rtc_device_register,rtc_device_unregister
[cpp]?view plain?copy
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);??
}??
(1):處理一個idr的結(jié)構(gòu),idr在linux內(nèi)核中指的就是整數(shù)ID管理機(jī)制,從本質(zhì)上來說,idr是一種將整數(shù)ID號和特定指針關(guān)聯(lián)在一起的機(jī)制。這個機(jī)制最早是在2003年2月加入內(nèi)核的,當(dāng)時是作為POSIX定時器的一個補(bǔ)丁。現(xiàn)在,在內(nèi)核的很多地方都可以找到idr的身影。詳細(xì)實(shí)現(xiàn)請參照相關(guān)內(nèi)核代碼。這里從內(nèi)核中獲取一個idr結(jié)構(gòu),并與id相關(guān)聯(lián)。
? ? (2):分配了一個rtc_device的結(jié)構(gòu)--rtc,并且初始化了相關(guān)的成員:id, rtc_class_ops等等。
? ? (3):首先調(diào)用rtc_dev_prepare(在rtc-dev.c中定義)。因?yàn)镽TC設(shè)備本質(zhì)來講還是字符設(shè)備,所以這里初始化了字符設(shè)備相關(guān)的結(jié)構(gòu):設(shè)備號以及文件操作。然后調(diào)用device_register將設(shè)備注冊到linux設(shè)備模型核心。這樣在模塊加載的時候,udev daemon就會自動為我們創(chuàng)建設(shè)備文件rtc(n)。
? ? (4):先后調(diào)用rtc_dev_add_device,rtc_sysfs_add_device,rtc_proc_add_device三個函數(shù)。rtc_dev_add_device注冊字符設(shè)備,rtc_sysfs_add_device只是為設(shè)備添加了一個鬧鐘屬性,rtc_proc_add_device?創(chuàng)建proc文件系統(tǒng)接口。
四. rtc-dev.c?
? ? ? ? rtc-dev.c 初始化了一個file_operations結(jié)構(gòu)--rtc_dev_fops,并定義了這些操作函數(shù)。
? 1. rtc_dev_fops rtc基本的文件操作
[cpp]?view plain?copy
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,??
;???
2. 函數(shù)的實(shí)現(xiàn)(以rtc_dev_read為例) ??
[cpp]?view plain?copy
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?
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;??
}??
這里的read不是應(yīng)用程序用來獲取時間的,而是有其他的作用,他幫助應(yīng)用程序周期性的完成一些工作。如果要使用這個功能,應(yīng)用程序首先保證RTC驅(qū)動程序提供這樣的功能。這個功能是這樣實(shí)現(xiàn)的:進(jìn)程讀取/dev/rtc(n),進(jìn)程睡眠直到RTC中斷將他喚醒。我們可以發(fā)現(xiàn),這里的睡眠是ldd3中提到的手工睡眠。這個函數(shù)的手工休眠過程如下:首先調(diào)用DECLARE_WAITQUEUE(wait, current),聲明一個等待隊列入口,然后調(diào)用add_wait_queue將這個入口加入到RTC的irq等待隊列里,然后進(jìn)入循環(huán)。在循環(huán)里首先把進(jìn)程的狀態(tài)改成TASK_INTERRUPTIBLE,這樣進(jìn)程就不能再被調(diào)度運(yùn)行。但是現(xiàn)在進(jìn)程還在運(yùn)行,沒有進(jìn)入睡眠狀態(tài)。程序然后讀取RTC里面的irq_data,如果不是零,那么程序跳出這個循環(huán),進(jìn)程不會睡眠。因?yàn)檫@個irq_data在rtc的中斷處理程序會被賦值,而讀過之后就會清零,所以如果數(shù)據(jù)不是零的話說明發(fā)生過一次中斷。如果是零那么沒有發(fā)生中斷,調(diào)用schedule,進(jìn)程會被調(diào)度出可運(yùn)行隊列,從而讓出處理器,真正進(jìn)入睡眠。跳出循環(huán)代表被喚醒,然后將進(jìn)程狀態(tài)改變?yōu)榭蛇\(yùn)行,移除等待隊列入口。最后將讀回的數(shù)據(jù)傳給用戶空間。
五. interface.c?
? ? ? ? interface.c里的所有函數(shù)的實(shí)現(xiàn)都對應(yīng)于rtc-dev.c 中ioctl相應(yīng)的命令。對應(yīng)關(guān)系如下:
RTC_ALM_READ ? ? ? ? ? ? ? ? ? ? rtc_read_alarm ? ? ? ? ? 讀取鬧鐘時間
RTC_ALM_SET ? ? ? ? ? ? ? ? ? ? ?rtc_set_alarm ? ? ? ? ? ?設(shè)置鬧鐘時間
RTC_RD_TIME ? ? ? ? ? ? ? ? ? ? ?rtc_read_time ? ? ? ? ? ?讀取時間與日期
RTC_SET_TIME ? ? ? ? ? ? ? ? ? ? rtc_set_time ? ? ? ? ? ? 設(shè)置時間與日期
RTC_PIE_ON RTC_PIE_OFF ? ? ? ? ? rtc_irq_set_state ? ? ? ? ? ? ?開關(guān)RTC全局中斷的函數(shù)
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 ? ? ? ? 設(shè)置中斷的頻率
? ? ? ?以上就是所有ioctl的命令與實(shí)現(xiàn)的對應(yīng)關(guān)系。其中如果不涉及中斷的話,有兩個命令需要我們特別關(guān)心一下,就是RTC_RD_TIME與RTC_SET_TIME。因?yàn)镽TC最基本的功能就是提供時間與日期。這兩個命令恰恰是獲取時間和設(shè)置時間。下面分析一下這兩個命令的實(shí)現(xiàn),也就是rtc_set_alarm與rtc_read_time函數(shù)的實(shí)現(xiàn):
? 1. rtc_read_time 函數(shù)
[cpp]?view plain?copy
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;??
}??
這個函數(shù)用了一個信號來保證在同一時刻只有一個進(jìn)程可以獲取時間。鎖定了這個信號量后,調(diào)用rtc->ops里面read函數(shù),這個函數(shù)是由具體的驅(qū)動程序?qū)崿F(xiàn)的,操作底層硬件。讀回的時間存放在rtc_time結(jié)構(gòu)里面的。
? 2. rtc_set_time 函數(shù)
[cpp]?view plain?copy
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;??
}??
這個函數(shù)其實(shí)和rtc_read_time函數(shù)差不多,同樣是鎖定信號量,同樣是調(diào)用底層驅(qū)動函數(shù)。但是這里的設(shè)置時間提供了兩個調(diào)用:一個是set_time,一個是set_mmss。因?yàn)橛械腞TC硬件只計算秒數(shù),不關(guān)心墻鐘時間,所以如果是這樣的RTC,必須實(shí)現(xiàn)set_mmss來設(shè)置時間。
六. rtc-sysfs.c 部分
? ? ? ? 這個部分主要是有關(guān)sysfs的操作。rtc-sysfs.c中定義了這樣一個設(shè)備屬性組,如下:
[cpp]?view plain?copy
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來完成的。這里的調(diào)用關(guān)系有點(diǎn)復(fù)雜:
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等文件,用戶讀這些文件的時候就會調(diào)用相應(yīng)的函數(shù)。如讀取name文件,就會調(diào)用rtc_sysfs_show_name函數(shù),這個函數(shù)也是在rtc-sysfs.c中實(shí)現(xiàn)的,作用是讀取并顯示時間。
七. rtc-proc.c?
? ? ? ?這個文件提供RTC的proc文件系統(tǒng)接口。proc文件系統(tǒng)是軟件創(chuàng)建的文件系統(tǒng),內(nèi)核通過他向外界導(dǎo)出信息,下面的每一個文件都綁定一個函數(shù),當(dāng)用戶讀取這個文件的時候,這個函數(shù)會向文件寫入信息。rtc-proc.c中初始化了一個文件操作:
[cpp]?view plain?copy
static?const?struct?file_operations?rtc_proc_fops?=?{??
.open???????=?rtc_proc_open,??
.read???????=?seq_read,??
.llseek?????=?seq_lseek,??
.release????=?rtc_proc_release,??
};??
RTC驅(qū)動在向RTC核心注冊自己的時候,由注冊函數(shù)rtc_device_resgister調(diào)用rtc_proc_add_device來實(shí)現(xiàn)proc接口的初始化,這個函數(shù)如下定義:
[cpp]?view plain?copy
void?rtc_proc_add_device(struct?rtc_device?*rtc)??
{??
if?(rtc->id?==?0)??
proc_create_data("driver/rtc",?0,?NULL,?&rtc_proc_fops,?rtc);??
}??
他主要調(diào)用了proc_create_data。proc_create_data完成創(chuàng)建文件節(jié)點(diǎn)的作用,并將文件的操作函數(shù)與節(jié)點(diǎn)聯(lián)系起來。調(diào)用這個函數(shù)后,在/proc/driver目錄下就會有一個文件rtc,應(yīng)用程序打開這個文件就會調(diào)用rtc_proc_open函數(shù),這個函數(shù)如下定義:
[cpp]?view plain?copy
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);??
}??
我們知道一個proc的文件必須與一個操作函數(shù)組成一個proc入口項,這個文件才能正常工作。這個函數(shù)最主要作用就是調(diào)用single_open,創(chuàng)建一個proc文件入口項,使其操作函數(shù)是rtc_proc_show,并初始化seq_file接口。rtc_proc_show函數(shù)如下定義:
[cpp]?view plain?copy
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;??
}??
這個函數(shù)就是最后給用戶顯示信息的函數(shù)了,可以看出他通過調(diào)用rtc_deivce中的操作函數(shù),讀取時間,日期和一些其他的信息顯示給用戶。?
六. 總結(jié)
RTC核心使底層硬件對用戶來說是透明的,并且減少了編寫驅(qū)動程序的工作量。RTC新的驅(qū)動接口提供了更多的功能,使系統(tǒng)可以同時存在多個RTC。/dev,sysfs,proc這三種機(jī)制的實(shí)現(xiàn)使得應(yīng)用程序能靈活的使用RTC,RTC核心雖然表面上看上去很簡單,但是還是涉及到很多知識,有些東西書上講的還是不夠詳細(xì),還需要通過分析代碼加深理解。 另外RTC核心代碼的組織方式也值得學(xué)習(xí),不同功能的代碼放在不同的文件中,簡單明了。??
?
評論
查看更多