分类: LINUX
2007-12-06 21:32:05
本程序是基于三星s3c2410ARM平台下的按键驱动例程,程序中两个按键分别占用了ARM芯片上的外部中断16和17,程序中设定外部中断为下降沿响应中断.本程序是在2.6.16内核版本下编译测试通过,交叉编译器采用3.4.1版本的arm-linux-gcc.
1.驱动程序文件名为button.c,其源码如下示:
/**********************start*******************************/
#i nclude
#i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #i nclude #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 #i nclude #i nclude #i nclude #i nclude #i nclude 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); close(fileno); return 0; }
/**************************end***********************/
命名为test.c,并交叉编译该文件:
#arm-linux-gcc test.c -o test
将二进制文件同样拷贝到目标板上,运行:
#./test &
即可看到实验效果. |