實作ISR

現在我們已經知道如何註冊ISR(Interrupt Service Routine),但是還沒寫出實作的ISR,形式上,ISR只是一般的C函式,其特殊的是其執行的時機-中斷時期(interrupt time)。 我們將要實作的函式利用上節提到函式指標去指定:

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

handler引數是中斷處理程序的指標,它的prototype如下所示:

typedef irqreturn_t (*irq_handler_t)(int,void*)

第一引數是IRQ編號,第二引數則會傳入dev_id的內容。中斷處理傳回值是irqreturn_t型別(實際上是int型別),需要傳回適當的數值。在收到中斷後,呼叫中斷處理程序時,很可能是共享IRQ的情形,如果不是這個裝置所負責的中斷處理,就甚麼都不做,但需要傳回IRQ_NONE。

傳回值 意義
IRQ_NONE 沒有處理這個中斷
IRQ_HANDLED 已經處理這個中斷

這些值也可以透過IRQ_RETVAL巨集來選取。

範例(按鈕中斷觸發LED):

#include <linux/module.h>   
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>


#define LED 27
#define BUTTON 23
#define MY_GPIO_INT_NAME "my_button_int"
#define MY_DEV_NAME "my_device"

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A Simple GPIO Device Driver module for RaspPi");

static short int button_irq = 0;
static unsigned long flags = 0;
static int led_trigger = 0;

static irqreturn_t button_isr(int irq, void *data)
{
    local_irq_save(flags);
    printk("button_isr !!!!\n");
    gpio_set_value(LED, led_trigger);
    led_trigger = led_trigger ? (0):(1);
    local_irq_restore(flags);
    return IRQ_HANDLED;
}


int init_module(void)
{
    // -- setup the led gpio as output
    printk("module: button interrupt example.\n");
    if(gpio_is_valid(LED) < 0) return -1;
    if(gpio_request(LED, "LED") < 0) return -1;
    gpio_direction_output(LED, 0 );

    // -- setup the button gpio as input and request irq
    if(gpio_request(BUTTON,"BUTTON") < 0) return -1;
    if(gpio_is_valid(BUTTON) < 0) return -1;
    if( (button_irq = gpio_to_irq(BUTTON)) < 0 )  return -1;
    if( request_irq( button_irq, button_isr ,IRQF_TRIGGER_FALLING, MY_GPIO_INT_NAME, MY_DEV_NAME)) return -1;
    return 0;

}


void cleanup_module(void)
{
    gpio_set_value(LED, 0);
    gpio_free(LED);
    free_irq(button_irq, MY_DEV_NAME);
    gpio_free(BUTTON);
}

中斷的生效與失效

有時候驅動程式要暫時抵制特定中斷路線的信號傳達。核心提供三個輔助函式,它們都被定義在<linux/irq.h>但是如果是共享情形下也沒無法抵制,現在大部分的驅動程式都是共享的,因此這些函式用到的機會就比較少。

void disable_iqr(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);

使用disable_irq(),所有cpu將不會收到此中斷信號,disable_irq_nosync()則是改變完暫存器位元遮罩後立刻返回,不管受影響的IRQ是否有ISR正在執行。enable_irq則是恢復中斷狀態。 倘若要壓制全部中斷的話,可以利用<linux/system.h>所定義兩個函式之一,來壓制當時處理器受理的任何中斷訊號:

void local_irq_save(unsigned long flags);
void local irq_disable(void);

local_irq_save()會將當時所有中斷狀態存入flags,irq_disable()則不會,而恢復中斷信號能力可用下列兩函式:

void local_irq_retore(unsigned long flags);
void local_irq_disable(void);

results matching ""

    No results matching ""