Linux 設備驅動--- 中斷處理


中斷的概念:

爲什麼需要中斷:

1,外設的處理速度一般慢於 CPU;

2,CPU 不能一直等待外部事件;

所以設備必須有一種方法來 通知 CPU  它 的工作進度,這種方法就是中斷.


中斷的實現:

在 Linux 驅動程序中,爲設備實現一箇中斷包含兩個步驟:

1,向內核註冊中斷;

2,實現中斷處理函數;


中斷註冊 request_irq :

request_irq 用於實現中斷的註冊功能:

函數:

int   request_irq  ( unsigned int irq ,  void (*handler) (int , void *, struct pt_regs *), unsigned long flags , const char * devname, void * dev_id )


參數:

unsigned int irq :申請的硬件中斷號;

void (*handler) (int , void *, struct pt_regs *)  :函數指針 handler ,向系統登記中斷處理函數,是個回掉函數;

const char * devname :指定設備驅動程序的名稱;

 void * dev_id  :傳入中斷處理程序的參數,可爲 NULL ,也可用來指定中斷服務函數需要參考的數據地址;共享中斷時使用;

unsigned long flags :(指定了 快速中斷 或者 慢速中斷 或者 中斷共享 等中斷處理屬性,上升沿,下降沿等;)

          在 flags 參數中,可以選擇一些與中斷管理有關的選項,

          如: 

          IRQF_DISABLED (SA_INTERRUPT)

          如果設置該位,表示是一個“快速”中斷處理程序,如果沒有設置該位,那麼是一個“慢速”中斷處理程序;

          IRQF_SHARED (SA_SHIRQ)

          該位表明中斷可以在設備間共享.

返回值:

爲 0 表示 成功;


快速 / 慢速中斷:

區別在於:快速中斷保證中斷處理的 原子性(不被打斷),而慢速中斷則不保證;

也就是“ 開啓中斷 ”標誌位(處理器IF)在運行 快速中斷處理程序是關閉的,因此在服務該中斷時,不會被其他類型的中斷打斷,

而調用慢速中斷處理時,其他類型的中斷仍可以得到服務;

慢速中斷可以中斷嵌套,快速中斷不可以.


共享中斷:

共享中斷 就是將不同的設備掛到同一個中斷信號線上,Linux 對共享的支持主要是爲 PCI 設備服務;

共享中斷處理程序 相同的中斷號, 所以需要來提供一個唯一的標誌來區分到底是哪個中斷;


共享中斷註冊 3 個特別之處:

也是通過 request_irq 函數來註冊的,但是有三個特別之處:

1,申請共享中斷時候,必須在 flags 參數中指定 IRQF_SHARED 位;

2,dev_id 參數必須是唯一的;

3,共享中斷的處理程序中,不能使用 disable_irq ( unsigned int irq) ,

          如果使用了這個函數,共享中斷信號線的其他設備將同樣無法使用中斷,也就是無法正常工作.


中斷處理程序  handler :

void (*handler) (int , void *, struct pt_regs *) :

handler 例子:

static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
	int down;

	down = !s3c2410_gpio_getpin(button_irqs->pin);

	if (down != (key_values[button_irqs->number] & 1))
	{
		key_values[button_irqs->number] = '0' + down;
		ev_press = 1;
		wake_up_interruptible(&button_waitq);
	}

	return IRQ_RETVAL(IRQ_HANDLED);
}

函數中 void *dev_id 參數是從 request_irq   函數裏的第五個參數 void * dev_id ;


err = request_irq(button_irqs[i].irq, irq_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]);

struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;	
};

static struct button_irq_desc button_irqs [] = {
	{IRQ_EINT1,	S3C2410_GPF1,	S3C2410_GPF1_EINT1,	0, "KEY1"}, /* K1 */
	{IRQ_EINT4,	S3C2410_GPF4,	S3C2410_GPF4_EINT4,	1, "KEY2"}, /* K2 */
	{IRQ_EINT2,	S3C2410_GPF2,	S3C2410_GPF2_EINT2,	2, "KEY3"}, /* K3 */
	{IRQ_EINT0,	S3C2410_GPF0,	S3C2410_GPF0_EINT0,	3, "KEY4"}, /* K4 */
};


中斷處理程序 特點:

中斷處理程序是普通的 C 代碼,特別之處在於中斷處理程序是在中斷上下文 運行的,它的行爲受到某些限制:

1,不能向用戶空間發送或接收數據;

2,不能使用可能引起阻塞的函數;

3,不能使用可能引起調度的函數;


中斷處理函數流程:

(共享中斷判斷具體是哪個中斷)


inb 從I/O端口讀取一個字節(BYTE, HALF-WORD) ;
outb 向I/O端口寫入一個字節(BYTE, HALF-WORD) ;
inw 從I/O端口讀取一個字(WORD,即兩個字節) ;
outw 向I/O端口寫入一個字(WORD,即兩個字節) ;

釋放中斷 free_irq  :

當設備不再需要使用中斷時( 通常在驅動卸載時 ),應當把它們返回給系統,
使用:
void  free_irq ( unsigned int irq ,void *dev_id )

實例程序:

/*************************************

NAME:EmbedSky_hello.c
COPYRIGHT:www.embedsky.net

*************************************/

#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>

#define DEVICE_NAME     "IRQ-Test"

struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;	
};
/*
err = request_irq(button_irqs[i].irq, irq_interrupt, IRQ_TYPE_EDGE_BOTH, 
				  button_irqs[i].name, (void *)&button_irqs[i]);
*/
#if !defined (CONFIG_SKY2440_IRQ_TEST)
static struct button_irq_desc button_irqs [] = {
	{IRQ_EINT1,	S3C2410_GPF1,	S3C2410_GPF1_EINT1,	0, "KEY1"}, /* K1 */
	{IRQ_EINT4,	S3C2410_GPF4,	S3C2410_GPF4_EINT4,	1, "KEY2"}, /* K2 */
	{IRQ_EINT2,	S3C2410_GPF2,	S3C2410_GPF2_EINT2,	2, "KEY3"}, /* K3 */
	{IRQ_EINT0,	S3C2410_GPF0,	S3C2410_GPF0_EINT0,	3, "KEY4"}, /* K4 */
};
#else
static struct button_irq_desc button_irqs [] = {
	{IRQ_EINT9,	S3C2410_GPG1,	S3C2410_GPG1_EINT9,	0, "KEY1"}, /* K1 */
	{IRQ_EINT11,	S3C2410_GPG3,	S3C2410_GPG3_EINT11,	1, "KEY2"}, /* K2 */
	{IRQ_EINT2,	S3C2410_GPF2,	S3C2410_GPF2_EINT2,	2, "KEY3"}, /* K3 */
	{IRQ_EINT0,	S3C2410_GPF0,	S3C2410_GPF0_EINT0,	3, "KEY4"}, /* K4 */
};
#endif
static volatile char key_values [] = {'0', '0', '0', '0'};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;


static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
	int down;

	down = !s3c2410_gpio_getpin(button_irqs->pin);

	if (down != (key_values[button_irqs->number] & 1))
	{
		key_values[button_irqs->number] = '0' + down;
		ev_press = 1;
		wake_up_interruptible(&button_waitq);
	}

	return IRQ_RETVAL(IRQ_HANDLED);
}


static int tq2440_irq_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, irq_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 tq2440_irq_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 tq2440_irq_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 tq2440_irq_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	=   tq2440_irq_open,
	.release	=   tq2440_irq_close, 
	.read	=   tq2440_irq_read,
	.poll	=   tq2440_irq_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" initialized\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("www.embedsky.net");
MODULE_DESCRIPTION("IRQ Test for EmbedSky SKY2440/TQ2440 Board");










發佈了115 篇原創文章 · 獲贊 21 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章