目的是實現,通過板子上一按鈕來控制系統恢復初始配置。 其實也就是在應用層將備份文件 覆蓋 配置文件這一個操作而已。
按鍵恢復默認配置,其基本的思想是 中斷+ 應用阻塞。
前前後後花了挺多的時間,看了中斷的理論,看了寄存器的配置,還去看輸入子系統,有些步驟是不必要的,但是做的時候並不知道不需要。
最後的實驗,代碼的大部分都是借用人家的。自己僅看的懂代碼而已,如果自己去寫一份,估計寫不出來。
連接如下:http://wenku.baidu.com/view/c82df93283c4bb4cf7ecd181.html
自己在看對方代碼的時候,有許多地方不太明白,因此把查閱的一些信息也都寫進代碼的註釋中了。
另外poll 是一種監聽機制,對自己方案的實現沒有任何作用(參閱部分資料後得出的結論)。所以就沒有註冊這個方法。
關於寄存器的操作時看 datasheet裏的,不過操作系統做了這部分的工作,自己畫蛇添足了。 而關於清中斷這一步,都說是要手動進行的,查了半天的資料都沒有看到相關的配置,於是也就放棄了。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/regs-irq.h>
#define DEVICE_NAME "RESTORE_BUTTON"
//#define S3C2410_GPF2 162
struct button_irq_desc
{
int irq;
int pin;
int pin_setting;
char * name;
};
// 這一部分還得看看具體情形纔好。2不是被佔用了麼?被加密芯片
//IRQ_EINT2 = 16+2 =18
//S3C2410_GPF2 = 32x5+2 =162
//S3C2410_GPF2_EINT2 = 0x02 << 4
//觸發設置爲上升沿觸發,因此
static struct button_irq_desc button=
{
IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,"RESTORE_BUTTON"
};
//設置等待隊列
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
//按鍵進行記錄
static volatile int ev_press = 0;
//觸發設置爲下降沿觸發
//中斷處理函數,由中斷號和中斷結構體組成。
static irqreturn_t irq_interrupt(int irq,void *dev_id)
{
printk("%s",dev_id); // 原先中斷有問題的,就是kernel panic,加了這句竟然莫名好了,摸不着頭腦。 沒有再試驗了
struct button_irq_desc * button_irq = (struct button_irq_desc*) dev_id;
int down;
// 這一部分的工作 爲清理中斷的工作,是否也被操作系統做完了?
//unsigned long srcpnd;
// 進入中斷處理函數,現在可以清中斷了。
//srcpnd = __raw_readl(S3C2410_SRCPND);
// 取反之後 再與操作
//srcpnd &=(~(1<<2));
//__raw_writel(srcpnd,S3C2410_SRCPND);
// 取值,值並不一定爲1,可能爲其他數值,但是不影響爲真。
down=s3c2410_gpio_getpin(button_irq->pin);
if(!down)
{
// 記錄按鍵被按下
ev_press=1;//;
//喚醒指定的註冊在等待隊列button_waitq 上的進程。該函數不能直接的立即喚醒進程,
//而是由調度程序轉換上下文,調整爲可運行狀態。
wake_up_interruptible(&button_waitq);
}
// 表示中斷處理結束
return IRQ_RETVAL(IRQ_HANDLED);
}
//register the interrupt,when failed ,quit;
static int irq_open(int irq,void *dev_id)
{
int err=0;
s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2);
err = request_irq(button.irq,irq_interrupt,IRQ_TYPE_EDGE_FALLING,button.name,(void*)&button);
//對應中斷號,中斷處理函數,中斷觸發,中斷名,設備結構體
// 正常時返回0 ,負責返回對應錯誤的負值。
if(err)
return -EBUSY;
return 0;
}
//釋放已經註冊的中斷
static int irq_close(struct inode* inode,struct file *file)
{
free_irq(button.irq,(void*)&button);
return 0;
};
static volatile char key_values[]={'0','0','0'}
static int irq_read(struct file* file,char __user *buff,size_t count)//)
{
//3unsigned long err;
//ev_press爲0,表明按鈕未被按下
if (!ev_press)
{
// nonblock 爲非阻塞的意思
// 即先判斷文件描述符是否支持阻塞
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
// 支持阻塞,則進入阻塞隊列,等待被喚醒
// 若ev_press 爲1的話,則被喚醒
else
//將當前進程加入到等待隊列button_waitq中,並在內部訪問ev_press看值是否成立。
// 將標誌位設置爲TASK_INTTUPTABLE 並從runqueue 中刪除,同時調用onshedule
// 當前進程則進入等待隊列,不被執行,即睡眠了。
wait_event_interruptible(button_waitq, ev_press);
}
// 將ev_press 重置爲0
ev_press = 0;
// 將值傳遞到用戶空間,數據已經被讀取。
// 需要改動
err = copy_to_user(buff, (const void *)key_values,count));
// 即完成讀取這個過程即可,並不需要真正的數據傳輸
// 這裏返回的是什麼?
//return err ? -EFAULT : min(sizeof(key_values), count);
// 直接返回0 ,表示程序運行完了。即可。
return 0;
};
//監聽函數,即監聽當前進程,這裏沒有放入操作列表中。
//這裏沒有使用監聽函數實現
static unsigned int irq_poll(struct file *file,struct poll_table_struct * wait)
{
unsigned int mask = 0;
// 將當前進程放入等待隊列中
// poll_wait()函數會監測進程隊列button_waitq裏的進程
// 它的作用就是把當前進程添加到wait參數指定的等待列表(poll_table)中
// 首先調用poll_wait將等待隊列添加到wait結構中
poll_wait(file,&button_waitq,wait);
if(ev_press)
mask |=POLLIN|POLLRDNORM; // 有數據可讀
return mask; // 返回事件記錄,若是0表示等待事件超時。
};
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = irq_open,
.release = irq_close,
.read = irq_read,
// .poll = irq_poll;
};
static struct miscdevice misc=
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
//unsigned long intmod;
//unsigned long intmask;
ret = misc_register(&misc);
printk(DEVICE_NAME" initialized\n");
if(ret<0){
printk("Add cdev failed!\n");
}
// 查到資料說寄存器的設置,操作系統已經做好了,自己這一部分的工作就有點畫蛇添足了。
// 對中斷寄存器的設置,初始化部分
// INTMOD 中斷模式寄存器,設置爲快中斷,爲1
// INTMASK 中斷屏蔽寄存器,設置爲0,表示響應中斷
// 其中還要對寄存器SRCPEND進行中斷清除,具體在代碼那個位置還需要商榷
// 要定位這些寄存器的位置在哪裏,並且進行設置。
//#define S3C2410_SRCPND S3C2410_IRQREG(0x000)
//#define S3C2410_INTMOD S3C2410_IRQREG(0x004)
//#define S3C2410_INTMSK S3C2410_IRQREG(0x008)
// 寄存器設置使用方法。
// 設置管腳爲中斷模式
//設置中斷模式和中斷屏蔽
//intmod = __raw_readl(S3C2410_INTMOD);
//將INTMOD 相應位 設置爲1,即爲快中斷,同時寫回寄存器中。
//具體哪一位,看 S3C2440 datasheet
//intmod = intmod|(1<<2);
//__raw_writel(intmod,S3C2410_INTMOD);
//將中斷服務設置爲可服務
//其他位置保持不變。
//intmask = __raw_readl(S3C2410_INTMSK);
//intmask = intmask&(~(1<<2));
//__raw_writel(intmask,S3C2410_INTMSK);
return ret;
}
static int __exit dev_exit(void)
{
misc_deregister(&misc);
return 0;
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("csw");
MODULE_DESCRIPTION("IRQ for restore IP");
/* 如果當前不可讀,那麼在sys_poll->do_poll中當前進程就會睡眠在等待隊列上,
* 這個等待隊列是由驅動程序提供的(就是poll_wait中傳入的那個)。當可讀的時候,
* 驅動程序可能有一部分代碼運行了(比如驅動的中斷服務程序),那麼在這部分代碼中,
* 就會喚醒等待隊列上的進程,也就是之前睡眠的那個,當那個進程被喚醒後do_poll會再一次的
* 調用驅動程序的poll函數,這個時候應用程序就知道是可讀的了。
* POOL方法就是用來支持非阻塞式的訪問,當然是立即返回,但是它會把這次請求放入一個等待
* 隊列中,當某個條件滿足時,內核會通知應用程序(應用程序的select函數會感知),然後就
* 會接着select操作
*poll_wait不會掛起當前進程,而是把自己註冊到某個事件等待隊列中.
*poll_wait()是用在select系統調用中的.
*/
應用程序部分就沒貼了,因爲忘記從linux 的 home 路徑下拷貝過來了。
應用程序主要實現的就是去讀取對應的設備文件,當讀到字符的時候,就實現拷貝工作,讀取不到的時候就阻塞睡眠了。