인터럽트 제어 구조


- 리눅스 커널의 IRQ 인터럽트 처리

1. 하드웨어에서 인터럽트가 발생하면 ARM 인터럽트 벡터 테이블로 점프.

2. 인터럽트 벡터 테이블의 IRQ핸들어인 asm_do_IRQ() 실행.

* arch/arm/kernel/irq.c

3. asm_do_IRQ()는 사용자 인터럽트 핸들러 테이블인 irq_desc[irqs]를 참조해 사용자 인터럽트 서비스 함수 실행

* asm_do_IRQ() --> handle_IRQ() --> generic_handle_irq() --> generic_handle_irq_desc() --> desc --> handle_irq()

4. 사용자는 irq_desc 테이블에 인터럽트 번호를 인덱스로 자신의 인터럽트 서비스 함수 등록.

5. 더 이상 인터럽트 처리가 필요 없으면 irq_desc 테이블에서 해제.


<처리 과정 그림>

그림 출처 : http://pmj0403.tistory.com/



- 기본 인터럽트 API

1. 헤더

* #include <linux/interrupt.h>

* #include <mach/irqs.h> //플랫폼 의존


2. 인터럽트 핸들러 등록 

int request_irq( unsigned int irq, irq_handler_t handler,
 unsigned long flags, const char *name, void *dev);
 

cs

* irq : 요청하는 인터럽트 번호

* handler : 인터럽트가 발생했을 때 처리 함수를 가리키는 포인터

* flags : 인터럽트 관리 옵션을 나타내는 비트 마스크

* name : /proc/interrupts에서 인터럽트 소유자를 표시할 때 사용

* dev : 인터럽트 공유를 위한 포인터로 인터럽트 라인이 비어 있을 고유 식별자로 사용



3. 주요 인터럽트 핸들러 플래그

* IRQ_DISABLED : 인터럽트 핸들러를 실행하는 동안 모든 인터럽트 비활성화

* IRQF_SAMPLE_RANDOM : 난수를 적용한 인터럽트로 타이머 인터럽트는 사용 불가

* IRQF_TIMER : 시스템 타이머를 적용한 인터럽트

* IRQF_SHARED : 인터럽트 공유로 참여하는 모든 핸들러가 이 플래그를 사용해야 함

* IRQF_TRIGGER_RISING : 신호가 상승할 때 인터럽트 인지.

* IRQF_TRIGGER_FALLING : 신호가 하강할 때 인터럽트 인지.


4. irq_handler_t 형 인터럼트 처리 함수

irqreturn_t my_interrupt_proc(int irq, void *dev_id) {
    ...
    return IRQ_HANDLED;
}
cs

* irq : 인터럽트 번호

* dev_id : reqeust_irq()의 마지막 인자로 전달한 고유 식별자 또는 사용자 매개 변수.



5. 인터럽트 핸들러 제거.

void free_irq( unsigned int irq, void *dev_id );


* irq : 해제할 인터럽트 번호.

* dev_id : request_irq()의 마지막 인자로 전달한 고유 식별자 또는 사용자 매개 변수


6. 인터럽트 허가와 금지.

#include <asm/irq.h>
 
void disable_irq(int irq);
void enable_irq(int irq);


* irq : 허가 또는 금지할 인터럽트 번호.


7. 인터럽트 제어 기본 흐름

* 모듈이 적재될 때 해당 irq 할당. 모듈이 제거될 때 irq 할당 제거.

* 인터럽트가 발생하면 자동으로 서비스 함수 실행.

#define PIR_IRQ gpio_to_irq(IMAX_GPIO_NR(325))
 
irqreturn_t pir_isr(int irq, void *dev_id) {
    ...
    return IRQ_HANDLED;
}
 
// 할당
if( request_irq( PIR_IRQ, pir_isr, IRQF_TRIGGER_RISING, "pir_irq"NULL< 0 )
{
    printk("[%s] Not request interrupt\n", __FUNCTION__ );
    return -EBUSY;
}
 
// 해제
free_irq(PIR_IRQ, NULL);








테스크릿


- 인터럽트 후반부 처리

1. 인터럽트 핸들러에서 오랜 시간을 점유 하는 작업을 처리하기 위한 인터페이스.

* 인터럽트 핸들러는 빠르고 간단한 일만 수행.

* 끝날 때쯤 후반부 처리기(softirq, tasklet, workqueue)에게 위임.


- 테스크릿 구조체

struct tasklet_struct
{
    struct tasklet_struct *next;        // 다음 태스크릿 포인터
    unsigned long state;                // 태스크릿 상태
    atomic_t count;                        // 참조 카운터로 0 일 때 실행
    void (*func)(unsigned long);        // 태스크릿 핸들러(함수포인터)
    unsigned long data;                    // 핸들러에 전달될 매개 변수
}
cs

* func : 태스크릿 핸들러는 매개변수인 data를 받아들임.

* stat : 0, TASKLET_STATE_SCHED, TASKLET_STATE_RUN 중 하나의 값.



- 테스크릿 선언

#include <linux/interrupt.h>
 
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);
cs

* tasklet_struct 구조체를 정적으로 생성.

** DECLARE_TASKET은 count = 0, DECLARE_TASKLET_DISABLED는 1.

* 태스크릿을 실행하면 func 핸들러에 data를 인자로 넘겨 실행.


- 태스크릿 핸들러

void tasklet_handler(unsigned long data) { //원하는 이름 가능
    ..
}
cs

* tasklet_schedule()에 의해 실행.



- 태스크릿 스케줄링

void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);
 
cs

1. 태스크릿의 state가 TASKLET_STATE_SCHED인지 체크.

* 이미 스케줄링 상태면 무시.

2. 인터럽트 상태를 저장하고 로컬 인터럽트 비활성화.

* 태스크릿을 다루는 동안 다른 곳에서 태스크릿 코드 방해 방지.

3. 스케줄링 할 태스크릿을 각 프로세서의 tasklet_vec 또는 tasklet_hi_vec에 등록.

* 연결 리스트 앞부분에 추가.

4. TASKLET_SOFTIRQ 또는 HI_SOFTIRQ 표시.

* softirq는 실행 전에 반드시 표시되어야함.

* 태스크릿은 가까운 미래에 do_softirq().에 의해 실행.

5. 인터럽트를 원래 상태로 복원한 후 복귀.




인터럽트 기반 택 스위치 제어 모듈 구현 예제.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
 
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
 
int sw_stat;
struct task_struct *sw_task;
extern void led_set_data(int data);
 
// GPIO 번호
#define JOG_UP      5
#define JOG_DOWN    6
#define JOG_LEFT   16
#define JOG_RIGHT  20
#define JOG_CENTER 21
 
static unsigned int sw_irq[] = {
    JOG_UP,       // irq:171
    JOG_DOWN,     // irq:172
    JOG_LEFT,     // irq:182 
    JOG_RIGHT,    // irq:186
    JOG_CENTER,   // irq:187
};
 
// 인터럽트 후 처리를 위한 태스크릿 함수 sw_tasklet_func() 구현
void sw_tasklet_func(unsigned long unuse){
    printk("%s", __FUNCTION__);
}
 
// 테스크릿 선언
// task_struct 구조체를 정적으로 생성
DECLARE_TASKLET(sw_tasklet, sw_tasklet_func, 0);
//                구조체          함수
 
 
// 인터럽트 처리 함수
irqreturn_t sw_isr(int irq, void *unuse) {
    int i;
            
    printk("#### [%s], irq = %d\n", __FUNCTION__, irq);
    
    // 스캐줄링 할 태스크릿을 등록
    tasklet_schedule(&sw_tasklet);
    return IRQ_HANDLED;
}
 
static int mod_hw_sw_int_init(void)
{
    int ret;
    int i;
 
    for (i = 0; i < ARRAY_SIZE(sw_irq); i++) {
        sw_irq[i] = gpio_to_irq(sw_irq[i]);
        printk("sw_irq=%d\n", sw_irq[i]);
        
        // 인터럽트 핸들러 등록
        ret = request_irq(sw_irq[i], sw_isr, IRQF_TRIGGER_RISING, "jog_sw irq"NULL);
        if(ret){
            printk("FAILED Request irq %d. error : %d \n", sw_irq[i], ret);
        }
    }
    if(ret) {
        return ret;
    }
    return 0;
}
 
static void mod_hw_sw_int_exit(void)
{
    int i;
 
    // 태스크릿 제거
    tasklet_kill(&sw_tasklet);
    for (i = 0; i < ARRAY_SIZE(sw_irq); i++){
        
        //인터럽트 핸들러 제거
        free_irq(sw_irq[i], NULL);
    }
}
 
module_init(mod_hw_sw_int_init);
module_exit(mod_hw_sw_int_exit);
 
MODULE_AUTHOR("PlanX Studio");
MODULE_DESCRIPTION("mod_hw_sw_int");
MODULE_LICENSE("Dual BSD/GPL");
 
cs






출처 : 인지소프트웨어 기술포럼 ( 전유진 강사님 수업자료)

+ Recent posts