⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 44b0-key.c

📁 在S3C44B0上实现的键盘和LED驱动程序
💻 C
字号:
/* last modify 2007.5.13, *//* by guanlan */#include<linux/module.h>#include<linux/init.h>#include<linux/sched.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/types.h>#include<asm/uaccess.h>#include<asm/irq.h>#define PDATG 	(*(volatile unsigned int *)0x01D20044) #define PCONG 	(*(volatile unsigned int *)0x01D20040)  	/* macro of registers */#define PUPG  	(*(volatile unsigned int *)0x01D20048)#define EXTINT 	(*(volatile unsigned int *)0x01D20050)#define EXTINTPND	(*(volatile unsigned int *)0x01D20054)#define INTMSK	(*(volatile unsigned int *)0x01E0000C)#define I_ISPC 	(*(volatile unsigned int *)0x01E00024)#define DEVICE_NAME "s3c44b0-keyboard"#define KBD_MAJOR 61		/* the major number of kbd dev */#define IRQ_KBD 21#define KEYSTATUS_UP 	0	/* the status of kbd */#define KEYSTATUS_DOWNX 1	/* some key generate interrupt,but not sure whether it is really pushed */#define KEYSTATUS_DOWN  2	/* it is sure that the key is pushed *//*  * we make use of low-level trigger interrupt,due to it is  more dependable than rising-edge trigger * interrupt.wo use so-called "qv dou" and timer in linux. * 1."qv dou" : if a interrupt generates,we not sure whether some key is really pushed.so we delay * 10ms,and read the register again to sure that key is pushed really!  * 2.it is a waste that just delay some time without doing other things.so we use timer in lunux, * and during the time we can do other things. */#define KBD_TIMER_DELAY (HZ/10)	#define KBD_TIMER_DELAY1 (HZ/100)/*  * if user's process can't deal the interrupt in time,the kbd drive can save the key number * in a cycle buffer,user can get the key number in BUF_TAIL when BUF_HEAD!=BUF_TAIL */#define MAX_KBD_BUF 16#define BUF_HEAD (kbd_dev.buf[kbd_dev.head])#define BUF_TAIL (kbd_dev.buf[kbd_dev.tail])#define INCBUF(x,mod) ((++(x))&((mod)-1))	/* mod = MAX_KBD_BUF *//*  * there are something confusing me.At first i want to disable the IRQ_KEY by using register INTMSK,  * but it doesn't work.second, i want to change the I/O port G's work mode from INT to INPUT,it is * effect,but i think it's not a good way.At last,i use the system API disable_irq and enable_irq, * it works well.And it cost me one day to finish it. *//***************************************************     #define KBD_OPEN_INT (INTMSK &= 0xffdfffff)  #define KBD_CLOSE_INT (INTMSK |= 0x200000)  #define KBD_OPEN_INT (PCONG |= 0xff00)  #define KBD_CLOSE_INT (PCONG &= 0xffff00ff)***************************************************/typedef unsigned char KBD_RET;typedef struct {	unsigned int status;		unsigned int head;	unsigned int tail;	KBD_RET buf[MAX_KBD_BUF];	wait_queue_head_t wq;}KBD_DEV;static KBD_DEV kbd_dev;static void(*kbdEvent)(void);/* a function point,it is used to call kbdEvent_dummy and kbdEvent_raw */static struct timer_list kbd_timer;void kbdEvent_dummy(void){}static int key = 0;int read_ExINT_key(void);int ISKBD_DOWN(void);void KBD_REG_INIT(void);static void kbdEvent_raw(void);static void kbd_int_isr(int irq,void *dev_id,struct pt_regs *reg);static void kbd_timer_handler(unsigned long data);	static int kbd_open(struct inode *inode,struct file *filp){	kbd_dev.head = kbd_dev.tail = 0;	kbdEvent = kbdEvent_raw;	MOD_INC_USE_COUNT;	return 0;}static int kbd_release(struct inode *inode,struct file *filp){	kbdEvent = kbdEvent_dummy;	MOD_DEC_USE_COUNT;	return 0;}static KBD_RET kbdRead(void){	KBD_RET kbd_ret;	kbd_ret = BUF_TAIL;	kbd_dev.tail = INCBUF(kbd_dev.tail,MAX_KBD_BUF);	return kbd_ret;}/*  * if kbd_dev.head!=kbd_dev.tail shows that there are keys saved in buffer,so we can read the * key number and do something according the number. * if user read at NOBLOCK mode,return immedicatly,if read at BLOCK mode ,then sleep on current * process,wait the interrupt to wake up it.*/static ssize_t kbd_read(struct file *filp,char *buffer,size_t count,\		loff_t *ppos){	KBD_RET kbd_ret;retry:	if(kbd_dev.head	!= kbd_dev.tail)	{		kbd_ret = kbdRead();		copy_to_user(buffer,(char *)&kbd_ret,sizeof(KBD_RET));		return sizeof(KBD_RET);	}	else	{		if(filp->f_flags&O_NONBLOCK)			return -EAGAIN;		interruptible_sleep_on(&(kbd_dev.wq));		if(signal_pending(current))			return -ERESTARTSYS;		goto retry;	}		return sizeof(KBD_RET);}static struct file_operations kbd_ops={	owner:		THIS_MODULE,	open:		kbd_open,	read:		kbd_read,	release:	kbd_release,};static int __init kbd_init(void){	int ret;	//set_external_irq(IRQ(4),EXT_LOWLEVEL,GPIO_PULLUP_EN);	//set_external_irq(IRQ(5),EXT_LOWLEVEL,GPIO_PULLUP_EN);	//set_external_irq(IRQ(6),EXT_LOWLEVEL,GPIO_PULLUP_EN);	KBD_REG_INIT();	kbdEvent = kbdEvent_dummy;	ret = register_chrdev(KBD_MAJOR,DEVICE_NAME,&kbd_ops);	if(ret<0)	{		printk(DEVICE_NAME"can't get major number\n");		return ret;	}	ret = request_irq(IRQ_KBD,kbd_int_isr,SA_INTERRUPT,DEVICE_NAME,kbd_int_isr);	if(ret) return ret;	kbd_dev.head = kbd_dev.tail = 0;	kbd_dev.status = KEYSTATUS_UP;	init_waitqueue_head(&(kbd_dev.wq));	init_timer(&kbd_timer);	kbd_timer.function = kbd_timer_handler;	printk(DEVICE_NAME "initialized\n");	return 0;}void KBD_REG_INIT(void){	PCONG |= 0xff00;	PUPG |= 0xf0;	EXTINT &= 0x8888ffff;	//KBD_OPEN_INT;}static void __exit kbd_exit(void){	int ret;	ret = unregister_chrdev(KBD_MAJOR,DEVICE_NAME);	if(ret<0)		printk("unable to release kbd device!");}/*  * the following interrupt service routine is the most important part of this driver. * we must clear the corresponding bits of EXTINTPND (by write 1 to EXTINTPND) and INTPND * (by write 1 to I_ISPC),otherwise it will generate interrupt all the time,never stop,and  * generate the error like "The IRQ 21 is locking the system,disabled."*/static void kbd_int_isr(int irq,void *dev_id,struct pt_regs *reg){	if((kbd_dev.status == KEYSTATUS_UP))	{		//KBD_CLOSE_INT;			disable_irq(IRQ_KBD);		kbd_dev.status = KEYSTATUS_DOWNX;		EXTINTPND |= 0x0f;		I_ISPC |= 0x200000;		kbd_timer.expires = jiffies + KBD_TIMER_DELAY1;		add_timer(&kbd_timer);		printk("Occured key board Interrupt,irq = %d\n",irq);	}	else	{		disable_irq(IRQ_KBD);		kbd_dev.status == KEYSTATUS_UP;		EXTINTPND |= 0x0f;		I_ISPC |= 0x200000;		printk("Something disturb the key value,but it has been settled\n");		enable_irq(IRQ_KBD);	}}static void kbd_timer_handler(unsigned long data){	if(ISKBD_DOWN())	{		if(kbd_dev.status == KEYSTATUS_DOWNX)		{			kbd_dev.status = KEYSTATUS_DOWN;			kbd_timer.expires = jiffies + KBD_TIMER_DELAY;			kbdEvent();			printk("KEY DOWN,code= %x\n",key);		}		else 		{			kbd_timer.expires = jiffies + KBD_TIMER_DELAY;		}		add_timer(&kbd_timer);			}	else	{		kbd_dev.status = KEYSTATUS_UP;		del_timer(&kbd_timer);		printk("KEY UP,code = %x\n",key);		//kbdEvent();		//KBD_OPEN_INT;		enable_irq(IRQ_KBD);	}	EXTINTPND |= 0x0f;	I_ISPC |= 0x200000;}int ISKBD_DOWN(void){ 	int temp;  	temp = EXTINTPND;	//temp = PDATG; 	if((temp&0x0f)==0)   	//if((temp&0x0f)==0x1111)		temp = 0; 	else   		temp = 1;	return temp;}/* the corresponding bit will always be set 1 if the key was pushed,though the INT was masked. */int read_ExINT_key(void){	int temp;	temp = EXTINTPND;//	temp = PDATG;	if(temp&0x01)//	if(~temp&0x01)   		temp = 1; 	else if(temp&0x02)//	else if(~temp&0x02)   		temp = 2;	else if(temp&0x04)//	else if(~temp&0x04)		temp = 3;	else if(temp&0x08)//	else if(~temp&0x08)		temp = 4;	else		temp = 0;	return temp;}static void kbdEvent_raw(void){	key = read_ExINT_key();	BUF_HEAD = key;	kbd_dev.head = INCBUF(kbd_dev.head,MAX_KBD_BUF);	wake_up_interruptible(&(kbd_dev.wq));}module_init(kbd_init);module_exit(kbd_exit);MODULE_LICENSE("LOJO");MODULE_AUTHOR("GUANLAN");MODULE_DESCRIPTION("kbd driver for S3C44B0");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -