驱动程序
#include<linux/module.h> #include<linux/init.h> #include<linux/types.h> #include<linux/kdev_t.h> #include<linux/kernel.h> //可以使用ptintk函数 #include<linux/fs.h> #include<linux/interrupt.h> //与中断相关的头文件 #include<linux/cdev.h> //字符设备的注册 #include<linux/err.h> //出错检查函数 #include<linux/slab.h> //与内存分配相关 #include<linux/sched.h> //与中断相关的头文件 #include<asm/io.h> //操作IO端口 #include<linux/wait.h> //等待队列 #include<asm/uaccess.h> //用户和内核空间数据的拷贝 #include<asm/arch/regs-gpio.h> //I/O口的寄存器地址及使用方式(输入、输出、中断或其他) #include<asm/arch/regs-irq.h> //与中断相关的寄存器 #include<asm/arch-s3c2410/irqs.h>//与平台相关的中断号的定义 #include<linux/delay.h> //时间延迟函数 #define key_major 234 //定义主设备号 #define key_minor 0 //定义次设备号 #define DEVICE_NAME "key_interrupt" //static struct cdev * key_cdev; //static dev_t dev_num; static volatile int key_flags=0; //作为等待队列的标志 DECLARE_WAIT_QUEUE_HEAD(queue); //初始化等待队列 static volatile int key_value[]={1,2,3,4}; //按键值 static volatile int key_press=0; //记录那个键被按下了 static dev_t dev_num; //设备号的dev_t表示 static struct cdev *key_cdev; struct key_irq_description{ unsigned int irq; unsigned long flags; const char *dev_name; }; struct key_irq_description key_irq[]={ {IRQ_EINT0,SA_INTERRUPT,"key"}, {IRQ_EINT2,SA_INTERRUPT,"key"}, {IRQ_EINT3,SA_INTERRUPT,"key"}, {IRQ_EINT4,SA_INTERRUPT,"key"}, }; /*中断处理函数*/ static irqreturn_t key_interrupt(int irq, void *dev_id,struct pt_regs *regs) { __raw_writel(__raw_readl(S3C2410_SRCPND)&0xffffffda,S3C2410_SRCPND);//clear srcpnd 0 2 3 4(这个很重要) mdelay(1); key_press= *(int *) dev_id; //把按键值付给key_press key_flags=1; //等待队列标志置一 wake_up_interruptible(&queue); //唤醒等待队列 return IRQ_HANDLED; } /*在这里只申请中断,可以节省中断资源*/ static int key_open(struct inode *inode, struct file *filp) { printk("now in key_open\n"); int ret; int i; for(i=0;i<sizeof(key_irq)/sizeof(key_irq[0]);i++){ ret=request_irq(key_irq[i].irq,key_interrupt,key_irq[i].flags,key_irq[i].dev_name,(void *)&key_value[i]);//申请中断 printk("please irq\n"); if(ret) break; } if(ret){ printk("now at free_irq\n"); i--; for(;i>=0;i--){ free_irq(key_irq[i].irq,(void *)&key_value[i]); return -EBUSY; } } return 0; } /*不使用相关资源时释放中断*/ static int key_close(struct inode *inode, struct file *filp) { int i; for(i=0;i<sizeof(key_irq)/sizeof(key_irq[0]);i++){ free_irq(key_irq[i].irq,(void *)&key_value[i]); } return 0; } /*完成数据从内核空间到用户控件的拷贝*/ static ssize_t key_read(struct file *filp, char __user *buf, size_t count , loff_t * offp) { unsigned long err; wait_event_interruptible(queue,key_flags); //开启等待队列 key_flags=0; //当key_flags=1时可释放等待队列 err=copy_to_user(buf,(const void *)&key_press,count); //注意表示方法 key_press=0; return err?-EFAULT:0; } static struct file_operations key_fops={ .owner = THIS_MODULE, .read = key_read, .open = key_open, .release = key_close, }; /*填充cdev结构*/ static void key_cdev_setup(void) { int err; cdev_init(key_cdev,&key_fops); //初始化已分配到的结构 key_cdev->owner=THIS_MODULE; //设置所有者字段 key_cdev->ops=&key_fops; err=cdev_add(key_cdev,dev_num,1); //告诉内核该结构的信息 if(IS_ERR(&err)){ //检查返回的指针是否是一个错误编码 printk(KERN_NOTICE "Error %d adding key_interrupt",err); } } /*初始化相关寄存器*/ static void initbutton(void) { __raw_writel((__raw_readl(S3C2410_GPFCON)&(~((3<<8)|(3<<6)|(3<<4)|(3<<0))))|((2<<8)|(2<<6)|(2<<4)|(2<<0)),S3C2410_GPFCON) ; //GPF2,0,3,4 set EINT __raw_writel((__raw_readl(S3C2410_EXTINT0)&(~(7|(7<<8)|(7<<12)|(7<<16)))),S3C2410_EXTINT0); __raw_writel((__raw_readl(S3C2410_EXTINT0)|(0|(0<<8)|(0<<12)|(0<<16))),S3C2410_EXTINT0); //set eint0,2,3,4 falling edge int __raw_writel((__raw_readl(S3C2410_EINTPEND)|((1<<4))),S3C2410_EINTPEND); //clear eint 4 __raw_writel((__raw_readl(S3C2410_EINTMASK)&(~((1<<4)))),S3C2410_EINTMASK); //enable eint 4 } /*模块初始化函数,分配设备编号,初始化寄存器,为cdev结构分配内存并初始化cdev结构*/ static int __init key_init(void) { int ret; initbutton(); //初始化相关寄存器 if(key_major){ dev_num=MKDEV(key_major,key_minor); ret=register_chrdev_region(dev_num,1,DEVICE_NAME); //静态分配设备编号 } else{ ret=alloc_chrdev_region(&dev_num,key_minor,1,DEVICE_NAME); //动态分配设备编号 } if(ret<0){ printk(KERN_WARNING "key: can't get key_major %d\n",key_major); return ret; } key_cdev=kmalloc(sizeof(struct cdev),GFP_KERNEL); //为cdev结构分配内存 if(!key_cdev){ printk("can't get this RAM\n"); ret=-ENOMEM; } memset(key_cdev,0,sizeof(struct cdev)); //cdev结构内存区清零 key_cdev_setup(); //注册设备结构cdev return 0; } /*模块退出函数,内部函数执行顺序要与模块初始化的顺序相反*/ static void __exit key_exit(void) { cdev_del(key_cdev); //删除cdev结构 kfree(key_cdev); //释放分配的内存 unregister_chrdev_region(dev_num,1); //释放设备编号 } module_init(key_init); module_exit(key_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("lizhibin"); MODULE_DESCRIPTION("key and interrupt");
测试程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> int main(int argc,char **argv) { int i; int ret; int fd; char press_cnt; printf("Hi I am come in\n"); sleep(5); printf("5 seconds after\n"); fd=open("/dev/key_interrupt",0); printf("key_interrupt by open\n"); if(fd<0) { printf("Can't open /dev/interrupt \n"); return -1; } while(1) { printf("come in while(1)\n"); ret = read(fd,&press_cnt,sizeof(press_cnt)); if(ret<0) { printf("read err !\n"); continue; } if(press_cnt) printf("Key%d has been pressed \n",press_cnt); } }
编译之后下载到开发板,使用insmod加载驱动程序、mknod创建设备节点、运行测试程序之后按下按键时就可以DNW上看到结果了,呵呵。
linux按键中断驱动程序——S3C2440
最新推荐文章于 2024-01-04 14:43:31 发布