日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux RTC 驱动模型分析

發(fā)布時(shí)間:2023/12/10 linux 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux RTC 驱动模型分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
linux RTC 驅(qū)動(dòng)模型分析 ? ? ? ? RTC(real time clock)實(shí)時(shí)時(shí)鐘,主要作用是給Linux系統(tǒng)提供時(shí)間。RTC因?yàn)槭请姵毓╇姷?#xff0c;所以掉電后時(shí)間不丟失。Linux內(nèi)核把RTC用作“離線”的時(shí)間與日期維護(hù)器。當(dāng)Linux內(nèi)核啟動(dòng)時(shí),它從RTC中讀取時(shí)間與日期,作為基準(zhǔn)值。在運(yùn)行期間內(nèi)核完全拋開(kāi)RTC,以軟件的形式維護(hù)系統(tǒng)的當(dāng)前時(shí)間與日期,并在需要時(shí)將時(shí)間回寫(xiě)RTC芯片。另外如果RTC提供了IRQ中斷并且可以定時(shí),那么RTC還可以作為內(nèi)核睡眠時(shí)喚醒內(nèi)核的鬧鐘。應(yīng)用程序可以用RTC提供的周期中斷做一些周期的任務(wù)。?linux有兩種rtc驅(qū)動(dòng)的接口,一個(gè)是老的接口,專(zhuān)門(mén)用在PC機(jī)上的。另外一鐘新接口是基于linux設(shè)備驅(qū)動(dòng)程序的。這個(gè)新的接口創(chuàng)建了一個(gè)RTC驅(qū)動(dòng)模型,實(shí)現(xiàn)了RTC的大部分基本功能。而底層驅(qū)動(dòng)無(wú)須考慮一些功能的實(shí)現(xiàn),只需將自己注冊(cè)的RTC核心中,其他工作由RTC核心來(lái)完成。下面分析RTC新接口的驅(qū)動(dòng)模型。
一. 驅(qū)動(dòng)模型結(jié)構(gòu)
? ? ? ? 與RTC核心有關(guān)的文件有:
? ? ? ? /drivers/rtc/class.c ? ? ? ? ?這個(gè)文件向linux設(shè)備模型核心注冊(cè)了一個(gè)類(lèi)RTC,然后向驅(qū)動(dòng)程序提供了注冊(cè)/注銷(xiāo)接口
? ? ? ? /drivers/rtc/rtc-dev.c ? ? ? 這個(gè)文件定義了基本的設(shè)備文件操作函數(shù),如:open,read等
? ? ? ? /drivers/rtc/interface.c ? ? 顧名思義,這個(gè)文件主要提供了用戶程序與RTC驅(qū)動(dòng)的接口函數(shù),用戶程序一般通過(guò)ioctl與RTC驅(qū)動(dòng)交互,這里定義了每個(gè)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ū)動(dòng)模型結(jié)構(gòu)如下圖:


二. 基本數(shù)據(jù)結(jié)構(gòu)
? 1. struct rtc_device 結(jié)構(gòu)
[cpp]?view plaincopy
  • 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??
  • };??
  • ? ? ? ? 這個(gè)結(jié)構(gòu)是RTC驅(qū)動(dòng)程序的基本數(shù)據(jù)結(jié)構(gòu),但是他不像其他核心的基本結(jié)構(gòu)一樣,驅(qū)動(dòng)程序以他為參數(shù)調(diào)用注冊(cè)函數(shù)注冊(cè)到核心。這個(gè)結(jié)構(gòu)是由注冊(cè)函數(shù)返回給驅(qū)動(dòng)程序的。
    ? 2. struct rtc_class_ops 結(jié)構(gòu)
    [cpp]?view plaincopy
  • 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);??
  • };??
  • ? ? ? ? 這個(gè)結(jié)構(gòu)是RTC驅(qū)動(dòng)程序要實(shí)現(xiàn)的基本操作函數(shù),注意這里的操作不是文件操作。驅(qū)動(dòng)程序通過(guò)初始化這樣一個(gè)結(jié)構(gòu),將自己實(shí)現(xiàn)的函數(shù)與RTC核心聯(lián)系起來(lái)。這里面的大部分函數(shù)都要驅(qū)動(dòng)程序來(lái)實(shí)現(xiàn)。而且這些函數(shù)都是操作底層硬件的,屬于最底層的函數(shù)。
    ? 3. struct rtc_time 結(jié)構(gòu)
    [cpp]?view plaincopy
  • 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;??
  • };??
  • ? ? ? ? 代表了時(shí)間與日期,從RTC設(shè)備讀回的時(shí)間和日期就保存在這個(gè)結(jié)構(gòu)體中
    三. class.c?
    ? 1. 模塊初始化函數(shù):rtc_init
    [cpp]?view plaincopy
  • 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)建了一個(gè)類(lèi)--rtc。我們知道類(lèi)是一個(gè)設(shè)備的高層視圖,他抽象出了底層的實(shí)現(xiàn)細(xì)節(jié)。類(lèi)的作用就是向用戶空間提供設(shè)備的信息,驅(qū)動(dòng)程序不需要直接處理類(lèi)。然后初始化類(lèi)結(jié)構(gòu)的相應(yīng)成員,rtc_suspend,rtc_resume這兩個(gè)函數(shù)也是在class.c中實(shí)現(xiàn)的。接下來(lái)調(diào)用rtc_dev_init(),這個(gè)函數(shù)為RTC設(shè)備動(dòng)態(tài)分配設(shè)備號(hào),保存在rtc_devt中。最后調(diào)用rtc_sysfs_init,初始化rtc_class的屬性。
    ? 2. 為底層驅(qū)動(dòng)提供接口:rtc_device_register,rtc_device_unregister
    [cpp]?view plaincopy
  • 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):處理一個(gè)idr的結(jié)構(gòu),idr在linux內(nèi)核中指的就是整數(shù)ID管理機(jī)制,從本質(zhì)上來(lái)說(shuō),idr是一種將整數(shù)ID號(hào)和特定指針關(guān)聯(lián)在一起的機(jī)制。這個(gè)機(jī)制最早是在2003年2月加入內(nèi)核的,當(dāng)時(shí)是作為POSIX定時(shí)器的一個(gè)補(bǔ)丁。現(xiàn)在,在內(nèi)核的很多地方都可以找到idr的身影。詳細(xì)實(shí)現(xiàn)請(qǐng)參照相關(guān)內(nèi)核代碼。這里從內(nèi)核中獲取一個(gè)idr結(jié)構(gòu),并與id相關(guān)聯(lián)。
    ? ? (2):分配了一個(gè)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ì)來(lái)講還是字符設(shè)備,所以這里初始化了字符設(shè)備相關(guān)的結(jié)構(gòu):設(shè)備號(hào)以及文件操作。然后調(diào)用device_register將設(shè)備注冊(cè)到linux設(shè)備模型核心。這樣在模塊加載的時(shí)候,udev daemon就會(huì)自動(dòng)為我們創(chuàng)建設(shè)備文件rtc(n)。
    ? ? (4):先后調(diào)用rtc_dev_add_device,rtc_sysfs_add_device,rtc_proc_add_device三個(gè)函數(shù)。rtc_dev_add_device注冊(cè)字符設(shè)備,rtc_sysfs_add_device只是為設(shè)備添加了一個(gè)鬧鐘屬性,rtc_proc_add_device?創(chuàng)建proc文件系統(tǒng)接口。
    四. rtc-dev.c?
    ? ? ? ? rtc-dev.c 初始化了一個(gè)file_operations結(jié)構(gòu)--rtc_dev_fops,并定義了這些操作函數(shù)。
    ? 1. rtc_dev_fops rtc基本的文件操作
    [cpp]?view plaincopy
  • 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 plaincopy
  • 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;??
  • }??
  • ? ? ? ? 這里的read不是應(yīng)用程序用來(lái)獲取時(shí)間的,而是有其他的作用,他幫助應(yīng)用程序周期性的完成一些工作。如果要使用這個(gè)功能,應(yīng)用程序首先保證RTC驅(qū)動(dòng)程序提供這樣的功能。這個(gè)功能是這樣實(shí)現(xiàn)的:進(jìn)程讀取/dev/rtc(n),進(jìn)程睡眠直到RTC中斷將他喚醒。我們可以發(fā)現(xiàn),這里的睡眠是ldd3中提到的手工睡眠。這個(gè)函數(shù)的手工休眠過(guò)程如下:首先調(diào)用DECLARE_WAITQUEUE(wait, current),聲明一個(gè)等待隊(duì)列入口,然后調(diào)用add_wait_queue將這個(gè)入口加入到RTC的irq等待隊(duì)列里,然后進(jìn)入循環(huán)。在循環(huán)里首先把進(jìn)程的狀態(tài)改成TASK_INTERRUPTIBLE,這樣進(jìn)程就不能再被調(diào)度運(yùn)行。但是現(xiàn)在進(jìn)程還在運(yùn)行,沒(méi)有進(jìn)入睡眠狀態(tài)。程序然后讀取RTC里面的irq_data,如果不是零,那么程序跳出這個(gè)循環(huán),進(jìn)程不會(huì)睡眠。因?yàn)檫@個(gè)irq_data在rtc的中斷處理程序會(huì)被賦值,而讀過(guò)之后就會(huì)清零,所以如果數(shù)據(jù)不是零的話說(shuō)明發(fā)生過(guò)一次中斷。如果是零那么沒(méi)有發(fā)生中斷,調(diào)用schedule,進(jìn)程會(huì)被調(diào)度出可運(yùn)行隊(duì)列,從而讓出處理器,真正進(jìn)入睡眠。跳出循環(huán)代表被喚醒,然后將進(jìn)程狀態(tài)改變?yōu)榭蛇\(yùn)行,移除等待隊(duì)列入口。最后將讀回的數(shù)據(jù)傳給用戶空間。
    五. interface.c?
    ? ? ? ? interface.c里的所有函數(shù)的實(shí)現(xiàn)都對(duì)應(yīng)于rtc-dev.c 中ioctl相應(yīng)的命令。對(duì)應(yīng)關(guān)系如下:
    RTC_ALM_READ ? ? ? ? ? ? ? ? ? ? rtc_read_alarm ? ? ? ? ? 讀取鬧鐘時(shí)間
    RTC_ALM_SET ? ? ? ? ? ? ? ? ? ? ?rtc_set_alarm ? ? ? ? ? ?設(shè)置鬧鐘時(shí)間
    RTC_RD_TIME ? ? ? ? ? ? ? ? ? ? ?rtc_read_time ? ? ? ? ? ?讀取時(shí)間與日期
    RTC_SET_TIME ? ? ? ? ? ? ? ? ? ? rtc_set_time ? ? ? ? ? ? 設(shè)置時(shí)間與日期
    RTC_PIE_ON RTC_PIE_OFF ? ? ? ? ? rtc_irq_set_state ? ? ? ? ? ? ?開(kāi)關(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)的對(duì)應(yīng)關(guān)系。其中如果不涉及中斷的話,有兩個(gè)命令需要我們特別關(guān)心一下,就是RTC_RD_TIME與RTC_SET_TIME。因?yàn)镽TC最基本的功能就是提供時(shí)間與日期。這兩個(gè)命令恰恰是獲取時(shí)間和設(shè)置時(shí)間。下面分析一下這兩個(gè)命令的實(shí)現(xiàn),也就是rtc_set_alarm與rtc_read_time函數(shù)的實(shí)現(xiàn):
    ? 1. rtc_read_time 函數(shù)
    [cpp]?view plaincopy
  • 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;??
  • }??
  • ? ? ? ? 這個(gè)函數(shù)用了一個(gè)信號(hào)來(lái)保證在同一時(shí)刻只有一個(gè)進(jìn)程可以獲取時(shí)間。鎖定了這個(gè)信號(hào)量后,調(diào)用rtc->ops里面read函數(shù),這個(gè)函數(shù)是由具體的驅(qū)動(dòng)程序?qū)崿F(xiàn)的,操作底層硬件。讀回的時(shí)間存放在rtc_time結(jié)構(gòu)里面的。
    ? 2. rtc_set_time 函數(shù)
    [cpp]?view plaincopy
  • 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;??
  • }??
  • ? ? ? ? 這個(gè)函數(shù)其實(shí)和rtc_read_time函數(shù)差不多,同樣是鎖定信號(hào)量,同樣是調(diào)用底層驅(qū)動(dòng)函數(shù)。但是這里的設(shè)置時(shí)間提供了兩個(gè)調(diào)用:一個(gè)是set_time,一個(gè)是set_mmss。因?yàn)橛械腞TC硬件只計(jì)算秒數(shù),不關(guān)心墻鐘時(shí)間,所以如果是這樣的RTC,必須實(shí)現(xiàn)set_mmss來(lái)設(shè)置時(shí)間。
    六. rtc-sysfs.c 部分
    ? ? ? ? 這個(gè)部分主要是有關(guān)sysfs的操作。rtc-sysfs.c中定義了這樣一個(gè)設(shè)備屬性組,如下:
    [cpp]?view plaincopy
  • 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),??
  • ?????{?},??
  • };??
  • ? ? ? ?這個(gè)屬性組是在class.c的模塊初始化函數(shù)中,由rtc_sysfs_init函數(shù)賦值給rtc_class->dev_attrs的,以后屬于這個(gè)類(lèi)的設(shè)備都會(huì)有這些屬性。但是我們知道要想一個(gè)設(shè)備結(jié)構(gòu)擁有一種屬性,必須調(diào)用device_create_file,這樣才會(huì)使這個(gè)屬性出現(xiàn)在sysfs相關(guān)設(shè)備目錄里。但是在這里的代碼中只是給這個(gè)類(lèi)的dev_attrs域賦值了這個(gè)屬性組指針,而沒(méi)有調(diào)用device_create_file。我原來(lái)以為是在rtc_device_resgister函數(shù)中,由rtc_sysfs_add_device完成這個(gè)工作,但是這個(gè)函數(shù)只是給設(shè)備添加了鬧鐘屬性,并沒(méi)有處理這個(gè)屬性組。最后發(fā)現(xiàn)這個(gè)工作是由device_register來(lái)完成的。這里的調(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來(lái)完成設(shè)備的屬性設(shè)置的。

    ? ? ? ? 設(shè)置完屬性后,在/sys/class/rtc/rtc(n)的目錄下就會(huì)出現(xiàn)name,date,time等文件,用戶讀這些文件的時(shí)候就會(huì)調(diào)用相應(yīng)的函數(shù)。如讀取name文件,就會(huì)調(diào)用rtc_sysfs_show_name函數(shù),這個(gè)函數(shù)也是在rtc-sysfs.c中實(shí)現(xiàn)的,作用是讀取并顯示時(shí)間。

    七. rtc-proc.c?
    ? ? ? ?這個(gè)文件提供RTC的proc文件系統(tǒng)接口。proc文件系統(tǒng)是軟件創(chuàng)建的文件系統(tǒng),內(nèi)核通過(guò)他向外界導(dǎo)出信息,下面的每一個(gè)文件都綁定一個(gè)函數(shù),當(dāng)用戶讀取這個(gè)文件的時(shí)候,這個(gè)函數(shù)會(huì)向文件寫(xiě)入信息。rtc-proc.c中初始化了一個(gè)文件操作:
    [cpp]?view plaincopy
  • static?const?struct?file_operations?rtc_proc_fops?=?{??
  • ????.open???????=?rtc_proc_open,??
  • ????.read???????=?seq_read,??
  • ????.llseek?????=?seq_lseek,??
  • ????.release????=?rtc_proc_release,??
  • };??
  • ? ? ? ? RTC驅(qū)動(dòng)在向RTC核心注冊(cè)自己的時(shí)候,由注冊(cè)函數(shù)rtc_device_resgister調(diào)用rtc_proc_add_device來(lái)實(shí)現(xiàn)proc接口的初始化,這個(gè)函數(shù)如下定義:
    [cpp]?view plaincopy
  • 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)系起來(lái)。調(diào)用這個(gè)函數(shù)后,在/proc/driver目錄下就會(huì)有一個(gè)文件rtc,應(yīng)用程序打開(kāi)這個(gè)文件就會(huì)調(diào)用rtc_proc_open函數(shù),這個(gè)函數(shù)如下定義:
    [cpp]?view plaincopy
  • 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);??
  • }??
  • ? ? ? ? 我們知道一個(gè)proc的文件必須與一個(gè)操作函數(shù)組成一個(gè)proc入口項(xiàng),這個(gè)文件才能正常工作。這個(gè)函數(shù)最主要作用就是調(diào)用single_open,創(chuàng)建一個(gè)proc文件入口項(xiàng),使其操作函數(shù)是rtc_proc_show,并初始化seq_file接口。rtc_proc_show函數(shù)如下定義:
    [cpp]?view plaincopy
  • 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;??
  • }??
  • ? ? ? ? 這個(gè)函數(shù)就是最后給用戶顯示信息的函數(shù)了,可以看出他通過(guò)調(diào)用rtc_deivce中的操作函數(shù),讀取時(shí)間,日期和一些其他的信息顯示給用戶。?
    六. 總結(jié)

    ? ? ? ? RTC核心使底層硬件對(duì)用戶來(lái)說(shuō)是透明的,并且減少了編寫(xiě)驅(qū)動(dòng)程序的工作量。RTC新的驅(qū)動(dòng)接口提供了更多的功能,使系統(tǒng)可以同時(shí)存在多個(gè)RTC。/dev,sysfs,proc這三種機(jī)制的實(shí)現(xiàn)使得應(yīng)用程序能靈活的使用RTC,RTC核心雖然表面上看上去很簡(jiǎn)單,但是還是涉及到很多知識(shí),有些東西書(shū)上講的還是不夠詳細(xì),還需要通過(guò)分析代碼加深理解。 另外RTC核心代碼的組織方式也值得學(xué)習(xí),不同功能的代碼放在不同的文件中,簡(jiǎn)單明了。??

    總結(jié)

    以上是生活随笔為你收集整理的linux RTC 驱动模型分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。