天天看點

mini2440 簡單按鍵中斷模式驅動程式

Makefile

KERN_DIR = /home/grh/kernel_source_code/linux-2.6.32.2
all : 
	make -C $(KERN_DIR) M=`pwd` modules
	arm-linux-gcc key_interrupt_app.c -o key_interrupt_app
clean :
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
obj-m += key_interrupt.o
copy : 
	cp key_interrupt.ko key_interrupt_app /nfs
           

驅動程式代碼:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>

#define		GRH_MODULE_NAME		"key_interrupt"

static int major;
static struct class *key_interrupt_class;
static struct class_device *key_interrupt_device; 
static int key_value;

//wait_event_interruptible函數需要的兩個變量
static DECLARE_WAIT_QUEUE_HEAD(grh_wait_interrupt); //休眠的程序隊列頭
static volatile int sleep_for_interrupt; //這個變量為0的時候read函數會休眠,中斷裡面将其置1,read函數末尾将其設定為0



//pin_desc是對每一個按鍵中斷的描述,不僅僅可以是整數,也可以是更複雜到的字段,這裡用簡單的按鍵值就行了
int pin_desc[6] = {
	1, 2, 3, 4, 5, 6
};


//中斷處理函數
static irqreturn_t grh_handle_key_eint(int irq, void *dev_id){
	int *p;
	p = dev_id;

	//printk(KERN_EMERG"key pressed! key=%d\n", *p);
	key_value = *p;

	//喚醒休眠的程序
	sleep_for_interrupt = 1;
	wake_up_interruptible(&grh_wait_interrupt);

	return IRQ_HANDLED;
}

static void init_key(void){
	//注冊irq中斷處理函數,将按鍵值和中斷号綁定,所有清中斷操作以及初始化中斷相關寄存器的操作全部交給
	//核心自動完成了,不再需要像裸機程式一樣顯式地對寄存器進行讀寫了,中斷發生後會自動跳到grh_handle_key_eint
	request_irq(IRQ_EINT8, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key1", pin_desc);
	request_irq(IRQ_EINT11, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key2", pin_desc+1);
	request_irq(IRQ_EINT13, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key3", pin_desc+2);
	request_irq(IRQ_EINT14, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key4", pin_desc+3);
	request_irq(IRQ_EINT15, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key5", pin_desc+4);
	request_irq(IRQ_EINT19, grh_handle_key_eint, IRQ_TYPE_EDGE_BOTH, "key6", pin_desc+5);
}

static int key_interrupt_open(struct inode *inode, struct file *file){
	printk(KERN_EMERG"DRIVER: OPEN\n");
	sleep_for_interrupt = 0;
	init_key();
	return 0;
}

static ssize_t key_interrupt_write(struct inode *inode, const char __user *buf, size_t count, loff_t *ppos){
	printk(KERN_EMERG"DRIVER: WRITE\n");
	return 0;
}

static ssize_t key_interrupt_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){
	printk(KERN_EMERG"DRIVER: READ\n");
	//根據sleep_for_interrupt的數值決定是否将驅動程序加進休眠隊列grh_wait_interrupt中
	wait_event_interruptible(grh_wait_interrupt, sleep_for_interrupt);
	copy_to_user(buf, &key_value, 4);

	//下一次進入read的時候繼續休眠等待中斷發生
	sleep_for_interrupt = 0;
	return 0;
}

int key_interrupt_release(struct inode *inode, struct file *file){
	//登出中斷
	free_irq(IRQ_EINT8, pin_desc);
	free_irq(IRQ_EINT11, pin_desc+1);
	free_irq(IRQ_EINT13, pin_desc+2);
	free_irq(IRQ_EINT14, pin_desc+3);
	free_irq(IRQ_EINT15, pin_desc+4);
	free_irq(IRQ_EINT19, pin_desc+5);
	
	printk(KERN_EMERG"DRIVER: RELEASE\n");
	return 0;
}


static struct file_operations key_interrupt_fops = {
	.owner = THIS_MODULE,
	.open = key_interrupt_open,
	.write = key_interrupt_write,
	.read = key_interrupt_read,
	.release = key_interrupt_release,
};

int key_interrupt_module_init(void){
	printk(KERN_EMERG"INIT MODULE!\n");

	//register the driver with the device
	major = register_chrdev(0, GRH_MODULE_NAME, &key_interrupt_fops);

	//create my own device class
	key_interrupt_class = class_create(THIS_MODULE, "key_interrupt_class");
	
	//create my device of my own class
	key_interrupt_device = device_create(key_interrupt_class, NULL, MKDEV(major,0), NULL, "key_interrupt_device");

	return 0;
}

void key_interrupt_module_exit(void){
	unregister_chrdev(major, GRH_MODULE_NAME);
	device_unregister(key_interrupt_device);
	class_destroy(key_interrupt_class);
	printk(KERN_EMERG"EXIT MODULE!\n");
}

module_init(key_interrupt_module_init);
module_exit(key_interrupt_module_exit);

MODULE_AUTHOR("GRH");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("KEY POLL DRIVER");
MODULE_LICENSE("GPL");
           

使用者空間測試程式:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(void){
	int fd;
	int i, sum;
	int key_value;

	fd = open("/dev/key_interrupt_device", O_RDWR);
	if(-1 == fd){
		printf("open key device error!\n");
		return -1;
	}

	while(1){
		read(fd, &key_value, 4);
		printf("key pressed : %d\n", key_value);
	}
	close(fd);
	return 0;
}
           

按下按鍵之後的運作結果:

mini2440 簡單按鍵中斷模式驅動程式

當沒有按鍵按下的時候,測試程式程序在核心态處于休眠狀态,這樣比起輪詢式的驅動而言,資源的消耗就小了太多了:

mini2440 簡單按鍵中斷模式驅動程式
mini2440 簡單按鍵中斷模式驅動程式
mini2440 簡單按鍵中斷模式驅動程式

繼續閱讀