인터럽트 제어 구조
- 리눅스 커널의 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); |
* 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(3, 25)) 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 |
출처 : 인지소프트웨어 기술포럼 ( 전유진 강사님 수업자료)
'임베디드 > 디바이스 드라이버' 카테고리의 다른 글
디바이스 드라이버 시그널 예제 (0) | 2018.01.02 |
---|---|
디바이스 드라이버 모션센터 인터럽트 예 (0) | 2018.01.02 |
디바이스 드라이버 GPIO 모션 센서 예제 (0) | 2017.12.28 |
디바이스 드라이버 GPIO 조이스틱 예제 (0) | 2017.12.28 |
Misc 디바이스 드라이버2 (0) | 2017.12.27 |