#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static wait_queue_head_t keypad_read_queue; static struct timer_list keypad_scan_timer; alignedWord *kpd_cookie; struct resource *kpd_res; spinlock_t keypad_lock; struct semaphore keypad_open_sema; #define SCANHZ 50 #define SCAN_JIFF_COUNT (HZ/SCANHZ) #define KEY_TIMEOUT (10*SCAN_JIFF_COUNT) #define BUFFER_SIZE 256 volatile unsigned char kpd_key_buffer[BUFFER_SIZE]; volatile int keypad_buff_count,keypad_buff_top,keypad_buff_bottom; volatile int close_timer; volatile int schedule_or_not; #define NO_SCAN_LINES 2 #define NO_DEC_LINES 7 #define NO_KEYS 14 #include #define AT91C_KEYPAD_PHYS_BASE ((unsigned int)0x4FFA0000) #define AT91C_KEYPAD_SIZE (0x4FFFFFFF - AT91C_KEYPAD_PHYS_BASE + 1) static unsigned short kpd_key_table[NO_SCAN_LINES][NO_DEC_LINES]; static unsigned char key_map[NO_KEYS]; static int kpd_curr_cycle; static int init_keytable(){ int i,j; for (i=0;iEBI_CSA|=AT91C_EBI_CS3A_SMC; AT91_SYS->EBI_CFGR = 0; AT91_SYS->EBI_SMC2_CSR[3]=0x220037b7; } inline unsigned int read_first_row(){ unsigned int res=0; res = kpd_cookie[0] & 0x7F; return res; } inline unsigned int read_second_row(){ unsigned int res=0; res = (kpd_cookie[0]>>16) & 0x7F; return res; } void scan_kbd(unsigned long ignore){ static int last_key=0; static int kpd_timeout=0; unsigned short var1=0; unsigned short var2=0; int pos=0; int key; unsigned long flags=0; int wake_up=0; if(close_timer){ return; } if(!schedule_or_not){ goto out; } kpd_timeout+=SCAN_JIFF_COUNT; var1=read_first_row(); var2=read_second_row(); if(var1){ pos=0; kpd_curr_cycle=0; while((pos<(NO_DEC_LINES-1)) && (!((var1>>pos)&0x01))){ pos++; } key=key_map[kpd_key_table[kpd_curr_cycle][pos]]; if ((last_key==key)&&(kpd_timeout= BUFFER_SIZE) keypad_buff_top=0; keypad_buff_count++; // Since we have exceeded the buffer size if (keypad_buff_count>BUFFER_SIZE){ keypad_buff_count=BUFFER_SIZE; keypad_buff_bottom++; if (keypad_buff_bottom>=BUFFER_SIZE) keypad_buff_bottom=0; } spin_unlock_irqrestore(&keypad_lock,flags); wake_up=1; } next_cycle2: if(var2){ pos=0; kpd_curr_cycle=1; while((pos<(NO_DEC_LINES-1)) && (!((var2>>pos)&0x01))) { pos++; } key=key_map[kpd_key_table[kpd_curr_cycle][pos]]; if ((last_key==key)&&(kpd_timeout= BUFFER_SIZE) keypad_buff_top=0; keypad_buff_count++; // Since we have exceeded the buffer size if (keypad_buff_count>BUFFER_SIZE) { keypad_buff_count=BUFFER_SIZE; keypad_buff_bottom++; if (keypad_buff_bottom>=BUFFER_SIZE) keypad_buff_bottom=0; } spin_unlock_irqrestore(&keypad_lock,flags); wake_up=1; } out: if(wake_up){ wake_up_interruptible(&keypad_read_queue); } keypad_scan_timer.expires = jiffies + SCAN_JIFF_COUNT; keypad_scan_timer.data = 0; keypad_scan_timer.function = scan_kbd; add_timer(&keypad_scan_timer); } static void init_kbd_timer(){ init_timer(&keypad_scan_timer); keypad_scan_timer.data = 0; keypad_scan_timer.function = scan_kbd; keypad_scan_timer.expires = jiffies + SCAN_JIFF_COUNT; add_timer(&keypad_scan_timer); } static void init_kbd_buffer(){ keypad_buff_count=keypad_buff_top=keypad_buff_bottom=0; } static int at91rm9200_keypad_open(struct inode *inode, struct file *filp){ if(down_trylock(&keypad_open_sema)){ return -EBUSY; } DPRINTK("Keypad open getting called \n"); schedule_or_not=1; init_kbd_buffer(); init_keytable(); init_keymap(); return 0; } static ssize_t at91rm9200_keypad_read(struct file *filp,char *buffer, size_t count, loff_t *ppos){ int i=0; unsigned long flags=0; unsigned char data[2]; memset(data,0,2); schedule_or_not=1; while(i0) { assert(keypad_buff_bottom < BUFFER_SIZE); data[0]=kpd_key_buffer[keypad_buff_bottom]; keypad_buff_bottom++; if (keypad_buff_bottom>=BUFFER_SIZE) keypad_buff_bottom=0; keypad_buff_count--; spin_unlock_irqrestore(&keypad_lock, flags); put_user(data[0], buffer); printk("read %d \n",data[0]); buffer++; i++; } else{ /* Sleep */ spin_unlock_irqrestore(&keypad_lock, flags); interruptible_sleep_on(&keypad_read_queue); if (signal_pending(current)){ schedule_or_not=0; return -EINTR; } } } schedule_or_not=0; return count; } unsigned int at91rm9200_keypad_poll(struct file *filp, poll_table *wait) { unsigned int mask=0; unsigned long poll_flags=0; spin_lock_irqsave(&keypad_lock, poll_flags); if(!(keypad_buff_count>0)) { spin_unlock_irqrestore(&keypad_lock, poll_flags); poll_wait(filp,&keypad_read_queue,wait); spin_lock_irqsave(&keypad_lock, poll_flags); if(!(keypad_buff_count>0)) { mask = 0; goto out; } } mask |= POLLIN | POLLRDNORM; out: spin_unlock_irqrestore(&keypad_lock, poll_flags); return mask; } static int at91rm9200_keypad_release(struct inode *inode, struct file *filp) { DPRINTK("Keypad release getting called \n"); up(&keypad_open_sema); return 0; } static int at91rm9200_keypad_ioctl(struct inode *inode,struct file *filep, unsigned int cmd, unsigned long arg) { unsigned long flags=0; switch(cmd){ case KBD_FLUSH_BUFFER: break; case KBD_SET_KEYMAP: break; case KBD_BUFFER_KEYS: break; case KBD_SCAN: break; default: break; } return 0; } static struct file_operations at91rm9200_keypad_fops = { owner: THIS_MODULE, open: at91rm9200_keypad_open, read: at91rm9200_keypad_read, poll: at91rm9200_keypad_poll, ioctl: at91rm9200_keypad_ioctl, release:at91rm9200_keypad_release, }; static int __init at91rm9200_keypad_init(void) { int ret; init_keypad_port(); close_timer=0; if(init_keypad_memspace()<0){ return -1; } spin_lock_init(&keypad_lock); sema_init(&keypad_open_sema,1); init_waitqueue_head(&keypad_read_queue); init_kbd_timer(); ret = register_chrdev(KBD_DEVICE_MAJOR,KBD_DEVICE_NAME,&at91rm9200_keypad_fops); if(ret < 0) { printk(KBD_DEVICE_NAME "can't get major number\n"); exit_keypad_memspace(); return ret; } DPRINTK("Keypad initialized \n"); return 0; } static void __exit at91rm9200_keypad_exit(void) { close_timer=1; mdelay(100); printk("del_timer returned %d \n",del_timer(&keypad_scan_timer)); exit_keypad_memspace(); unregister_chrdev(KBD_DEVICE_MAJOR, KBD_DEVICE_NAME); DPRINTK("Keypad Uninitialized \n"); } module_init(at91rm9200_keypad_init); module_exit(at91rm9200_keypad_exit);