本人蹩作,还望高手们多指出缺点!以下是我的原代码,若有兴趣的朋友们,联系交流!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 GPC5 K1 K2 K3 K4 K5 K6 */ #define CONFIG_TARGET_RPM // 注释掉则开发板按键驱动程序为外部中断方式触发,RPM板则为定时扫描。 // 此参数已在内核配置里定义了,此处为调试目的 #define FOR_YL2410BORD // 次参数定义为在开发板调试按键,注释掉为实际RPM目标板 // 实际运行以上两个参数都要注释掉。驱动为定时器扫描键盘。 #define ZMF091026 1 #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 222 static dev_t key_major = KEY_MAJOR; #define KEY_FIRST_DOWN 0 #define KEY_SECOND_DOWN 1 #define KEY_STATUS_UP 2 typedef 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 #endif int 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_RPM struct timer_list key_timer; #else struct 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 #ifdef FOR_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); #endif static 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; } #endif static void keyevent_intimerhandler(KEY_RET key){ #ifdef CONFIG_TARGET_RPM #ifdef ZMF091026 if (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 anything 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; #else int 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 #else if (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 #ifdef FOR_YL2410BORD #ifdef ZMF091026 u32 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); #else u32 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 #ifdef ZMF091026 int 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 anything key_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)); #else int 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); } } #endif static 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_RPM key_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); #else request_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_RPM for (result=0; result<KEY_NUM; result++) s3c2410_gpio_cfgpin(key_info_tab[result].pin, key_info_tab[result].pin_setting); #else s3c2410_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_RPM del_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; } 驱动程序如上: */