rct

/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_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);//讀取定時中斷的時間
int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);//設置定時中斷
int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled);//設置定時中斷的開關

void rtc_update_irq(struct rtc_device *rtc,
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)

這是2.6.27.8的內核,沒有給其它模塊提供更新中斷的接口。因爲更新中斷是用軟件模擬的。
2.6.27.8的更新中斷在  定義CONFIG_RTC_INTF_DEV_UIE_EMUL的情況下用軟件定時器模擬 

發佈了4 篇原創文章 · 獲贊 4 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章