#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/cdev.h> #include <linux/fcntl.h> #include <linux/delay.h> #include <linux/time.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/devfs_fs_kernel.h> #include <linux/spinlock.h> #include <linux/poll.h> #include <linux/sched.h> #include <asm/irq.h> #include <asm/hardware.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/arch/map.h> #include <asm/arch/regs-gpio.h> #define MAX_KEY_BUF 16 #define UP 0 #define DOWN 1 #define NAME "s3c2410-key" static int key_major; dev_t devno; typedef struct { unsigned char pressure; unsigned char number; } KEY_RET; typedef struct { KEY_RET buf[MAX_KEY_BUF]; int head,tail; wait_queue_head_t wq; spinlock_t lock; struct cdev cdev; } KEY_DEV; KEY_DEV s3c2410_key_dev; #define BUF_HEAD (s3c2410_key_dev.buf[s3c2410_key_dev.head]) #define BUF_TAIL (s3c2410_key_dev.buf[s3c2410_key_dev.tail]) #define DELAY (HZ/100) /* 10 ms */ #define INCBUF(x,mod) ((++(x)) & ((mod) - 1)) static struct timer_list key_timer[4]; static struct key_info { int irq_no; int key_no; } key_info_tab[16] = { {IRQ_EINT19,1},{IRQ_EINT19,2},{IRQ_EINT19,3},{IRQ_EINT19,13}, {IRQ_EINT11,4},{IRQ_EINT11,5},{IRQ_EINT11,6},{IRQ_EINT11,14}, {IRQ_EINT2,7}, {IRQ_EINT2,8}, {IRQ_EINT2,9}, {IRQ_EINT2,15}, {IRQ_EINT0,10},{IRQ_EINT0,11},{IRQ_EINT0,12},{IRQ_EINT0,16}, }; static void gpio_set(void) { s3c2410_gpio_cfgpin(S3C2410_GPE11,S3C2410_GPE11_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPE13,S3C2410_GPE13_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPG2,S3C2410_GPG2_OUTP); s3c2410_gpio_cfgpin(S3C2410_GPG6,S3C2410_GPG6_OUTP); s3c2410_gpio_setpin(S3C2410_GPE11,0);s3c2410_gpio_setpin(S3C2410_GPE13,0); s3c2410_gpio_setpin(S3C2410_GPG2,0);s3c2410_gpio_setpin(S3C2410_GPG6,0); s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_EINT0); s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2); s3c2410_gpio_cfgpin(S3C2410_GPG3,S3C2410_GPG3_EINT11); s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPG11_EINT19); set_irq_type(IRQ_EINT0,IRQT_FALLING);set_irq_type(IRQ_EINT2,IRQT_FALLING); set_irq_type(IRQ_EINT11,IRQT_FALLING);set_irq_type(IRQ_EINT19,IRQT_FALLING); } static void irq_unmask(void) { enable_irq(IRQ_EINT0);enable_irq(IRQ_EINT2); enable_irq(IRQ_EINT11);enable_irq(IRQ_EINT19); } static void irq_mask(void) { disable_irq(IRQ_EINT0);disable_irq(IRQ_EINT2); disable_irq(IRQ_EINT11);disable_irq(IRQ_EINT19); } static unsigned char key_scan(void) { s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_INP); s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_INP); s3c2410_gpio_cfgpin(S3C2410_GPG3,S3C2410_GPG3_INP); s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPG11_INP); s3c2410_gpio_setpin(S3C2410_GPE11,0); s3c2410_gpio_setpin(S3C2410_GPE13,1); s3c2410_gpio_setpin(S3C2410_GPG2,1); s3c2410_gpio_setpin(S3C2410_GPG6,1); if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0a; if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x07; if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x04; if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x01; s3c2410_gpio_setpin(S3C2410_GPE11,1); s3c2410_gpio_setpin(S3C2410_GPE13,0); s3c2410_gpio_setpin(S3C2410_GPG2,1); s3c2410_gpio_setpin(S3C2410_GPG6,1); if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0C; if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x09; if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x06; if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x03; s3c2410_gpio_setpin(S3C2410_GPE11,1); s3c2410_gpio_setpin(S3C2410_GPE13,1); s3c2410_gpio_setpin(S3C2410_GPG2,0); s3c2410_gpio_setpin(S3C2410_GPG6,1); if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x10; if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x0F; if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x0E; if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x0D; s3c2410_gpio_setpin(S3C2410_GPE11,1); s3c2410_gpio_setpin(S3C2410_GPE13,1); s3c2410_gpio_setpin(S3C2410_GPG2,1); s3c2410_gpio_setpin(S3C2410_GPG6,0); if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0B; if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x08; if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x05; if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x02; } static irqreturn_t irq_isr(int irq, void *dev_id, struct pt_regs *reg) { int i = *((int *)dev_id); spin_lock_irq(&(s3c2410_key_dev.lock)); //防止此中断被再次调用发生死锁故而关闭此CPU中断 BUF_HEAD.pressure = DOWN; BUF_HEAD.number = key_scan(); if(i == IRQ_EINT19) {key_timer[0].expires =jiffies+DELAY;add_timer(key_timer);} if(i == IRQ_EINT11) {key_timer[1].expires =jiffies+DELAY;add_timer(key_timer+1);} if(i == IRQ_EINT2) {key_timer[2].expires =jiffies+DELAY;add_timer(key_timer+2);} if(i == IRQ_EINT0) {key_timer[3].expires =jiffies+DELAY;add_timer(key_timer+3);} gpio_set(); spin_unlock_irq(&(s3c2410_key_dev.lock)); return IRQ_HANDLED; } static void key_timer_handler(unsigned long data) { unsigned long i = data; if(BUF_HEAD.number == key_scan()) { if(BUF_HEAD.pressure == DOWN) { BUF_HEAD.pressure = UP; INCBUF(s3c2410_key_dev.head,MAX_KEY_BUF); wake_up_interruptible(&(s3c2410_key_dev.wq)); //唤醒进程 } } gpio_set(); if(i == IRQ_EINT19) {key_timer[0].expires =jiffies+DELAY;add_timer(key_timer);} if(i == IRQ_EINT11) {key_timer[1].expires =jiffies+DELAY;add_timer(key_timer+1);} if(i == IRQ_EINT2) {key_timer[2].expires =jiffies+DELAY;add_timer(key_timer+2);} if(i == IRQ_EINT0) {key_timer[3].expires =jiffies+DELAY;add_timer(key_timer+3);} } static int request_irqs(void) { struct key_info *k=key_info_tab; int i,j; for(i=0;i<16;i+=4) { j=request_irq((k+i)->irq_no,irq_isr,SA_INTERRUPT,NAME,&((k+i)->irq_no)); if(j) return j; } return 0; } static void free_irqs(void) { struct key_info *k=key_info_tab; int i; for(i=0;i<16;i+=4) free_irq((k+i)->irq_no,NULL); } static int s3c2410key_open(struct inode *inode, struct file *file) { s3c2410_key_dev.head = s3c2410_key_dev.tail = 0; gpio_set(); irq_unmask(); return 0; } static int s3c2410key_release(struct inode *inode, struct file *file) { int i; irq_mask(); for(i = 0 ;i < 4 ; i++) del_timer(key_timer+i); return 0; } static ssize_t read_key(KEY_RET *addr,size_t data) { size_t i; spin_lock_irq(&(s3c2410_key_dev.lock)); if(!data) return 0; for(i= 0;i < data;i++) { addr->pressure = BUF_TAIL.pressure; addr->number = BUF_TAIL.number; addr++; INCBUF(s3c2410_key_dev.tail,MAX_KEY_BUF); } spin_unlock_irq(&(s3c2410_key_dev.lock)); return (data*sizeof(KEY_RET)); } static ssize_t s3c2410key_read(struct file *filp,char __user *buffer,size_t count, loff_t *ppos) { KEY_RET ret; next: if(s3c2410_key_dev.head != s3c2410_key_dev.tail) { if(count >= s3c2410_key_dev.head-s3c2410_key_dev.tail) count = s3c2410_key_dev.head-s3c2410_key_dev.tail; count = read_key(&ret,count); if (count) copy_to_user(buffer, (char *)&ret, count); return count; } else { if(filp->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&(s3c2410_key_dev.wq)); goto next; } } static struct file_operations s3c2410_key_ops = { .owner = THIS_MODULE, .open = s3c2410key_open, .release = s3c2410key_release, .read = s3c2410key_read, }; static char __initdata banner[] = "S3C2410 keydriver, 2009 yangdan edit\n"; static int __init s3c2410_key_init(void) { int result,i; printk(banner); result=alloc_chrdev_region(&devno,0,1,"keydev"); key_major =MAJOR(devno); if(result < 0) { printk(KERN_NOTICE "error %d request device no",result); return result; } cdev_init(&s3c2410_key_dev.cdev,&s3c2410_key_ops); s3c2410_key_dev.cdev.owner = THIS_MODULE; result = cdev_add(&s3c2410_key_dev.cdev,devno,1); if(result) { printk(KERN_NOTICE "error %d adding keydev",result); goto error; } for(i=0;i < MAX_KEY_BUF ; i++) s3c2410_key_dev.buf.pressure = UP; init_waitqueue_head(&(s3c2410_key_dev.wq)); #ifdef CONFIG_DEVFS_FS devfs_mk_dir("char/key"); devfs_mk_cdev(MKDEV(key_major,0),S_IFCHR|S_IRUGO|S_IWUSR,"char/key/%s",NAME); #endif result=request_irqs(); if(result) { printk(KERN_NOTICE "error %d request irq",result); goto error; } for(i=0;i < 4 ; i++) { if(i == 0) setup_timer(key_timer+i,key_timer_handler,IRQ_EINT19); else if(i == 1) setup_timer(key_timer+i,key_timer_handler,IRQ_EINT11); else if(i == 2) setup_timer(key_timer+i,key_timer_handler,IRQ_EINT2); else setup_timer(key_timer+i,key_timer_handler,IRQ_EINT0); } return 0; error: unregister_chrdev_region(devno,1); return result; } static void __exit s3c2410_key_exit(void) { free_irqs(); #ifdef CONFIG_DEVFS_FS devfs_remove("char/key/%s", NAME); devfs_remove("char/key"); #endif cdev_del(&s3c2410_key_dev.cdev); unregister_chrdev_region(devno,1); } module_init(s3c2410_key_init); module_exit(s3c2410_key_exit); MODULE_AUTHOR("yangdan, "); MODULE_DESCRIPTION("ucdragon fs2410 key Driver"); MODULE_LICENSE("GPL"); 添加了devfs功能用于自动生成设备文件,所以使用时内核要添加devfs选项 键盘驱动 编写一个键盘驱动程序,有一些细节需要总结。 对于按键和触摸屏等设备需要使用一个环形缓冲区来保存数据(其中缓冲区的长度要选取16的倍数,以方便其自动复位),在这里是用head和tail来分别指向数据写入和数据读出,即当有一个按键按下时对应一个数据写入时则添加head;当应用程序读出一个数据时则添加tail;当head不等于tail时则表明有资源可读取,具体参照按键驱动程序。 在程序中大量使用了中断和通用IO口设置程序,arch/arm/mach-s3c2410/gpio.c中有大量的函数用于根据引脚来配置功能函数可以很方便的简化程序设计: 由于IO口配置时要无缝配置故而要参照以下函数的写法 s3c2410_gpio_cfgpin //设置引脚为输出 输入 外部中断模式 s3c2410_gpio_getcfg //获取配置值以查看引脚处于那种工作模式 s3c2410_gpio_pullup //将引脚设为上拉功能 s3c2410_gpio_setpin //将引脚设置为输出0或1 s3c2410_gpio_getpin //读取引脚输入值当输入为0时则值为0,为非0时则输入为1 在写读函数时一定要将自己定义的结构体转换为字节数来传递给应用程序,否则会出错 |
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |