📄 fs2410_key_matrix .c
字号:
/* * s3c2410_gpio_button.c * * genric routine for S3C2410-base machine's button * * Based on sa1100_gpio_button.c * * Author: Janghoon Lyu <nandy@mizi.com> * Date : $Date: 2003/03/27 06:28:13 $ * * $Revision: 1.1.2.12 $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * 2002-09-03 Initial code by nandy */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/irq.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <asm/hardware.h>#define DEVICE_NAME "key_matrix"#define KEY_MATRIX_6#ifdef KEY_MATRIX_6#define KEY_MATRIX_COL 6#define KEY_MATRIX_RAW 6#else#define KEY_MATRIX_COL 4#define KEY_MATRIX_RAW 4#endif#define BUFFER_MAX 32static u8 key_buffer[BUFFER_MAX];static int buffer_head, buffer_tail;static wait_queue_head_t buffer_wq;static spinlock_t buffer_lock;static volatile int DeviceOpened;static int DevMajor;/*************************************************************************************/static __inline void set_io1(void){#ifdef KEY_MATRIX_6 // GPF0,GPF2,GPF3 input with pull-up GPFCON &= ~(3 | (3<<4) | (3<<6)); GPFUP &= ~(1 | (1<<2) | (1<<3)); // CPG0,GPG3,GPG11 input, GPG2,GPG6,GPG8,GPG10 output GPGCON = (GPGCON & ~(3 | (3<<6) | (3<<22) | (3<<4) | (3<<12) | (3<<16) | (3<<20))) | (1<<4) | (1<<12) | (1<<16) | (1<<20); GPGUP = (GPGUP & ~(1 | (1<<3) | (1<<11))) | (1<<2) | (1<<6) | (1<<8) | (1<<10); //GPE11,GPE13 output GPECON = (GPECON & ~((3<<22) | (3<<26))) | (1<<22) | (1<<26); GPEUP |= (1<<11) | (1<<13); GPGDAT &= ~((1<<2) | (1<<6) | (1<<8) | (1<<10)); //output 0 GPEDAT &= ~((1<<11) | (1<<13)); //output 0#else // GPF0,GPF2 input with pull-up GPFCON &= ~(3 | (3<<4)); GPFUP &= ~(1 | (1<<2)); // GPG3,GPG11 input, GPG2,GPG6 output GPGCON = (GPGCON & ~((3<<6) | (3<<22) | (3<<4) | (3<<12))) | (1<<4) | (1<<12); GPGUP = (GPGUP & ~((1<<3) | (1<<11))) | (1<<2) | (1<<6);// set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_EN | GPIO_MODE_IN);// set_gpio_ctrl(GPIO_F2 | GPIO_PULLUP_EN | GPIO_MODE_IN);// set_gpio_ctrl(GPIO_G3 | GPIO_PULLUP_EN | GPIO_MODE_IN);// set_gpio_ctrl(GPIO_G11 | GPIO_PULLUP_EN | GPIO_MODE_IN); GPECON = (GPECON & ~((3<<22) | (3<<26))) | (1<<22) | (1<<26); GPEUP |= (1<<11) | (1<<13);// set_gpio_ctrl(GPIO_E11 | GPIO_PULLUP_DIS | GPIO_MODE_OUT);// set_gpio_ctrl(GPIO_E13 | GPIO_PULLUP_DIS | GPIO_MODE_OUT);// set_gpio_ctrl(GPIO_G2 | GPIO_PULLUP_DIS | GPIO_MODE_OUT);// set_gpio_ctrl(GPIO_G6 | GPIO_PULLUP_DIS | GPIO_MODE_OUT); GPGDAT &= ~((1<<2) | (1<<6)); //output 0 GPEDAT &= ~((1<<11) | (1<<13)); //output 0#endif}static __inline void set_io2(void){#ifdef KEY_MATRIX_6 // GPF0,GPF2,GPF3 output GPFCON = (GPFCON & ~(3 | (3<<4) | (3<<6))) | 1 | (1<<4) | (1<<6); GPFUP |= 1 | (1<<2) | (1<<3); // GPG0,GPG3,GPG11 output, GPG2,GPG6,GPG8,GPG10 input GPGCON = (GPGCON & ~(3 | (3<<6) | (3<<22) | (3<<4) | (3<<12) | (3<<16) | (3<<20))) | 1 | (1<<6) | (1<<22); GPGUP = (GPGUP & ~((1<<2) | (1<<6) | (1<<8) | (1<<10))) | 1 | (1<<3) | (1<<11); // GPE11,GPE13 input with pull-up GPECON &= ~((3<<22) | (3<<26)); GPEUP &= ~((1<<11) | (1<<13)); GPFDAT &= ~(1 | (1<<2) | (1<<3)); //output 0 GPGDAT &= ~(1 | (1<<3) | (1<<11)); //output 0#else // GPF0,GPF2 output GPFCON = (GPFCON & ~(3 | (3<<4))) | 1 | (1<<4); GPFUP |= 1 | (1<<2); // GPG3,GPG11 output, GPG2,GPG6 input GPGCON = (GPGCON & ~((3<<6) | (3<<22) | (3<<4) | (3<<12))) | (1<<6) | (1<<22); GPGUP = (GPGUP & ~((1<<2) | (1<<6))) | (1<<3) | (1<<11); // GPE11,GPE13 input with pull-up GPECON &= ~((3<<22) | (3<<26)); GPEUP &= ~((1<<11) | (1<<13)); GPFDAT &= ~(1 | (1<<2)); //output 0 GPGDAT &= ~((1<<3) | (1<<11)); //output 0#endif}static __inline u32 get_io1(void){ u32 val = -1; if(!(GPFDAT&1)) val &= ~1; if(!(GPFDAT&(1<<2))) val &= ~2; if(!(GPGDAT&(1<<3))) val &= ~4; if(!(GPGDAT&(1<<11))) val &= ~8;#ifdef KEY_MATRIX_6 if(!(GPFDAT&(1<<3))) val &= ~0x10; if(!(GPGDAT&1)) val &= ~0x20;#endif return val;}static __inline u32 get_io2(void){ u32 val = -1; if(!(GPEDAT&(1<<11))) val &= ~1; if(!(GPEDAT&(1<<13))) val &= ~2; if(!(GPGDAT&(1<<2))) val &= ~4; if(!(GPGDAT&(1<<6))) val &= ~8;#ifdef KEY_MATRIX_6 if(!(GPGDAT&(1<<8))) val &= ~0x10; if(!(GPGDAT&(1<<10))) val &= ~0x20;#endif return val;}static __inline void init_io(int init){ set_gpio_ctrl(GPIO_E11 | GPIO_PULLUP_DIS | GPIO_MODE_OUT); set_gpio_ctrl(GPIO_E13 | GPIO_PULLUP_DIS | GPIO_MODE_OUT); set_gpio_ctrl(GPIO_G2 | GPIO_PULLUP_DIS | GPIO_MODE_OUT); set_gpio_ctrl(GPIO_G6 | GPIO_PULLUP_DIS | GPIO_MODE_OUT);#ifdef KEY_MATRIX_6 set_gpio_ctrl(GPIO_G8 | GPIO_PULLUP_DIS | GPIO_MODE_OUT); set_gpio_ctrl(GPIO_G10 | GPIO_PULLUP_DIS | GPIO_MODE_OUT);#endif if(1) {//init) { set_external_irq(IRQ_EINT0, EXT_LOWLEVEL, GPIO_PULLUP_EN); set_external_irq(IRQ_EINT2, EXT_LOWLEVEL, GPIO_PULLUP_EN); set_external_irq(IRQ_EINT11, EXT_LOWLEVEL, GPIO_PULLUP_EN); set_external_irq(IRQ_EINT19, EXT_LOWLEVEL, GPIO_PULLUP_EN);#ifdef KEY_MATRIX_6 set_external_irq(IRQ_EINT3, EXT_LOWLEVEL, GPIO_PULLUP_EN); set_external_irq(IRQ_EINT8, EXT_LOWLEVEL, GPIO_PULLUP_EN);#endif } else { set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_EN | GPIO_MODE_EINT); set_gpio_ctrl(GPIO_F2 | GPIO_PULLUP_EN | GPIO_MODE_EINT); set_gpio_ctrl(GPIO_G3 | GPIO_PULLUP_EN | GPIO_MODE_EINT); set_gpio_ctrl(GPIO_G11 | GPIO_PULLUP_EN | GPIO_MODE_EINT);#ifdef KEY_MATRIX_6 set_gpio_ctrl(GPIO_F3 | GPIO_PULLUP_EN | GPIO_MODE_EINT); set_gpio_ctrl(GPIO_G0 | GPIO_PULLUP_EN | GPIO_MODE_EINT);#endif } write_gpio_bit(GPIO_E11, 0); write_gpio_bit(GPIO_E13, 0); write_gpio_bit(GPIO_G2, 0); write_gpio_bit(GPIO_G6, 0);#ifdef KEY_MATRIX_6 write_gpio_bit(GPIO_G8, 0); write_gpio_bit(GPIO_G10, 0);#endif}static __inline int request_irqs(void (*handler)(int, void *, struct pt_regs *)){ int ret; //printk("request_irq : IRQ_EINT0\n"); ret = request_irq(IRQ_EINT0, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail; //printk("request_irq : IRQ_EINT2\n"); ret = request_irq(IRQ_EINT2, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail1; //printk("request_irq : IRQ_EINT11\n"); ret = request_irq(IRQ_EINT11, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail2; //printk("request_irq : IRQ_EINT19\n"); ret = request_irq(IRQ_EINT19, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail3; #ifdef KEY_MATRIX_6 //printk("request_irq : IRQ_EINT3\n"); ret = request_irq(IRQ_EINT3, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail4; //printk("request_irq : IRQ_EINT8\n"); ret = request_irq(IRQ_EINT8, handler, SA_INTERRUPT, DEVICE_NAME, DEVICE_NAME); if(ret) goto fail5;#endif return 0;#ifdef KEY_MATRIX_6fail5: free_irq(IRQ_EINT3, DEVICE_NAME);fail4: free_irq(IRQ_EINT19, DEVICE_NAME);#endiffail3: free_irq(IRQ_EINT11, DEVICE_NAME);fail2: free_irq(IRQ_EINT2, DEVICE_NAME);fail1: free_irq(IRQ_EINT0, DEVICE_NAME);fail: return ret;}static __inline void free_irqs(void){#ifdef KEY_MATRIX_6 free_irq(IRQ_EINT8, DEVICE_NAME); free_irq(IRQ_EINT3, DEVICE_NAME);#endif free_irq(IRQ_EINT19, DEVICE_NAME); free_irq(IRQ_EINT11, DEVICE_NAME); free_irq(IRQ_EINT2, DEVICE_NAME); free_irq(IRQ_EINT0, DEVICE_NAME);}static __inline void enable_irqs(void){ enable_irq(IRQ_EINT0); enable_irq(IRQ_EINT2); enable_irq(IRQ_EINT11); enable_irq(IRQ_EINT19);#ifdef KEY_MATRIX_6 enable_irq(IRQ_EINT3); enable_irq(IRQ_EINT8);#endif}static __inline void disable_irqs(void){ disable_irq(IRQ_EINT0); disable_irq(IRQ_EINT2); disable_irq(IRQ_EINT11); disable_irq(IRQ_EINT19);#ifdef KEY_MATRIX_6 disable_irq(IRQ_EINT3); disable_irq(IRQ_EINT8);#endif}/*************************************************************************************/static volatile int thread_leave;static struct completion thread_dead;static wait_queue_head_t thread_wq;static void key_matrix_isr(int irq, void *dev_id, struct pt_regs *regs){// printk("%d\n", irq); disable_irqs(); wake_up(&thread_wq);}static __inline void buffer_key(u32 no, u32 pressure){ if(!DeviceOpened) return; spin_lock_irq(&buffer_lock); key_buffer[buffer_tail] = no | (pressure ? 0x80 : 0); //printk("buffer key 0x%x\n", key_buffer[buffer_tail]); buffer_tail = (buffer_tail+1) & (BUFFER_MAX-1); if(buffer_tail==buffer_head) buffer_head = (buffer_head+1) & (BUFFER_MAX-1); spin_unlock_irq(&buffer_lock); wake_up_interruptible(&buffer_wq);}static int key_matrix_scan_thread(void *arg){ u16 key_matrix_r0[KEY_MATRIX_COL]; u16 key_matrix_r1[KEY_MATRIX_COL]; for(buffer_head=0; buffer_head<KEY_MATRIX_COL; buffer_head++) key_matrix_r0[buffer_head] = 0xffff; buffer_head = 0; /* we might get involved when memory gets low, so use PF_MEMALLOC */ current->flags |= PF_MEMALLOC; snprintf(current->comm, sizeof(current->comm), "keyscand");#ifndef __rh_config_h__ /* HAVE_NPTL */ spin_lock_irq(¤t->sigmask_lock); sigfillset(¤t->blocked); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock);#else spin_lock_irq(¤t->sighand->siglock); sigfillset(¤t->blocked); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock);#endif daemonize(); while(!thread_leave) { u32 val, val1, key_r, times, stable = 0; DECLARE_WAITQUEUE(wait, current); add_wait_queue(&thread_wq, &wait); set_current_state(TASK_INTERRUPTIBLE); cli(); //must disable irq before enable all external interrupt enable_irqs(); sti(); schedule(); remove_wait_queue(&thread_wq, &wait); //if(thread_leave) // break; // printk("start key matrix scan...\n"); key_r = -1; while(!thread_leave) { times = 4; val1 = -1; while(times) { set_io1(); udelay(10); val = get_io1()&0xffff; set_io2(); udelay(10); val |= get_io2()<<16; if(times!=4) if(val!=val1) { stable = 0; break; } val1 = val; times--; } if(!times) { if(val==key_r) stable += (stable < 10) ? 1 : 0; else stable = 0; key_r = val; } if(stable==3) { int i, j, vary, multi=0, act_cols=0, act_raws=0; for(i=0; i<KEY_MATRIX_COL; i++) if(!(key_r&(1<<i))) act_cols++; if(act_cols>=2) { for(j=0; j<KEY_MATRIX_RAW; j++) if(!(key_r&(1<<(16+j)))) act_raws++; if(act_raws>=2) { printk(KERN_WARNING "multi keys!\n"); multi = 1; } } //printk("%x\n", key_r); for(i=0; i<KEY_MATRIX_COL; i++) { key_matrix_r1[i] = 0xffff; for(j=0; j<KEY_MATRIX_RAW; j++) if(!(((key_r>>(16+j))|(key_r>>i))&1)) key_matrix_r1[i] &= ~(1<<j); vary = key_matrix_r0[i]^key_matrix_r1[i]; if(vary) { //printk("%x,%x\n", key_matrix_r0[i], key_matrix_r1[i]); for(j=0; j<KEY_MATRIX_RAW; j++) if(vary&(1<<j)&&!multi) buffer_key(i*KEY_MATRIX_RAW+j, key_matrix_r1[i]&(1<<j)); key_matrix_r0[i] = key_matrix_r1[i]; } } if(val==-1) { // printk("end key matrix scan...\n"); init_io(0); break; //wait for interrupt } } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/100); } } complete_and_exit(&thread_dead, 0);}static int key_matrix_open(struct inode *inode, struct file *filp){ buffer_head = buffer_tail = 0; init_waitqueue_head(&buffer_wq); DeviceOpened++; MOD_INC_USE_COUNT; return 0;}static int key_matrix_release(struct inode *inode, struct file *filp){ DeviceOpened--; MOD_DEC_USE_COUNT; return 0;}static ssize_t key_matrix_read(struct file *filp, char *buffer, size_t count, loff_t *ppos){retry: if (buffer_head != buffer_tail) { spin_lock_irq(&buffer_lock); copy_to_user(buffer, (char *)key_buffer + buffer_head, 1); buffer_head = (buffer_head+1) & (BUFFER_MAX-1); spin_unlock_irq(&buffer_lock); return 1; } else { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&buffer_wq); if (signal_pending(current)) return -ERESTARTSYS; goto retry; } return 1;}static unsigned int key_matrix_poll(struct file *filp, struct poll_table_struct *wait){ poll_wait(filp, &buffer_wq, wait); return (buffer_head == buffer_tail) ? 0 : (POLLIN | POLLRDNORM); }static struct file_operations key_matrix_fops = { owner: THIS_MODULE, open: key_matrix_open, read: key_matrix_read, release: key_matrix_release,#ifdef USE_ASYNC fasync: key_matrix_fasync,#endif poll: key_matrix_poll,};#ifdef CONFIG_DEVFS_FSstatic devfs_handle_t devfs_dir, devfs_dev;#endifstatic int __init init_key_matrix(void){ int ret; printk("FS2410 key matrix (GPIO) driver\n"); ret = register_chrdev(0, DEVICE_NAME, &key_matrix_fops); if (ret < 0) { printk(DEVICE_NAME "can't get major number!\n"); return ret; } DevMajor = ret; init_io(1); //initialize external irq before request_irq cli(); //disable irq before request all external interrupts ret = request_irqs(key_matrix_isr); if(ret) { sti(); printk("fail to request irq for device!\n"); unregister_chrdev(DevMajor, DEVICE_NAME); return ret; } disable_irqs(); //all irqs are disabled, enable them in key_matrix_scan_thread // printk("0x%x, 0x%x, 0x%x\n", INTMSK, INTPND, SRCPND); init_io(1); //if low-level interrupt present, must clear INTPND!!! // printk("0x%x, 0x%x, 0x%x\n", INTMSK, INTPND, SRCPND); sti(); // printk("HaHa\n"); init_completion(&thread_dead); init_waitqueue_head(&thread_wq); ret = kernel_thread(key_matrix_scan_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); if (ret < 0) { printk("fail to create kernel thread for device!\n"); free_irqs(); //now all irqs are disabled, so needn't cli() return ret; }#ifdef CONFIG_DEVFS_FS devfs_dir = devfs_mk_dir(NULL, "keypad", NULL); devfs_dev = devfs_register(devfs_dir, "0", DEVFS_FL_DEFAULT, DevMajor, 0, S_IFCHR | S_IRUSR | S_IWUSR, &key_matrix_fops, NULL);#endif return 0;}static void __exit exit_key_matrix(void){ thread_leave = 1; wake_up(&thread_wq); wait_for_completion(&thread_dead);#ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_dev); devfs_unregister(devfs_dir);#endif //cli(); //if a interrupt happened, this irq will be disabled in handler, free_irqs(); //so cli() is not required //sti(); unregister_chrdev(DevMajor, DEVICE_NAME);}module_init(init_key_matrix);module_exit(exit_key_matrix);MODULE_LICENSE("GPL");MODULE_AUTHOR("antiscle <hzh12@tom.com>");MODULE_DESCRIPTION("key matrix (GPIO) driver for S3C2410");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -