?? linux2.6內核下的一個按鍵中斷驅動程序示例 .txt
字號:
(原創)linux2.6內核下的一個按鍵中斷驅動程序示例
[ 2006-11-15 21:33:00 | By: Kision ]
本程序是基于三星s3c2410ARM平臺下的按鍵驅動例程,程序中兩個按鍵分別占用了ARM芯片上的外部中斷16和17,程序中設定外部中斷為下降沿響應中斷.本程序是在2.6.16內核版本下編譯測試通過,交叉編譯器采用3.4.1版本的arm-linux-gcc.
1.驅動程序文件名為button.c,其源碼如下示:
/**********************start*******************************/
#i nclude <linux/config.h>
#i nclude <linux/module.h>
#i nclude <linux/version.h>
#i nclude <linux/kernel.h>
#i nclude <linux/init.h>
#i nclude <linux/fs.h>
#i nclude <asm/hardware.h>
#i nclude <asm/delay.h>
#i nclude <asm/uaccess.h>
#i nclude <asm-arm/arch-s3c2410/regs-gpio.h>
#i nclude <asm/io.h>
#i nclude <asm-arm/arch-s3c2410/irqs.h>
#i nclude <asm-arm/irq.h>
#i nclude <linux/interrupt.h>
#i nclude <linux/wait.h>
#define BUTTON_IRQ1 IRQ_EINT16
#define BUTTON_IRQ2 IRQ_EINT17
#define DEVICE_NAME "button"
static int buttonMajor=0;
#define BUTTONMINOR 0
#define MAX_BUTTON_BUF 16
#define BUTTONSTATUS_1 16
#define BUTTONSTATUS_2 17
static unsigned char buttonRead(void);
static int flag=0;
typedef struct {
unsigned int buttonStatus; //按鍵狀態
unsigned char buf[MAX_BUTTON_BUF]; //按鍵緩沖區
unsigned int head,tail; //按鍵緩沖區頭和尾
wait_queue_head_t wq; //等待隊列
} BUTTON_DEV;
static BUTTON_DEV buttondev;
#define BUF_HEAD (buttondev.buf[buttondev.head]) //緩沖區頭
#define BUF_TAIL (buttondev.buf[buttondev.tail]) //緩沖區尾
#define INCBUF(x,mod) ((++(x)) & ((mod)-1)) //移動緩沖區指針
static void (*buttonEvent)(void);
static void buttonEvent_dummy(void) {}
static void buttonEvent_1(void)
{
if(buttondev.buttonStatus==BUTTONSTATUS_2) {
BUF_HEAD=BUTTONSTATUS_2;
}
else {
BUF_HEAD=BUTTONSTATUS_1;
}
buttondev.head=INCBUF(buttondev.head,MAX_BUTTON_BUF);
flag=1;
wake_up_interruptible(&(buttondev.wq));
printk("buttonEvent_1\n");
}
static irqreturn_t isr_button(int irq,void *dev_id,struct pt_regs *regs)
{
printk("Occured key board Inetrrupt,irq=%d\n",irq-44);
switch (irq) {
case BUTTON_IRQ1:buttondev.buttonStatus=BUTTONSTATUS_1;
break;
case BUTTON_IRQ2:buttondev.buttonStatus=BUTTONSTATUS_2;
break;
default:break;
}
buttonEvent();
return 0;
}
static int button_open(struct inode *inode,struct file *filp)
{
int ret;
buttondev.head=buttondev.tail=0;
buttonEvent=buttonEvent_1;
ret=request_irq(BUTTON_IRQ1,isr_button,SA_INTERRUPT,DEVICE_NAME,NULL);
if(ret) {
printk("BUTTON_IRQ1: could not register interrupt\n");
return ret;
}
ret=request_irq(BUTTON_IRQ2,isr_button,SA_INTERRUPT,DEVICE_NAME,NULL);
if(ret) {
printk("BUTTON_IRQ2: could not register interrupt\n");
return ret;
}
return 0;
}
static int button_release(struct inode *inode,struct file *filp)
{
buttonEvent=buttonEvent_dummy;
free_irq(BUTTON_IRQ1,NULL);
free_irq(BUTTON_IRQ2,NULL);
return 0;
}
static ssize_t button_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
{
static unsigned char button_ret;
retry:
printk("retry start\n");
if(buttondev.head!=buttondev.tail) {
button_ret=buttonRead();
copy_to_user(buffer,(char *)&button_ret,sizeof(unsigned char));
printk("the button_ret is 0x%x\n",button_ret);
return sizeof(unsigned char);
}
else {
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
printk("sleep\n");
//interruptible_sleep_on(&(buttondev.wq));//為安全起見,最好不要調用該睡眠函數
wait_event_interruptible(buttondev.wq,flag);
flag=0;
printk("sleep_after\n");
if(signal_pending(current))
{
printk("rturn -ERESTARTSYS\n");
return -ERESTARTSYS;
}
goto retry;
}
return sizeof(unsigned char);
}
static struct file_operations button_fops= {
.owner = THIS_MODULE,
.open = button_open,
.read = button_read,
.release = button_release,
};
static int __init s3c2410_button_init(void)
{
int ret;
set_irq_type(BUTTON_IRQ1,IRQT_FALLING);
set_irq_type(BUTTON_IRQ2,IRQT_FALLING);
buttonEvent=buttonEvent_dummy;
ret=register_chrdev(0,DEVICE_NAME,&button_fops);
if(ret<0) {
printk("button: can't get major number\n");
return ret;
}
buttonMajor=ret;
#ifdef CONFIG_DEVFS_FS
devfs_mk_cdev(MKDEV(buttonMajor,BUTTONMINOR),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
#endif
//buttondev.head=buttondev.tail=0;
buttondev.buttonStatus=BUTTONSTATUS_1;
init_waitqueue_head(&(buttondev.wq));
printk(DEVICE_NAME"initialized\n");
return 0;
}
static unsigned char buttonRead(void)
{
unsigned char button_ret;
button_ret=BUF_TAIL;
buttondev.tail=INCBUF(buttondev.tail,MAX_BUTTON_BUF);
return button_ret;
}
static void __exit s3c2410_button_eixt(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_remove(DEVICE_NAME);
#endif
unregister_chrdev(buttonMajor,DEVICE_NAME);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kision");
MODULE_DESCRIPTION ("the first char device driver");
module_init(s3c2410_button_init);
module_exit(s3c2410_button_eixt);
/*************************end***************************/
2. 當然在編寫2.6內核驅動程序之前應該已經自己建立好一個2.6的內核源碼樹(我這里是基于s3c2410移植的源碼樹,本處該源碼是放在宿主機的/home/src/linux-2.6.16目錄下的),如果沒有的話,那么需要自己去建立好這個源碼樹.自己編寫的模塊化驅動程序可以不放在內核源碼之內,但是此外還需要一個自己編寫一個Makefile文件(該文件和上面的button.c文件應放在同一個目錄下),其內容如下示:
ifneq ($(KERNELRELEASE),)
obj-m :=button.o
else
KERNELDIR ?= /home/src/linux-2.6.16
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
endif
3.在宿主機的終端下,進入驅動程序目錄內,敲入命令:
#make
就會在該目錄下生成button.ko文件,這就是2.6內核下生成的驅動加載模塊,注意是.ko文件,不同于2.4內核下的.o文件.
把該button.ko文件拷貝到目標板上,在minicom終端下進入該文件目錄,敲入:
#insmod button.ko
如果終端顯示有buttoninitialized則表示加載成功.
這時可以用命令lsmod查看動態加載模塊:
#lsmod
當然,可以用如下命令查看devfs文件系統信息:
#cat /proc/devices
如果用卸載該模塊,敲入命令:
#rmmod button
4.加載驅動程序后,可以自己再編寫一個簡單的測試程序,如下:
/**************************start***********************/
#i nclude <sys/stat.h>
#i nclude <fcntl.h>
#i nclude <stdio.h>
#i nclude <sys/time.h>
#i nclude <sys/types.h>
#i nclude <unistd.h>
main()
{
int retval;
int fileno;
int ts, maxfd;
int ret= 0,i,j;
int number;
fileno = open("/dev/button",O_RDWR);
if (fileno == -1) {
printf("open device led errr!\n");
return 0;
}
while(1) {
read(fileno,&number,1);
printf("key=0x%x\n",number);
}
close(fileno);
return 0;
}
/**************************end***********************/
命名為test.c,并交叉編譯該文件:
#arm-linux-gcc test.c -o test
將二進制文件同樣拷貝到目標板上,運行:
#./test &
即可看到實驗效果.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -