sep0718 key driver. * * Changelog: * 1-June-2010 LSF Initial version * * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/interrupt.h> #include <linux/time.h> #include <linux/spinlock_types.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <asm/irq.h> #include <mach/gpio.h> #define COL1_INT 15 //中断号 #define COL2_INT 14 #define COL3_INT 13 #define KEY_MAJOR 200 //主设备号 #define MAX_KEY_BUF 16 //按键缓冲区的大小 #define KEY_XNUM 3 #define KEY_YNUM 3 #define KEY_UP 0 #define KEY_DOWN 1 #define KEY_UNSURE 2 #define KEY_TIMER_DELAY_JUDGE 4 //判断是否有按键按下 jiffes #define KEY_TIMER_DELAY_LONGTOUCH 10 //判断是否长时间按下 jiffes static int key_map[] = {0,1,2,3,4,5,6,7,8}; struct keydev { unsigned int keystatus; //按键状态 unsigned int buf[MAX_KEY_BUF]; //按键缓冲区 unsigned int write,read; //按键缓冲区头和尾 wait_queue_head_t wq; //等待队列 struct cdev cdev; } ; struct keydev *key_dev; //键盘结构体 struct timer_list key_timer; //定时器 //开启键盘中断 static void unmaskkey(void) { SEP0718_INT_ENABLE(COL1_INT); SEP0718_INT_ENABLE(COL2_INT); SEP0718_INT_ENABLE(COL3_INT); } //关闭键盘中断 static void maskkey(void) { SEP0718_INT_DISABLE (COL1_INT) ; SEP0718_INT_DISABLE (COL2_INT) ; SEP0718_INT_DISABLE (COL3_INT) ; } static void sep0718_key_setup(void) { maskkey(); //关闭键盘中断 sep0718_gpio_cfgpin( E , 01 , 6 , 1 ,1); sep0718_gpio_cfgpin( E , 01 , 7 , 1 ,1); sep0718_gpio_cfgpin( E , 01 , 8 , 1 ,1); //set sel :general ; dir : output sep0718_gpio_setpin( E , 0 ,6); sep0718_gpio_setpin( E , 0 ,7); sep0718_gpio_setpin( E , 0 ,8); //pin level :low sep0718_gpio_cfgpin( I , 01 , 13 , 0 ,1); sep0718_gpio_cfgpin( I , 01 , 14 , 0 ,1); sep0718_gpio_cfgpin( I , 01 , 15 , 0 ,1); //dir : input sep0718_gpio_cfgpin( I , 10 , 13 , 11 ,1); sep0718_gpio_cfgpin( I , 10 , 14 , 11 ,1); sep0718_gpio_cfgpin( I , 10 , 15 , 11 ,1);//interrupt type: lowlevel triggered unmaskkey(); } static irqreturn_t sep0718_key_irqhandler(int irq, void *dev_id) { int i ; int row_num = -1; int row = -1; int col = -1; int irq_value = 0; int key_value = 0; maskkey(); //关闭键盘中断 SEP0718_INT_CLR(irq); //清除相应的外部中断signal sep0718_gpio_cfgpin( I,11,0,0xe000,-1 ); // *(volatile unsigned long*)GPIO_PORTI_INTCLR_V = 0xe000 ; key_dev->keystatus = KEY_UNSURE; one: mdelay(10); irq_value = ( sep0718_gpio_getpin(I,-1) & 0xe000 ) ; //读取中断口数值 if (irq_value != 0xe000) //如果有低电平,表示键盘仍然有键被按着 //FP { if (key_dev->keystatus == KEY_UNSURE) { key_dev->keystatus = KEY_DOWN; //读取键盘的位置 col = irq -13 ; for (i=0; i<KEY_YNUM; i++) { row_num = 6+i ; sep0718_gpio_setpin( E,1,row_num ); if ( sep0718_gpio_getpin( I,irq) ) { row = i + 1 ; } sep0718_gpio_setpin( E,0,row_num ); } key_value = col*3 + row ; printk("row is %d\n col is %d\n key_value is %d\n",row,col,key_value); if ( key_value > 0 ) { key_dev->buf[key_dev->write] = key_map[key_value]; if (++(key_dev->write) == MAX_KEY_BUF) //按键缓冲区循环存取 { key_dev->write = 0; } } wake_up_interruptible(&(key_dev->wq)); goto one ; //检测是否持续按键 } else //一定是键按下 { goto one ; } } else //键已抬起 { key_dev->keystatus = KEY_UP; unmaskkey(); //打开键盘中断 } return IRQ_HANDLED; } static ssize_t sep0718_key_read(struct file *filp, char __user *buff, size_t size, loff_t *ppos) { int total_num = 0; unsigned long err; int i; int buffer[17] = {0}; printk("enter into sep0718_key_read !!!!!\n"); retry: if ((key_dev->write) != (key_dev->read)) //当前缓冲队列中有数据 { if ((key_dev->write) > (key_dev->read)) { total_num = (key_dev->write) - (key_dev->read); } else { total_num = (16 - key_dev->read) + key_dev->write; } if (size > total_num) { size = total_num; } //上面这一部分主要功能是计算出缓存中还有中数据的大小,并用size来表示 buffer[0] = size; for (i=1; i<(size+1); i++) { buffer[i] = key_dev->buf[key_dev->read]; if (++(key_dev->read) == MAX_KEY_BUF) { key_dev->read = 0; } } err = copy_to_user(buff, (char *)buffer, (size+1)*4); //将缓存key_dev->buf[ ]中的数据全部拷贝到buffer[ ]数组中,然后通过copy_to_user()函数将buffer[ ]中的数据由内核拷贝到用户区,结束时key_dev->read == key_dev->write !!! 在键盘测试程序中,由于每次中按键一次就执行测试程序,故按键值显示的始终是当前按键值! return err ? -EFAULT : 0; } else //当前缓冲队列中没有数据 { if(filp->f_flags & O_NONBLOCK) //假如用户采用的是非堵塞方式读取 return -EAGAIN; interruptible_sleep_on(&(key_dev->wq)); //用户采用阻塞方式读取,调用该函数使进程睡眠 goto retry; } } static ssize_t sep0718_key_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { return 0; } static int sep0718_key_open(struct inode *inode, struct file *filp) { sep0718_key_setup(); return 0; } static int sep0718_key_release(struct inode *inode, struct file *filp) { return 0; } static struct file_operations sep0718_key_fops = { .owner = THIS_MODULE, .read = sep0718_key_read, .write = sep0718_key_write, .open = sep0718_key_open, .release = sep0718_key_release, }; static int sep0718_request_irqs(void) //FP { //申请中断 if (request_irq(COL1_INT,sep0718_key_irqhandler,IRQF_DISABLED,"0718KEY",NULL)) goto irq0_fail; if (request_irq(COL2_INT,sep0718_key_irqhandler,IRQF_DISABLED,"0718KEY",NULL)) goto irq1_fail; if (request_irq(COL3_INT,sep0718_key_irqhandler,IRQF_DISABLED,"0718KEY",NULL)) goto irq2_fail; return 0; //出错处理 irq2_fail: free_irq(COL3_INT,NULL); irq1_fail: free_irq(COL2_INT,NULL); irq0_fail: free_irq(COL1_INT,NULL); return -1; } static void sep0718_free_irqs(void) { free_irq(COL1_INT,NULL); free_irq(COL2_INT,NULL); free_irq(COL3_INT,NULL); } static int __init sep0718_key_init(void) { int err,result; dev_t devno; devno = MKDEV(KEY_MAJOR, 0); result = register_chrdev_region(devno, 1, "sep0718_key"); //向系统静态申请设备号 if (result < 0) { return result; } key_dev = kmalloc(sizeof(struct keydev), GFP_KERNEL); if (key_dev == NULL) { result = -ENOMEM; unregister_chrdev_region(devno, 1); return result; } memset(key_dev,0,sizeof(struct keydev)); //初始化 if(sep0718_request_irqs()) //注册中断函数 { unregister_chrdev_region(devno,1); kfree(key_dev); return -1; } cdev_init(&key_dev->cdev, &sep0718_key_fops); key_dev->cdev.owner = THIS_MODULE; key_dev->keystatus = KEY_UP; init_waitqueue_head(&(key_dev->wq)); err = cdev_add(&key_dev->cdev, devno, 1); //向系统注册该字符设备 if (err) { unregister_chrdev_region(devno,1); kfree(key_dev); sep0718_free_irqs(); return err; } return 0; } static void __exit sep0718_key_exit(void) { sep0718_free_irqs(); cdev_del(&key_dev->cdev); kfree(key_dev); unregister_chrdev_region(MKDEV(KEY_MAJOR, 0), 1); } module_init(sep0718_key_init); module_exit(sep0718_key_exit); MODULE_AUTHOR("LSF"); MODULE_LICENSE("GPL");
浙公网安备 33010602011771号