============================================
作者:yuanlulu
http://blog.csdn.net/yuanlulu
版權沒有,但是轉載請保留此段聲明
/drivers/rtc/rtc-test.c下有一個rtc驅動的架構例程。
填寫 rtc_class_ops
編寫rtc核心驅動的主要步驟就是填寫 rtc_class_ops。
這個結構體中使用的struct device參數就是rtc_device_register()使用的那個dev,它代表總線上的實體裝置。這個
struct device的driver_data資料一般儲存struct device的狀态,包括指向rtc_device的指針。
驅動開發者至少應該提供read_time/set_time這兩個接口,其他函數都是可選的。
141 struct rtc_class_ops {
142 int (*open)(struct device *); //打開裝置時的回調函數,這個函數應該初始化硬體并申請資源
143 void (*release)(struct device *); //這個函數是裝置關閉時被調用的,應該登出申請的資源。
144 int (*ioctl)(struct device *, unsigned int, unsigned long); //ioctl函數,對于想讓rtc自己實作的指令應傳回enoioctlcmd
145 int (*read_time)(struct device *, struct rtc_time *); //讀取時間
146 int (*set_time)(struct device *, struct rtc_time *); //設定時間
147 int (*read_alarm)(struct device *, struct rtc_wkalrm *); //讀取下一次定時中斷的時間
148 int (*set_alarm)(struct device *, struct rtc_wkalrm *); //設定下一次定時中斷的時間
149 int (*proc)(struct device *, struct seq_file *); //procfs接口,該函數決定你在終端中cat /proc/driver/rtc時輸出相關的資訊
150 int (*set_mmss)(struct device *, unsigned long secs); // 将傳入的參數secs轉換為struct rtc_time然後調用set_time函數。程式員可以不實作這個函數,但前提是定義好了read_time/set_time,因為rtc架構需要用這兩個函數來實作這個功能。
151 int (*irq_set_state)(struct device *, int enabled); //周期采樣中斷的開關,根據enabled的值來設定
152 int (*irq_set_freq)(struct device *, int freq); //設定周期中斷的頻率
153 int (*read_callback)(struct device *, int data); //使用者空間獲得資料後會傳入讀取的資料,并用這個函數傳回的資料更新資料。
154 int (*alarm_irq_enable)(struct device *, unsigned int enabled); //alarm中斷使能開關,根據enabled的值來設定
155 int (*update_irq_enable)(struct device *, unsigned int enabled); //更新中斷使能開關,根據enabled的值來設定
156 };
1.免定義的ioctl指令
這裡的ioctl函數并不一定要實作所有的指令,對于一些指令如果rtc_class_ops的ioctl傳回enoioctlcmd的話,核心的rtc子系統會
實作這些指令的方法。不需要自己實作的指令有:
* rtc_rd_time, rtc_set_time read_time/set_time
* rtc_alm_set, rtc_alm_read, rtc_wkalm_set, rtc_wkalm_rd 調用set_alarm/read_alarm
* rtc_irqp_set, rtc_irqp_read 調用 irq_set_freq來實作。如果不支援修改中斷頻率,就不要定義這個函數。
* rtc_pie_on, rtc_pie_off 通過irq_set_state來實作。
rtc子系統實作這些指令的方式是調用你編寫的函數,如果根本不提供這些函數的話,也根本不能實作這些指令。
2.read_callback
每次有資料可讀取,read_callback便會被調用。
如果定義了read_callback,使用者空間讀取到的實際是read_callback傳回的值。
檔案/drivers/rtc/rtc-dev.c的rtc_dev_read函數中可了解read_callback與irq_data之間的關系
static ssize_t
rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
。。。。。。。。。。
//這裡睡眠并等待讀取資料
。。。。。。。。。。
if (ret == 0) {//如果讀取到有效資料
/* check for any data updates */
if (rtc->ops->read_callback)
data = rtc->ops->read_callback(rtc->dev.parent, data);//調用read_callback并用它傳回的值更新資料
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;
}
3.中斷處理函數報告事件類型。
rtc支援各種中斷,中斷處理函數中應該向系統中報告中斷的事件類型。
一個rtc中斷處理函數的例子如下:
static irqreturn_t sep0611_rtc_isr(int irq, void *id)
unsigned int int_stat;
struct rtc_device *rdev = id;
void __iomem *base = sep0611_rtc_base;
int_stat = readl(base + sep0611_rtc_int_sts);
writel(int_stat, base + sep0611_rtc_int_sts);
if (int_stat & alarm_flag) {
rtc_update_irq(rdev, 1, rtc_af | rtc_irqf);
}
if (int_stat & samp_flag) {
/*reload the samp_count every time after a samp_int triggers*/
writel(samp_count << 16, base + sep0611_rtc_samp);
rtc_update_irq(rdev, 1, rtc_pf | rtc_irqf);
if (int_stat & sec_flag) {
rtc_update_irq(rdev, 1, rtc_uf | rtc_irqf);
return irq_handled;
相關宏的定義如下:
#define rtc_irqf 0x80 /* any of the following is active */
#define rtc_pf 0x40
#define rtc_af 0x20
#define rtc_uf 0x10
rtc_update_irq的原型是:
void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
spin_lock(&rtc->irq_lock);
rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
spin_unlock(&rtc->irq_lock);
spin_lock(&rtc->irq_task_lock);
if (rtc->irq_task)
rtc->irq_task->func(rtc->irq_task->private_data);
spin_unlock(&rtc->irq_task_lock);
wake_up_interruptible(&rtc->irq_queue);
kill_fasync(&rtc->async_queue, sigio, poll_in);
其中num是上次報告以來中斷發生的次數,events是事件類型掩碼。
4.中斷回調函數
可以看到rtc_update_irq會調用rtc->irq_task->func。
核心子產品可以在rtc上注冊回調函數,rtc報告中斷事件的時候,這個函數會被調用。注冊的函數接口是:
int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
int retval = -ebusy;
if (task == null || task->func == null)
return -einval;
/* cannot register while the char dev is in use */
if (test_and_set_bit_lock(rtc_dev_busy, &rtc->flags))
return -ebusy;
/*注意,當有使用者空間使用該rtc對應的裝置節點的時候不能注冊struct rtc_task,
注冊之後使用者空間無法打開該節點,因為rtc->flags被裝置為rtc_dev_busy。
任何時候rtc都是獨占的 */
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == null) {
rtc->irq_task = task;
retval = 0;
spin_unlock_irq(&rtc->irq_task_lock);
clear_bit_unlock(rtc_dev_busy, &rtc->flags);
return retval;
其中 struct rtc_task的定義如下:
typedef struct rtc_task {
void (*func)(void *private_data); //回調函數
void *private_data; //傳給回調函數的參數
} rtc_task_t;
注冊 rtc_class_ops
編寫好 rtc_class_ops之後,可以對它進行注冊。注冊函數的原型如下:
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
第一個參數是為rtc指定的名字,第二個參數是rtc的父裝置節點,一般是paltform_device的dev成員。
第四個參數是擁有者子產品指針,一般傳入this_module。
注冊成功的話傳回struct rtc_device指針。注冊成功之後往往把dev的drvdata指向rtc的狀态資訊結構體。
登出
登出傳入的參數是注冊時傳回的指針。
void rtc_device_unregister(struct rtc_device *rtc);
登出之後要做和注冊時相反的工作,比如把dev的drvdata設為null,并釋放其它的資源。
從其它子產品中通路rtc
/drivers/rtc/interface.c定義了可供其它子產品通路的接口。
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);//讀取時間
int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);//設定時間
//傳入1900年以來的秒數來設定rtc.隻要rtc的rtc_class_ops實作了read_time/set_time,
//rtc_class_ops就不需要自己定義set_mmss成員。
int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs);
int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled);//設定定時中斷的開關
unsigned long num, unsigned long events);//中斷到來後,這個函數被調用來更新資料。
struct rtc_device *rtc_class_open(char *name);//使用名字打開rtc裝置并傳回struct rtc_device *。
void rtc_class_close(struct rtc_device *rtc);//打開rtc裝置。
int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task);//注冊中斷回調函數。此時rtc被獨占,使用者空間無法打開。
void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task);//登出中斷回調函數。
//設定周期中斷的頻率
//這裡傳入的task應該是已經使用rtc_irq_register注冊國的task。
int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq);
//打開或關閉周期中斷。
////這裡傳入的task應該是已經使用rtc_irq_register注冊國的task
int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)