1. 程式人生 > >初入android驅動開發之字符設備(四-中斷)

初入android驅動開發之字符設備(四-中斷)

異常 ade 線程 pts 解碼 hand 地址 bsp -m

上一篇講到android驅動開發中,應用是怎樣去操作底層硬件的整個流程,實現了按鍵控制led的亮滅。當然,這是一個非常easy的實例,只是略微演變一下,就能夠得到廣泛的應用。

如開發掃描頭,應用透過監聽上報的按鍵的鍵值,監聽到,則調用掃描頭的模塊。成功,則點亮LED燈,並把掃描頭解碼後的條碼信息。通過廣播的形式發出。又扯到其他地方,這裏主要說說中斷。

1. 中斷的一些概念

中斷,是什麽?

中斷。能夠看成是cpu對特殊事件的一種處理的機制,這類特殊事件一般指緊急事件或者說異常事件。非常easy的一個樣例,你拿你手機正在看視頻,來了一個電話。你接完電話,還是停在視頻。本來你的cpu正在運行看視頻這一系列的指令處理。但當接收到電話,會產生一個中斷,cpu依據優先級推斷。優先級高於當前則停止當前工作。並保存,然後運行中斷的處理函數,其中斷這一系列的事件處理完成以後。再運行保存在暫停隊列中的工作。這是一個外部中斷的樣例。

那麽中斷,是指 CPU 在運行程序的過程中,出現了某些突發事件時 CPU 必須暫停運行當前的程序,轉去處理突發事件,處理完成後 CPU 又返回原程序被中斷的位置並繼續運行。依據中斷的來源,中斷可分為內部中斷和外部中斷,內部中斷的中斷源來自 CPU內部(軟件中斷指令、溢出、除法錯誤等,比如,操作系統從用戶態切換到內核態需借助 CPU 內部的軟件中斷),外部中斷的中斷源來自 CPU 外部,由外設提出請求。

中斷,實現它的機制?

中斷。當外設發出一個中斷信號,cpu則依據中斷信號,來進行分析處理,依據中斷信號所對於的地址。去調用中斷處理函數。所以。中斷處理函數。是值該中斷產生後,cpu應該去緊急運行的事件。

那麽,這裏主要解說一下中斷處理函數的機制。

s5pv210是arm架構的芯片,當中斷的資源很的豐富,這裏有32個外部中斷和其余的gpio中斷。一般。實際開發中,中斷主要由外設發出。所以,這裏我們基本都是用的外部中斷。採用外部中斷的 CPU 通常為不同的中斷分配不同的中斷號,當檢測到某中斷號的中斷到來後,就自己主動跳轉到與該中斷號相應的地址運行。

不同中斷號的中斷有不同的入口地址。

中斷處理機制,,Linux 將中斷處理程序分解為兩個半部:頂半部(top half)和底半部(bottom half)。

在這兩者重要的差別,頂半部,不可被中斷,而底半部,能夠被新的中斷打開。那麽,這兩者之前的差別,就認為了它們各自獨特的特性。頂半部,不可被打斷,所以註定它的運行時間要很很的高速,所以一般它僅僅是簡單的讀取寄存器的中斷狀態並清楚中斷標誌,然後就把底半部處理程序掛究竟半部運行隊列中。而這樣,中斷處理的大部分工作就落究竟半部了。由於可被打斷,相對來說,時間就比較充足。運行一些耗時的任務。

底半部的三種方式:軟中斷、tasklet、工作隊列。

這裏有個博文鏈接。主要將三種機制以及之間的差異。http://blog.chinaunix.net/uid-20768928-id-5077401.html

中斷,當中關鍵的一些函數?

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)

irq:中斷號,這裏由gpio_to_irq()方法得到。
handler:發生中斷時首先要運行的硬中斷處理函數,這個函數能夠通過返回 IRQ_WAKE_THREADED喚醒中斷線程,也可返回IRQ_HANDLE不運行中斷線程
thread_fn : 中斷線程,類似於中斷下半部若傳參為null,則和request_irq()一樣
qflags:中斷標誌。備註:IRQF_SHARED 共享中斷時,dev_id不能為空。由於釋放irq時要區分哪個共享中斷
devname:中斷名

dev_id: 傳給中斷處理函數的參數。

2.簡單的實例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <mach/gpio.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/wait.h>
#include <linux/sched.h>

#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>

static struct class *buttondrv_class;
static struct device *buttondrv_class_dev;
int major;
volatile unsigned long *GPCCON;
volatile unsigned long *GPCDAT;
//static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static unsigned char key_val;
static volatile int ev_press = 0;
struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};
struct pin_desc pins_desc[2] = {
	{S5PV210_GPH3(7), 0x01},
};
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>buttons_irq\n");
	struct pin_desc *pindesc = (struct pin_desc *)dev_id;
	unsigned int pinval;
	
	pinval = gpio_get_value(pindesc->pin);
	printk("irq >>>>>>>>>>>>>>>>>>>>>>>>>>>>pinval =%d \n",pinval);
	if (pinval)
	{	
		key_val = 0x80 | pindesc->key_val;
		printk("1111 >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val);
	}
	else
	{
		key_val = pindesc->key_val;
		printk("0000 >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val);
		
	}
    ev_press = 1;               
//    wake_up_interruptible(&button_waitq);  
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int button_drv_open(struct inode *inode, struct file *file)
{
	printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_open\n");
	int ret=-1;
	s3c_gpio_setpull(S5PV210_GPH3(7), S3C_GPIO_PULL_NONE);
	ret = request_threaded_irq(gpio_to_irq(S5PV210_GPH3(7)), NULL,
					buttons_irq,
					IRQF_TRIGGER_RISING,
					"s2", &pins_desc[0]);
	printk("ret=%d irq=%d >>>>>>>>>>>>>>>>>>>>>>>>>\n ",ret,gpio_to_irq(S5PV210_GPH3(7)));
	return 0;
}

int button_drv_close(struct inode *inode, struct file *file)
{
	free_irq(gpio_to_irq(S5PV210_GPH3(7)), &pins_desc[0]);
	return 0;
}

static int button_drv_read(struct file *filp, char __user *buf, 
                                         size_t count, loff_t *offp)
{
	printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_read\n");
	if (count != 1)
		return -EINVAL;
	printk("read >>>>>>>>>>>>>>>>>>>>>>>>>>>key_val =%d \n",key_val);
//	wait_event_interruptible(button_waitq, ev_press);
	copy_to_user(buf, &key_val, 1);
	key_val=0;
	ev_press = 0;
	return 1;

}

static struct file_operations button_drv_fops = {
    .owner  =   THIS_MODULE,   
    .open   =   button_drv_open,       
    .read	=	button_drv_read,
    .release =  button_drv_close,
};

static int button_drv_init(void){
	printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_init\n");

    GPCCON = (volatile unsigned long *)ioremap(0xE0200C60, 8);
	GPCDAT= GPCCON + 1;
	if (!GPCCON) {
		return -EIO;
	}
	major = register_chrdev(0, "button_drv", &button_drv_fops); 
	buttondrv_class = class_create(THIS_MODULE, "buttondrv");
	buttondrv_class_dev = device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); 
	return 0;
}

static void button_drv_exit(void){
	printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_exit\n");	
	unregister_chrdev(major, "button_drv"); 
	device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);
	iounmap(GPCCON);

}

module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
關於代碼一些簡單的說明:

static DECLARE_WAIT_QUEUE_HEAD(button_waitq)

wake_up_interruptible(&button_waitq)

wait_event_interruptible(button_waitq, ev_press)

這個是等待隊列的機制,當有中斷的時候。喚醒。把事件增加工作隊列中,處理完事件後。繼續休眠,直到下次中斷。

3.關於一些調試方法:

一般編寫中斷的程序,最基本的是要看,gpio口的中斷號是否申請成功。這裏主要依據打印語句進行調試了。

若驅動程序不報錯誤了,則可進入android系統下,cat proc/interrupts ,可查看到你申請成功的中斷。

初入android驅動開發之字符設備(四-中斷)