Linux2.6键盘驱动汇总:中断方式驱动/扫描方式驱动 Home » 编程开发 » Linux2.6键盘驱动汇总:中断方式驱动/扫描方式驱动 本人蹩作,还望高手们多指出缺点!以下是我的原代码,若有兴趣的朋友们,联系交流!QQ:26451602 /**************************************************************** Copyright(c) 2009-2010,Newkoom Net Tech.Co,.Ltd 模块名称(Filename): kbd_s3c2410.c 项目名称(Projectname): RPM(Remote Power Manager System) 版本号(Version): 1.0.0 创建日期(Date): 2009-8-18 作者(Author): ZMF(Zheng meifu) 功能描述(Description): key button driver for linux2.6.14.1 其他说明(Others): 修改记录(History):2009-10-26: 依据阿潘要求,改为刚按下/按住/释放都要给应用层信号。修改标志:ZMF091026****************************************************************/#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/input.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/hardware.h>#include <linux/devfs_fs_kernel.h>/*RPM按键共6个。GPC0 GPC1 GPC2 GPC3 GPC4 GPC5K1 K2 K3 K4 K5 K6*/#defineCONFIG_TARGET_RPM // 注释掉则开发板按键驱动程序为外部中断方式触发,RPM板则为定时扫描。// 此参数已在内核配置里定义了,此处为调试目的#defineFOR_YL2410BORD// 次参数定义为在开发板调试按键,注释掉为实际RPM目标板// 实际运行以上两个参数都要注释掉。驱动为定时器扫描键盘。#defineZMF0910261#define DEVICE_NAME "keybutton"#define MAX_KEY_BUF 16 //KEY BUFFER SIZE#ifdef CONFIG_TARGET_RPM#ifdef FOR_YL2410BORD#define KEY_NUM 8 //key num #else#define KEY_NUM 6 //key num #endif#else#define KEY_NUM 4 //key num #endif#define INCBUF(x,mod) ((++(x))&((mod)-1))#define KEY_DELAYMS(X) (HZ/(1000/(X)))#define KEY_S_UP 0x1000#define KEY_S_HOLD 0x2000#define KEY_MAJOR 222static dev_t key_major = KEY_MAJOR;#define KEY_FIRST_DOWN 0#define KEY_SECOND_DOWN 1#define KEY_STATUS_UP 2typedef unsigned int KEY_RET;struct key_dev_t{ struct cdev cdev; unsigned int keystatus[KEY_NUM]; KEY_RET buf[MAX_KEY_BUF]; unsigned int head,tail; wait_queue_head_t wq;#ifdef CONFIG_TARGET_RPM unsigned int key_pressed; // =1 key is pressed =0 release#endifint wait_cond;//struct semaphore sam;};struct key_dev_t key_dev;#define BUF_HEAD (key_dev.buf[key_dev.head])#define BUF_TAIL (key_dev.buf[key_dev.tail])#ifdef CONFIG_TARGET_RPMstruct timer_list key_timer;#elsestruct timer_list key_timer[KEY_NUM];#endif//static int wait_cond=0;static struct key_info {#ifdef CONFIG_TARGET_RPM unsigned int pin;//gpio port unsigned int pin_setting; int key_code;//key value#else int irq;//中断号 unsigned int pin;//gpio port unsigned int pin_setting; int key_code;//key value char *name;#endif}key_info_tab[] = {#ifdef CONFIG_TARGET_RPM#ifdefFOR_YL2410BORD{S3C2410_GPF0,S3C2410_GPF0_INP,1}, { S3C2410_GPB6,S3C2410_GPB6_OUTP,2}, { S3C2410_GPG5,S3C2410_GPG5_INP,3}, { S3C2410_GPB7,S3C2410_GPB7_OUTP,4}, { S3C2410_GPG3,S3C2410_GPG3_INP,5}, { S3C2410_GPB6,S3C2410_GPB6_OUTP,6},{ S3C2410_GPG11,S3C2410_GPG11_INP,7}, { S3C2410_GPB7,S3C2410_GPB7_OUTP,8},#else{S3C2410_GPC0,S3C2410_GPC0_INP,1}, { S3C2410_GPC1,S3C2410_GPC1_INP,2}, { S3C2410_GPC2,S3C2410_GPC2_INP,3}, { S3C2410_GPC3,S3C2410_GPC3_INP,4}, { S3C2410_GPC4,S3C2410_GPC4_INP,5}, { S3C2410_GPC5,S3C2410_GPC5_INP,6}, #endif#else{IRQ_EINT0,S3C2410_GPF0,S3C2410_GPF0_EINT0,KEY_UP,"Key Up" }, /*{ IRQ_EINT1,S3C2410_GPF1,S3C2410_GPF1_EINT1,KEY_DOWN,"Key Down"}, { IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,KEY_LEFT,"Key Left" }, */{ IRQ_EINT13,S3C2410_GPG5,S3C2410_GPG5_EINT13,KEY_RIGHT,"Key Right" }, { IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,KEY_ENTER,"Key Enter" }, { IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,KEY_EXIT,"Key Exit" },#endif};#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].pin) == 0)static void key_timer_handler(unsigned long data);#if !defined(CONFIG_TARGET_RPM)static irqreturn_t key_eint_hander(int irq, void *dev_id, struct pt_regs *reg);static int request_irqs(void);#endifstatic int key_open(struct inode *inode, struct file *filp);static int key_release(struct inode *inode, struct file *filp);static ssize_t key_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);static ssize_t key_read(struct file *file,char __user *buffer, size_t count, loff_t *ppos);static const struct file_operations key_fops ={ .owner = THIS_MODULE, .read = key_read, .write = key_write, .open = key_open, .release = key_release,};static int key_open(struct inode *inode, struct file *filp){filp->private_data = &key_dev;key_dev.wait_cond = 1;printk(KERN_NOTICE "key opened/n");return 0;}static int key_release(struct inode *inode, struct file *filp){ printk(KERN_NOTICE "key released/n"); return 0;}static ssize_t key_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos){ return 0;}static ssize_t key_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos){ KEY_RET key_ret; struct key_dev_t *dev; dev = (struct key_dev_t*)filp->private_data;retry: if (key_dev.head != key_dev.tail) { key_ret = BUF_TAIL; dev->tail = INCBUF(dev->tail,MAX_KEY_BUF); copy_to_user(buffer,(char*)&key_ret,sizeof(KEY_RET)); return sizeof(KEY_RET); }else { if (filp->f_flags & O_NONBLOCK) { return -EAGAIN; } //interruptible_sleep_on(&(dev->wq)); //为安全起见,最好不要调用该睡眠函数 //用户采用阻塞方式读取,调用该函数使进程睡眠 wait_event_interruptible(dev->wq,key_dev.wait_cond); key_dev.wait_cond = 0; if (signal_pending(current)) { return -ERESTARTSYS; } goto retry; } return sizeof(KEY_RET);}#if !defined(CONFIG_TARGET_RPM)static irqreturn_t key_eint_hander(int irq, void *dev_id, struct pt_regs *reg){ int key =(int)dev_id; int i; int found = 0; for (i=0; i<ARRAY_SIZE(key_info_tab); i++) { if (key_info_tab[i].irq == irq) { found = 1; break; } } if (!found) { printk(KERN_NOTICE"bad irq %d in button/n", irq); return IRQ_NONE; } //printk(KERN_NOTICE "key_eint_hander:key:%d/n",key); disable_irq(key_info_tab[key].irq); key_dev.keystatus[key] = KEY_FIRST_DOWN; key_timer[key].expires = jiffies + KEY_DELAYMS(10); add_timer(&key_timer[key]); return IRQ_HANDLED;}#endifstatic void keyevent_intimerhandler(KEY_RET key){#ifdef CONFIG_TARGET_RPM#ifdefZMF091026if (key_dev.keystatus[key] == KEY_FIRST_DOWN) {/*刚按下*/BUF_HEAD = key_info_tab[key].key_code;key_dev.key_pressed=1;key_dev.keystatus[key] = KEY_SECOND_DOWN;}else if (key_dev.keystatus[key] == KEY_SECOND_DOWN){//一直按下BUF_HEAD = key_info_tab[key].key_code | KEY_S_HOLD;}else if (key_dev.keystatus[key] == KEY_STATUS_UP){if(key_dev.key_pressed == 1)return; // second press other key,not do anythingBUF_HEAD = key_info_tab[key].key_code | KEY_S_UP;}key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);wake_up_interruptible(&(key_dev.wq));key_dev.wait_cond = 1;#elseint i;if (key_dev.key_pressed==0){// 若之前未按下则为有效键if (key_dev.keystatus[key] == KEY_FIRST_DOWN) {/*刚按下*/BUF_HEAD = key_info_tab[key].key_code;key_dev.key_pressed=1;key_timer.data=0;key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);wake_up_interruptible(&(key_dev.wq));key_dev.wait_cond = 1;}else if (key_dev.keystatus[key] == KEY_STATUS_UP){ /*抬起*/key_dev.keystatus[key] = KEY_FIRST_DOWN;for (i=0; i<KEY_NUM; i++) // 为防止第二次扫到其他键if (i != key)key_dev.keystatus[i] = KEY_STATUS_UP;}} // 若一直按着则不处理#endif#elseif (key_dev.keystatus[key] == KEY_FIRST_DOWN)//刚按下BUF_HEAD = key_info_tab[key].key_code;else if (key_dev.keystatus[key] == KEY_SECOND_DOWN)//一直按下BUF_HEAD = key_info_tab[key].key_code | KEY_S_HOLD;else if (key_dev.keystatus[key] == KEY_STATUS_UP)//抬起BUF_HEAD = key_info_tab[key].key_code | KEY_S_UP;key_dev.head = INCBUF(key_dev.head,MAX_KEY_BUF);wake_up_interruptible(&(key_dev.wq));// 以上程序刚按键和按键保持及释放都会产生键值key_dev.wait_cond = 1;// 若key_read()里使用了wait_event_interruptible()函数,则此条件要打开#endif}static void key_timer_handler(unsigned long data){#ifdef CONFIG_TARGET_RPM#ifdefFOR_YL2410BORD#ifdefZMF091026u32 temp_read;mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); s3c2410_gpio_setpin(S3C2410_GPB6, 0);s3c2410_gpio_setpin(S3C2410_GPB7, 0);if ((ISKEY_DOWN(1-1)||ISKEY_DOWN(3-1)||ISKEY_DOWN(5-1)||ISKEY_DOWN(7-1))&&(key_dev.key_pressed==1) )goto scan_line;if (key_dev.key_pressed==1) {key_dev.key_pressed=0;for (temp_read=0; temp_read<KEY_NUM; temp_read++){if (key_dev.keystatus[temp_read] == KEY_SECOND_DOWN){key_dev.keystatus[temp_read] = KEY_STATUS_UP;keyevent_intimerhandler(temp_read);}key_dev.keystatus[temp_read] = KEY_STATUS_UP; // release to KEY_STATUS_UP}goto scan_comp;}scan_line:s3c2410_gpio_setpin(S3C2410_GPB6, 0);s3c2410_gpio_setpin(S3C2410_GPB7, 1);if (ISKEY_DOWN(1-1)){if(key_dev.key_pressed==0) key_dev.keystatus[1-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(1-1);goto scan_comp;}else if (ISKEY_DOWN(3-1)){if(key_dev.key_pressed==0) key_dev.keystatus[3-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(3-1); goto scan_comp;}else if (ISKEY_DOWN(5-1)){if(key_dev.key_pressed==0) key_dev.keystatus[5-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(5-1); goto scan_comp;}else if (ISKEY_DOWN(7-1)){if(key_dev.key_pressed==0) key_dev.keystatus[7-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(7-1); goto scan_comp;}s3c2410_gpio_setpin(S3C2410_GPB6, 1);s3c2410_gpio_setpin(S3C2410_GPB7, 0);if (ISKEY_DOWN(1-1)){if(key_dev.key_pressed==0) key_dev.keystatus[2-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(2-1); goto scan_comp;}else if (ISKEY_DOWN(3-1)){if(key_dev.key_pressed==0) key_dev.keystatus[4-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(4-1); goto scan_comp;}else if (ISKEY_DOWN(5-1)){if(key_dev.key_pressed==0) key_dev.keystatus[6-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(6-1); goto scan_comp;}else if (ISKEY_DOWN(7-1)){if(key_dev.key_pressed==0) key_dev.keystatus[8-1] = KEY_FIRST_DOWN;keyevent_intimerhandler(8-1); goto scan_comp;}scan_comp:s3c2410_gpio_setpin(S3C2410_GPB6, 1);s3c2410_gpio_setpin(S3C2410_GPB7, 1);#elseu32 temp_read;/*if(down_trylock(&key_dev.sam)){mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); return ;} */mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); s3c2410_gpio_setpin(S3C2410_GPB6, 0);s3c2410_gpio_setpin(S3C2410_GPB7, 0);if ((ISKEY_DOWN(1-1)||ISKEY_DOWN(3-1)||ISKEY_DOWN(5-1)||ISKEY_DOWN(7-1))&&(key_dev.key_pressed==1) )return;if (key_dev.key_pressed==1) {for (temp_read=0; temp_read<KEY_NUM; temp_read++)if (key_dev.keystatus[temp_read] != KEY_STATUS_UP)key_dev.keystatus[temp_read] = KEY_STATUS_UP;key_dev.key_pressed=0;}s3c2410_gpio_setpin(S3C2410_GPB6, 0);s3c2410_gpio_setpin(S3C2410_GPB7, 1);if (ISKEY_DOWN(1-1)){keyevent_intimerhandler(1-1);goto scan_comp;}else if (ISKEY_DOWN(3-1)){keyevent_intimerhandler(3-1); goto scan_comp;}else if (ISKEY_DOWN(5-1)){keyevent_intimerhandler(5-1); goto scan_comp;}else if (ISKEY_DOWN(7-1)){keyevent_intimerhandler(7-1); goto scan_comp;}s3c2410_gpio_setpin(S3C2410_GPB6, 1);s3c2410_gpio_setpin(S3C2410_GPB7, 0);if (ISKEY_DOWN(1-1)){keyevent_intimerhandler(2-1); goto scan_comp;}else if (ISKEY_DOWN(3-1)){keyevent_intimerhandler(4-1); goto scan_comp;}else if (ISKEY_DOWN(5-1)){keyevent_intimerhandler(6-1); goto scan_comp;}else if (ISKEY_DOWN(7-1)){keyevent_intimerhandler(8-1); goto scan_comp;}scan_comp:s3c2410_gpio_setpin(S3C2410_GPB6, 1);s3c2410_gpio_setpin(S3C2410_GPB7, 1);//up(&key_dev.sam);#endif#else// not for yl-2410 devp board#ifdefZMF091026int keyscan_hist = data;int keyscan_read_io;u32 temp_read;temp_read=__raw_readl(S3C2410_GPCDAT);keyscan_read_io = temp_read & 0x3f;if ( keyscan_read_io != 0x3f){switch(keyscan_read_io){case 0x3e:temp_read=0; break;case 0x3d:temp_read=1; break;case 0x3b:temp_read=2; break;case 0x37:temp_read=3; break;case 0x2f:temp_read=4; break;case 0x1f:temp_read=5; break;default:temp_read=0xff; break;}if(temp_read != 0xff){if ( key_dev.key_pressed==0)key_dev.keystatus[temp_read] = KEY_FIRST_DOWN;keyevent_intimerhandler(temp_read);} /*else{for (temp_read=0; temp_read<KEY_NUM; temp_read++)key_dev.keystatus[temp_read] = KEY_STATUS_UP;key_dev.key_pressed=0;}*/ // press double key error!! Not do anythingkey_timer.data=0;}else if ( key_dev.key_pressed==1) {if (++keyscan_hist >=2) {key_dev.key_pressed=0;for (temp_read=0; temp_read<KEY_NUM; temp_read++){if(key_dev.keystatus[temp_read] == KEY_SECOND_DOWN){key_dev.keystatus[temp_read] = KEY_STATUS_UP;keyevent_intimerhandler(temp_read);}key_dev.keystatus[temp_read] = KEY_STATUS_UP; // release to KEY_STATUS_UP}key_timer.data=0;}else key_timer.data=keyscan_hist;}mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); #elseint keyscan_hist = data;int keyscan_read_io;u32 temp_read;temp_read=__raw_readl(S3C2410_GPCDAT);keyscan_read_io = temp_read & 0x3f;if ( keyscan_read_io != 0x3f){switch(keyscan_read_io){case 0x3e:keyevent_intimerhandler(0);break;case 0x3d:keyevent_intimerhandler(1);break;case 0x3b:keyevent_intimerhandler(2);break;case 0x37:keyevent_intimerhandler(3);break;case 0x2f:keyevent_intimerhandler(4);break;case 0x1f:keyevent_intimerhandler(5);break;default:break;}}else if ( key_dev.key_pressed==1) {for (temp_read=0; temp_read<KEY_NUM; temp_read++)if (key_dev.keystatus[temp_read] != KEY_STATUS_UP)key_dev.keystatus[temp_read] = KEY_STATUS_UP;if (++keyscan_hist >=2) {key_dev.key_pressed=0;key_timer.data=0;}else key_timer.data=keyscan_hist;}mod_timer(&key_timer, jiffies+ KEY_DELAYMS(50)); #endif#endif#else int key = data; if (ISKEY_DOWN(key)) { //printk(KERN_NOTICE"key_dev.keystatus[key]:%d/n",key); if (key_dev.keystatus[key] == KEY_FIRST_DOWN)//从中断进入 { keyevent_intimerhandler(key); key_dev.keystatus[key] = KEY_SECOND_DOWN; key_timer[key].expires = jiffies + KEY_DELAYMS(200); add_timer(&key_timer[key]); }else { keyevent_intimerhandler(key); key_timer[key].expires = jiffies + KEY_DELAYMS(200);//HOLD key,每隔200MS发送一次 add_timer(&key_timer[key]); } }else { key_dev.keystatus[key] = KEY_STATUS_UP; keyevent_intimerhandler(key); enable_irq(key_info_tab[key].irq); }#endif}#if !defined(CONFIG_TARGET_RPM)//申请irq中断static int request_irqs(void){ int i; for (i=0; i<ARRAY_SIZE(key_info_tab); i++) { s3c2410_gpio_cfgpin(key_info_tab[i].pin, key_info_tab[i].pin_setting); set_irq_type(key_info_tab[i].irq, IRQT_FALLING);//下降沿触发 if (request_irq(key_info_tab[i].irq, key_eint_hander, SA_INTERRUPT, DEVICE_NAME, (void *)i)) { return -1; } } return 0;}//释放irq中断static void free_irqs(void){ int i; for (i=0; i<ARRAY_SIZE(key_info_tab); i++) { free_irq(key_info_tab[i].irq, (void *)i); }}#endifstatic void key_init_cdev(void){ int i; int err,devno = MKDEV(key_major,0); cdev_init((struct cdev *)&(key_dev.cdev),(struct file_operations *)&key_fops); key_dev.cdev.owner = THIS_MODULE; key_dev.cdev.ops = (struct file_operations *)&key_fops; key_dev.wait_cond = 0; err = cdev_add(&key_dev.cdev, devno, 1); if (err) { printk(KERN_NOTICE "Error %d adding key",err); } key_dev.head = key_dev.tail = 0; for(i=0; i<KEY_NUM; i++) { key_dev.keystatus[i] = KEY_STATUS_UP; } init_waitqueue_head(&(key_dev.wq));#ifdef CONFIG_TARGET_RPMkey_dev.key_pressed=0;init_timer(&key_timer);key_timer.expires = jiffies + KEY_DELAYMS(50);key_timer.function = key_timer_handler; key_timer.data = 0x00;add_timer(&key_timer);#elserequest_irqs();for(i=0; i<KEY_NUM; i++) { key_timer[i].function = key_timer_handler; key_timer[i].data = i; init_timer(&key_timer[i]);}#endif}static int __init key_init(void){ int result; dev_t devno = MKDEV(key_major,0); if (key_major) { result = register_chrdev_region(devno, 1, DEVICE_NAME); }else { result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME); key_major = MAJOR(devno); } if (result < 0) { return result; }//init_MUTEX(&key_dev.sam);#ifdef CONFIG_TARGET_RPMfor (result=0; result<KEY_NUM; result++)s3c2410_gpio_cfgpin(key_info_tab[result].pin, key_info_tab[result].pin_setting);#elses3c2410_gpio_cfgpin(S3C2410_GPB6, S3C2410_GPB6_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPB7, S3C2410_GPB7_OUTP);s3c2410_gpio_setpin(S3C2410_GPB6, 0);s3c2410_gpio_setpin(S3C2410_GPB7, 1); #endif key_init_cdev();#ifdef CONFIG_DEVFS_FS devfs_mk_cdev(MKDEV(key_major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVICE_NAME); #endif printk(KERN_NOTICE "/dev/keybutton inited: www.newkoom.com zhengmeifu@sina.com/n"); return 0;}static void __exit key_exit(void){#if !defined(CONFIG_TARGET_RPM) int i;#endif cdev_del(&key_dev.cdev); unregister_chrdev_region(MKDEV(key_major,0),1);#ifdef CONFIG_DEVFS_FS devfs_remove(DEVICE_NAME); #endif #ifdef CONFIG_TARGET_RPMdel_timer(&key_timer);#else for(i=0; i<KEY_NUM; i++) { del_timer(&key_timer[i]); } free_irqs();#endif}module_init(key_init);module_exit(key_exit);MODULE_AUTHOR("zhengmeifu@sina.com");MODULE_DESCRIPTION("RPM or (yls3c2410 devlp board) keyboard driver");MODULE_LICENSE("Dual BSD/GPL");/*linux驱动开发之key今天做了key的驱动,程序测试ok。key的驱动牵涉的内核知识很多,有中断,内核定时器,阻塞。后续有时间我会再写一个详细的分析。测试如下:先通过cat /proc/devices 插看keybutton的主号为222.mknod /dev/keybutton c 222 0测试程序如下:*//* * Buttons Example for linux s3c2410 rpm */ /*#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/select.h>#include <sys/time.h>#include <errno.h>int main(void){ int buttons_fd; int key_value; buttons_fd = open("/dev/keybutton", 0); if (buttons_fd < 0) { perror("cann’t open device /dev/keybutton"); exit(1); } for (;;) { int ret = read(buttons_fd, &key_value, sizeof key_value); printf("You pressed buttons %d/n", key_value); } close(buttons_fd); return 0;}驱动程序如上:*/ 奢侈地享受旅不问人,行随己意的潇洒。