S3C2440下linux按鍵驅(qū)動編寫及測試程序
驅(qū)動程序tang2440_buttons.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "buttons"
/*定義中斷所用的結(jié)構(gòu)體*/
struct button_irq_desc {
int irq; //按鍵對應(yīng)的中斷號
int pin; //按鍵所對應(yīng)的 GPIO 端口
int pin_setting; //按鍵對應(yīng)的引腳描述,實(shí)際并未用到,保留
int number; //定義鍵值,以傳遞給應(yīng)用層/用戶態(tài)
char *name; //每個按鍵的名稱
};
/*結(jié)構(gòu)體實(shí)體定義*/
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"},
{IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"},
{IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"},
{IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"},
{IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"},
{IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
};
/*開發(fā)板上按鍵的狀態(tài)變量,注意這里是’0’,對應(yīng)的 ASCII 碼為 30*/
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
/*因?yàn)楸掘?qū)動是基于中斷方式的,在此創(chuàng)建一個等待隊(duì)列,以配合中斷函數(shù)使用;當(dāng)有按鍵按下并讀取到鍵
值時,將會喚醒此隊(duì)列,并設(shè)置中斷標(biāo)志,以便能通過 read 函數(shù)判斷和讀取鍵值傳遞到用戶態(tài);當(dāng)沒有按
鍵按下時,系統(tǒng)并不會輪詢按鍵狀態(tài),以節(jié)省時鐘資源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中斷標(biāo)識變量,配合上面的隊(duì)列使用,中斷服務(wù)程序會把它設(shè)置為 1,read 函數(shù)會把它清零*/
static volatile int ev_press = 0;
/*本按鍵驅(qū)動的中斷服務(wù)程序*/
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int down;
/*獲取被按下的按鍵狀態(tài)*/
down = !s3c2410_gpio_getpin(button_irqs->pin);
/*狀態(tài)改變,按鍵被按下,從這句可以看出,當(dāng)按鍵沒有被按下的時候,寄存器的值為 1(上拉),但按
鍵被按下的時候,寄存器對應(yīng)的值為 0*/
if (down != (key_values[button_irqs->number] & 1)) { // Changed
/*如果 key1 被按下,則 key_value[0]就變?yōu)椤?’,對應(yīng)的 ASCII 碼為 31*/
key_values[button_irqs->number] = '0' + down;
ev_press = 1; /*設(shè)置中斷標(biāo)志為 1*/
wake_up_interruptible(&button_waitq); /*喚醒等待隊(duì)列*/
}
return IRQ_RETVAL(IRQ_HANDLED);
}
/*
*在應(yīng)用程序執(zhí)行 open(“/dev/buttons”,...)時會調(diào)用到此函數(shù),在這里,它的作用主要是注冊 6 個按鍵的中斷。
*所用的中斷類型是 IRQ_TYPE_EDGE_BOTH,也就是雙沿觸發(fā),在上升沿和下降沿均會產(chǎn)生中斷,這樣做是為了更加有
*效地判斷按鍵狀態(tài)
*/
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
int i;
int err = 0;
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
/*注冊中斷函數(shù)*/
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
button_irqs[i].name, (void *)&button_irqs[i]);
if (err)
break;
}
if (err) {
/*如果出錯,釋放已經(jīng)注冊的中斷,并返回*/
i--;
for (; i >= 0; i--) {
if (button_irqs[i].irq < 0) {
continue;
}
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return -EBUSY;
}
/*注冊成功,則中斷隊(duì)列標(biāo)記為 1,表示可以通過 read 讀取*/
ev_press = 1;
/*正常返回*/
return 0;
}
/*
*此函數(shù)對應(yīng)應(yīng)用程序的系統(tǒng)調(diào)用 close(fd)函數(shù),在此,它的主要作用是當(dāng)關(guān)閉設(shè)備時釋放 6 個按鍵的中斷處理函數(shù)
*/
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
/*釋放中斷號,并注銷中斷處理函數(shù)*/
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return 0;
}
/*
*對應(yīng)應(yīng)用程序的 read(fd,...)函數(shù),主要用來向用戶空間傳遞鍵值
*/
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err;
if (!ev_press) {
if (filp->f_flags & O_NONBLOCK)
/*當(dāng)中斷標(biāo)識為 0 時,并且該設(shè)備是以非阻塞方式打開時,返回*/
return -EAGAIN;
else
/*當(dāng)中斷標(biāo)識為 0 時,并且該設(shè)備是以阻塞方式打開時,進(jìn)入休眠狀態(tài),等待被喚醒*/
wait_event_interruptible(button_waitq, ev_press);
}
/*把中斷標(biāo)識清零*/
ev_press = 0;
/*一組鍵值被傳遞到用戶空間*/
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
/*把調(diào)用 poll 或者 select 的進(jìn)程掛入隊(duì)列,以便被驅(qū)動程序喚醒*/
poll_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
/*設(shè)備操作集*/
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_buttons_open,
.release = s3c24xx_buttons_close,
.read = s3c24xx_buttons_read,
.poll = s3c24xx_buttons_poll,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/*設(shè)備初始化,主要是注冊設(shè)備*/
static int __init dev_init(void)
{
int ret;
/*把按鍵設(shè)備注冊為 misc 設(shè)備,其設(shè)備號是自動分配的*/
ret = misc_register(&misc);
printk (DEVICE_NAME"tinitializedn");
return ret;
}
/*注銷設(shè)備*/
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}