|
#include
#include
#include #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #include
#include
#include
#include
#include
#include #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
在寫讀函數時一定要將自己定義的結構體轉換為字節數來傳遞給應用程序,否則會出錯。 本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/yd4330152763132/archive/2010/02/01/5275689.aspx |
|