這是一個按鍵中斷。linux的中斷機制和中斷原理的內容很多,不在這裡寫了。先寫一下關於第一個中斷的驅動程式。
中斷的驅動程式也是按照模組進行編寫的。
首先這是一個按鍵中斷,按鍵的驅動檔案不能寫,可以讀,可以開啟。這個程式中還使用了poll()對中斷進行了監控。
static int __init dev_init(void){int ret;ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}
這一部分是註冊函數。是一個混雜裝置的註冊。ret = misc_register(&misc)中的&misc是取混雜裝置結構體的地址。混雜裝置的結構體如下:
static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};
在這個結構體中,MISC_DYNAMIC_MINOR是混雜裝置的次裝置號(主裝置號為10),DEVICE_NAME定義了混雜裝置的名稱,&dev_fops取混雜裝置file_operation結構體的地址。其中關於DEVICE_NAME使用的是宏定義:
#define DEVICE_NAME "button"
關於dev_fops的定義如下:
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,};
結構體的第一個成員是所有者。第二個成員是open函數,第三個是read函數,第四個是poll監控函數。
分別來介紹四個函數。
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;} 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) { 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; } ev_press = 1; return 0;}
首先實現了中斷的註冊request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, utton_irqs[i].name, (void *)&button_irqs[i]);這個函數嵌套在for迴圈中,button_irqs代表著定義的數組,數組的成員是6個。 每一個中斷對應的中斷處理函數是一樣的buttons_interrupt。 IRQ_TYPE_EDGE_BOTH對應的是中斷的處理屬性,上升沿和下降沿都要產生中斷。 button_irqs定義了裝置的名稱,從button_irqs數組傳遞進來六個裝置的名字。
button_irqs涉及到共用中斷的時候開始使用,(void *)&button_irqs[i]選擇了地址,不同的資料對應的地址是不一樣的。這樣就確保了dev_id的不同。sizeof(button_irqs)/sizeof(button_irqs[0])整個數組的大小除以一個成員的大小,得到的是數群組成員的個數。
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) return -EAGAIN;else wait_event_interruptible(button_waitq, ev_press); } //執行到這裡的時候ev_press=1,這個實在中斷程式中實現的。 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);}
在讀函數中,要注意的ev_press的變化和wait_event_interruptible(button_waitq, ev_press)阻塞。
這裡如果要想讓程式進行非阻塞,所以ev_press的值應該是1,但是這個1的實現是在中斷程式中實現的。ev_press只是一個標誌,並不是決定非阻塞的決定因素。
static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait){ unsigned int mask = 0; poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask;}
再說一下poll實現的步驟:
①poll_wait(file, &button_waitq, wait); 指明要使用的等待隊列是什麼,這裡是button_waitq。
② mask |= POLLIN | POLLRDNORM; 返回掩碼。不能向按鍵裡寫資料。
static irqreturn_t buttons_interrupt(int irq, void *dev_id){ struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; int down; // udelay(0); down = !s3c2410_gpio_getpin(button_irqs->pin); //讀取GPIO寄存器的資料寄存器的值。 //這裡的!是因為down為1的時候表示被按下了。 //s3c2410_gpio_getpin(button_irqs->pin);表示沒有按下的時候等於1。 if (down != (key_values[button_irqs->number] & 1)) { // Changed key_values[button_irqs->number] = '0' + down; //防止下次再按下的時候中斷產生操作。 cyx = key_values[button_irqs->number]; printk ("hh"); printk ("%d",down); printk ("%d\n",cyx); ev_press = 1; wake_up_interruptible(&button_waitq); } return IRQ_RETVAL(IRQ_HANDLED);}
這裡的是中斷的處理常式。其中的cyx = key_values[button_irqs->number];是我自己加的,為了列印一下這個值。
這裡整體出現思維混亂,首先是down的值在執行之後是1,0的變化。這裡的down是通過函數s3c2410_gpio_getpin(button_irqs->pin);取資料寄存器的值再取反得到的,因為按鍵被按下時所對應的的寄存器是0,所以被按下時down的值是1。down的值解釋到這裡。再說說cyx,cyx的值在程式執行以後是49和48變化,瞬間不理解了。後來發現它的玄機在這裡:key_values[button_irqs->number] = '0' + down;這裡的‘0’是一個常量值48!!!所以48+0(1)出現了49和48的變化。你可能會問,那你列印hh的目的是什嗎?因為自己是菜鳥,所以想徹底搞清楚到底在哪裡或者說怎麼執行的中斷。最後要注意的是:
down = !s3c2410_gpio_getpin(button_irqs->pin); //讀取GPIO寄存器的資料寄存器的值。 //這裡的!是因為down為1的時候表示被按下了。//s3c2410_gpio_getpin(button_irqs->pin);表示沒有按下的時候等於1。 if (down != (key_values[button_irqs->number] & 1)) { // Changedkey_values[button_irqs->number] = '0' + down; //防止下次再按下的時候中斷產生操作。
這個一個演算法技巧,我覺得要反覆看看的地方。
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;}free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return 0;}
這裡是中斷登出函數,對應的open函數。這裡首先是通過for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)for迴圈實現了六個按鍵寄存器的便利,然後free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);釋放了中斷。其中sizeof(button_irqs)/sizeof(button_irqs[0]);實現了結構體成員數量的實現。其中button_irqs定義如下:
static struct button_irq_desc button_irqs [] = { {IRQ_EINT8 , S3C2410_GPG0 , S3C2410_GPG0_EINT8 , 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG3 , S3C2410_GPG3_EINT11 , 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG5 , S3C2410_GPG5_EINT13 , 2, "KEY2"}, {IRQ_EINT14, S3C2410_GPG6 , S3C2410_GPG6_EINT14 , 3, "KEY3"}, {IRQ_EINT15, S3C2410_GPG7 , S3C2410_GPG7_EINT15 , 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 5, "KEY5"},};
裝置的登出函數:
static void __exit dev_exit(void){misc_deregister(&misc);}
misc_deregister,混雜裝置的登出函數。
下面附上完整的驅動程式代碼(由於老謝使用的是linux2.6.29的核心版本,而自己的手上只有linux2.6.32的驅動程式,所以兩套程式會有一點不同。)所有代碼親測可用。
#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/irq.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/gpio.h>#define DEVICE_NAME "button"struct button_irq_desc { int irq; int pin; int pin_setting; int number; char *name;};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"},};static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};static DECLARE_WAIT_QUEUE_HEAD(button_waitq);static volatile int ev_press = 0;static int cyx;static irqreturn_t buttons_interrupt(int irq, void *dev_id){ struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; int down; // udelay(0); down = !s3c2410_gpio_getpin(button_irqs->pin); if (down != (key_values[button_irqs->number] & 1)) { // Changedkey_values[button_irqs->number] = '0' + down;cyx = key_values[button_irqs->number]; ev_press = 1; wake_up_interruptible(&button_waitq);printk ("%d\n",cyx);printk ("hh");printk ("%d\n",down); } return IRQ_RETVAL(IRQ_HANDLED);}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;} 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) { 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; } ev_press = 1; return 0;}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;}free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return 0;}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) return -EAGAIN;else wait_event_interruptible(button_waitq, ev_press); } 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; poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask;}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,};static int __init dev_init(void){int ret;ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}static void __exit dev_exit(void){misc_deregister(&misc);}module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("FriendlyARM Inc.");
下面是應用程式的代碼:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/select.h>#include <sys/time.h>#include <errno.h>#include <linux/input.h>int main(void){int buttons_fd;int key_value,i=0,count;struct input_event ev_key;buttons_fd = open("/dev/button", O_RDWR);if (buttons_fd < 0) {perror("open device buttons");exit(1);}for (;;) {count = read(buttons_fd,&ev_key,sizeof(struct input_event));for(i=0; i<(int)count/sizeof(struct input_event); i++)if(EV_KEY==ev_key.type)printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);if(EV_SYN==ev_key.type)printf("syn event\n\n");}close(buttons_fd);return 0;}
下面是驅動程式的Makefile
ifneq ($(KERNELRELEASE),)obj-m := button.oelseKDIR := /home/guoqian/4-3-1/linux-2.6.29all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-clean:rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul*endif
最後附上驅動程式的執行。
最後的執行效果是每按一下按鍵會出現兩次列印,這是因為open函數中的這段代碼的原因:
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]);
IRQ_TYPE_EDGE_BOTH這個參數定義了GPIO的上升沿和下降沿都要產生中斷。