天天看點

linux下如何寫RTC驅動

============================================

作者: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)               

繼續閱讀