#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/wait.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/uaccess.h> //copy_to_user #include <linux/jiffies.h> //内核时钟 #include <asm/atomic.h> //atomic_t #include <mach/regs-gpio.h> //S3C2410_GPF0等的定义 #include <mach/hardware.h> //s3c2410_gpio_getpin等的定义 #include <mach/irqs.h> //IRQ_EINT0等的定义 #define DEV_NAME "fl2440_button_driver" #define KEY_COUNT 4 //按键的个数 #define MAX_KEY_BUF 16 //缓冲区的长度 #define KEY_UP 0 //按键的状态 抬起 #define KEY_DOWN 1 //按下 #define KEY_DOWN_INT 2 //按下,来自中断 #define DELAY_1 HZ/50 #define DELAY_2 HZ/20 #define IS_KEY_DOWN(x) !s3c2410_gpio_getpin(key_infos[x].gpio) typedef unsigned char KEY_RET; struct KEY_DEV { unsigned int key_status[KEY_COUNT];//四个键的状态,应当被初始化为抬起 KEY_RET key_buffer[MAX_KEY_BUF];//按键缓冲区,大小为16个字符 unsigned int head,tail;//按键缓冲区的头和尾 wait_queue_head_t wq;//等待队列 struct cdev cdev;//cdev结构体 }key_dev; static struct timer_list key_timers[KEY_COUNT];//去抖动定时器 //按键的硬件结构体 struct KEY_INFO { int irq;//中断号 unsigned long gpio;//gpio引脚地址 char key_value;//键值 char key_name[6]; }key_infos[KEY_COUNT]= { {IRQ_EINT0,S3C2410_GPF0,'A',"key_a"}, {IRQ_EINT2,S3C2410_GPF2,'B',"key_b"}, {IRQ_EINT3,S3C2410_GPF3,'C',"key_c"}, {IRQ_EINT4,S3C2410_GPF4,'D',"key_d"}, }; int dev_major=0; //主设备号 atomic_t need_request_irq=ATOMIC_INIT(1); irqreturn_t interrupt_handler(int irq,void* dev_id) { int key=(int)dev_id; disable_irq(key_infos[key].irq); key_dev.key_status[key]=KEY_DOWN_INT; key_timers[key].expires=jiffies+DELAY_1; add_timer(&key_timers[key]); return IRQ_HANDLED; } static int dev_open(struct inode* inode,struct file *filp) { int i=0; printk("this is %s /n",__FUNCTION__); filp->private_data=container_of(inode->i_cdev,struct KEY_DEV,cdev); if(atomic_dec_and_test(&need_request_irq)) //如果是第一次打开,就注册中断 { atomic_add(2,&need_request_irq); for(i=0;i<KEY_COUNT;i++) { if(request_irq(key_infos[i].irq,interrupt_handler,IRQF_TRIGGER_LOW|IRQF_DISABLED,key_infos[i].key_name,(void*)i)) goto error0; } } key_dev.head=key_dev.tail=0; return 0; error0: printk("request_irq error,%d!/n",i); for(--i;i>=0;i--) free_irq(key_infos[i].irq,(void*)i); return -EBUSY; } static int dev_release(struct inode *inode,struct file *filp) { int i=0; printk("this is %s /n",__FUNCTION__); atomic_dec(&need_request_irq); for(i=0;i<KEY_COUNT;i++) free_irq(key_infos[i].irq,(void*)i); return 0; } ssize_t dev_read(struct file *filp,char __user *userp,size_t count,loff_t *offp) { int c,i; if(key_dev.tail==key_dev.head)//没有数据 { if(filp->f_flags & O_NONBLOCK)//非阻塞的 return -EAGAIN; if(wait_event_interruptible(key_dev.wq,key_dev.tail!=key_dev.head)) return -ERESTARTSYS; } c=min(key_dev.tail-key_dev.head,count); copy_to_user((void*)userp,key_dev.key_buffer,c); //移动剩下的按键值到前面去 for(i=c;i<=key_dev.tail;i++) { key_dev.key_buffer[i-c]=key_dev.key_buffer[i]; } key_dev.tail-=c; return 0; } struct file_operations fops= { .owner=THIS_MODULE, .open=dev_open, .release=dev_release, .read=dev_read, }; //记录按键值,唤醒等待队列 void key_event(int key) { if(key_dev.tail-key_dev.head>=MAX_KEY_BUF)//缓冲区已满 return ; key_dev.key_buffer[key_dev.tail++]=key_infos[key].key_value; wake_up_interruptible(&key_dev.wq); //唤醒等待队列 } //定时器处理函数 void key_timer_function(unsigned long arg) { int key=arg; if(IS_KEY_DOWN(key)) { if(key_dev.key_status[key]==KEY_DOWN_INT) { key_dev.key_status[key]=KEY_DOWN; key_timers[key].expires=jiffies+DELAY_2; key_event(key);//记录按键值,唤醒等待队列 add_timer(&key_timers[key]); } else { key_timers[key].expires=jiffies+DELAY_2; add_timer(&key_timers[key]); } } else //按键抬起 { key_dev.key_status[key]=KEY_UP; enable_irq(key_infos[key].irq); } } static int __init dev_init(void) { dev_t dev_id; int err,i; err=alloc_chrdev_region(&dev_id,0,KEY_COUNT,DEV_NAME); if(err) goto error0; dev_major=MAJOR(dev_id); cdev_init(&key_dev.cdev,&fops); key_dev.cdev.owner=THIS_MODULE; err=cdev_add(&key_dev.cdev,dev_id,KEY_COUNT); if(err) goto error2; for(i=0;i<KEY_COUNT;i++) { init_timer(&key_timers[i]); key_timers[i].data=i; key_timers[i].function=key_timer_function; key_dev.key_status[i]=KEY_UP; } init_waitqueue_head(&key_dev.wq); printk("init module successful!/n"); return 0; error2: cdev_del(&key_dev.cdev); unregister_chrdev_region(dev_id,KEY_COUNT); error0: return err; } static void __exit dev_exit(void) { cdev_del(&key_dev.cdev); unregister_chrdev_region(MKDEV(dev_major,0),KEY_COUNT); printk("exit module!/n"); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL");