Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

20113
1478
166890


[리눅스커널][인터럽트] 인터럽트 핸들러 등록하기 - request_irq() 5. 인터럽트

인터럽트가 발생하면 이를 핸들링하는 인터럽트 핸들러가 호출됩니다. 예를 들면 다음과 같습니다.
 - 컴퓨터에서 키보드 자판을 입력하면 키보드 인터럽트가 발생해 키보드 인터럽트 핸들러가 
 - 휴대폰에서 USB 케이블을 연결하면 USB 인터럽트가 발생해 USB 인터럽트 핸들러가 호출됩니다.

디바이스 드라이버 입장에서 이렇게 인터럽트가 발생해 인터럽트 핸들러가 호출되려면 인터럽트를 제대로 초기화시켜야 합니다.

이번 절에서는 인터럽트 핸들러를 등록하는 방법과 이를 검증하는 실습 패치를 살펴봤습니다. 인터럽트 핸들러를 등록하는 코드는 실전 디바이스 드라이버 개발에서 자주 접할 가능성이 매우 높으니 잘 숙지합시다. 

인터럽트 핸들러 등록 과정 분석하기 

인터럽트 핸들러 등록은 인터럽트를 초기화하는 과정에 포함돼 있습니다.
인터럽트 초기화하려면 호출하는 request_irq() 함수 선언부와 함수 사용법에 대해 살펴보겠습니다.

request_irq() 함수 소개  

request_irq() 함수 선언부는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
    const char *name, void *dev)

request_irq() 함수로 인터럽트를 정상 초기화하면 true, 아니면 false를 반환합니다.

다음 인자를 확인합시다.

unsigned int irq;
인터럽트 번호입니다.

irq_handler_t handler;
인터럽트가 발생하면 호출될 인터럽트 핸들러 주소입니다.

unsigned long flags;
인터럽트 속성 플래그입니다.

const char *name;
인터럽트 이름입니다.

void *dev;
인터럽트 핸들러에 전달하는 매개인자입니다. 
보통 디바이스 드라이버를 제어하는 구조체 주소를 전달합니다.
디바이스 드라이버와 인터럽트 핸들러를 연결하는 중요한 인터페이스입니다.

request_irq() 함수를 써서 인터럽트를 초기화하는 라즈비안 코드 분석 

request_irq() 함수 선언부를 소개했으니 라즈비안에서 request_irq() 함수를 써서 인터럽트를 초기화하는 코드를 보겠습니다.
 [https://github.com/raspberrypi/linux/blob/rpi-4.19.y/drivers/usb/host/dwc_otg/dwc_otg_driver.c]
01 static int dwc_otg_driver_probe(
02 #ifdef LM_INTERFACE
03        struct lm_device *_dev
...
04          int retval = 0;
05          dwc_otg_device_t *dwc_otg_device;
06          int devirq;
...          
07 dev_dbg(&_dev->dev, "Calling request_irq(%d)\n", devirq);
08 retval = request_irq(devirq, dwc_otg_common_irq,
09                             IRQF_SHARED,
10                             "dwc_otg", dwc_otg_device);

08 번째 줄 코드를 보면 request_irq() 함수를 호출해 인터럽트를 초기화합니다.
request_irq() 함수로 전달하는 인자는 다음과 같습니다.
 - unsigned int irq = devirq(62);    /* 인터럽트 번호 */
 - irq_handler_t handler = dwc_otg_common_irq;  /* 인터럽트 핸들러 */
 - unsigned long flags = 0x80(IRQF_SHARED);  /* 인터럽트 속성 플래그 */
 - const char *name = "dwc_otg";  /* 인터럽트 이름  */
 - void *dev = dwc_otg_device; /* 인터럽트 핸들러에 전달한 매개인자 */

각 필드에 저장하는 인터럽트 속성은 위 코드 주석문을 참고합시다.

request_irq() 함수와 request_threaded_irq() 함수 분석

위에서 살펴본 dwc_otg_driver_probe() 함수에서 request_irq() 함수에 전달한 인자가 커널 내부에서 어떻게 초기화되는지 궁금하지 않나요? request_irq() 함수 선언부와 전달 인자를 소개했으니 이어서 request_irq() 함수 소스 코드 분석으로 들어갑니다.

request_irq() 함수 코드 구현부를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
01 static inline int __must_check
02 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
03     const char *name, void *dev)
04 {
05 return request_threaded_irq(irq, handler, NULL, flags, name, dev);
06 }

05번째 줄 코드를 보면 request_irq() 함수에 전달된 인자를 실어서 request_threaded_irq() 함수를 호출합니다. request_irq() 함수는 특별한 동작이 없습니다.

request_threaded_irq() 함수 세부 코드 분석 전 함수 주요 동작을 소개합니다.
 - 인터럽트 번호로 인터럽트 디스크립터 가져오기
 - struct irqaction 구조체로 동적 메모리 할당
 - struct irqaction 구조체 필드에 인터럽트 초기화 인자 설정하기
  : 인터럽트 핸들러, 인터럽트 속성, 인터럽트 핸들러 매개인자

이어서 request_threaded_irq() 함수 코드를 분석하겠습니다.
01 int request_threaded_irq(unsigned int irq, irq_handler_t handler,
02 irq_handler_t thread_fn, unsigned long irqflags,
03 const char *devname, void *dev_id)
04 {
05 struct irqaction *action;
06 struct irq_desc *desc;
07 int retval;
...
08 desc = irq_to_desc(irq);
09 if (!desc)
10 return -EINVAL;
...
11 action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
12 if (!action)
13 return -ENOMEM;
...
14 action->handler = handler;
15 action->thread_fn = thread_fn;
16 action->flags = irqflags;
17 action->name = devname;
18 action->dev_id = dev_id;

먼저 08 번째 줄 코드입니다.
08 desc = irq_to_desc(irq);
09 if (!desc)
10 return -EINVAL;
정수형 인터럽트 번호를 저장한 irq 인자로 irq_to_desc() 함수를 호출해 인터럽트 디스크립터 주소를 desc 변수에 가져옵니다. 만약 인터럽트 디스크립터 주소가 NULL이면 09~10 번째 줄 코드와 같이 -EINVAL 를 반환합니다. 만약 디바이스 드라이버에서 오타나 실수로 잘못된 인터럽트 번호로 request_irq() 함수를 호출하면 이 부분에서 에러 코드를 반환하며 request_threaded_irq() 함수 실행을 마칩니다.
  
다음 11~13 번째 줄 코드를 보겠습니다.
11 action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
12 if (!action)
13 return -ENOMEM;

11 번째 줄 코드는 kzalloc() 함수를 호출해 struct irqaction 구조체 크기로 동적 메모리를 할당 받습니다. 12~13 번째 줄은 동적 메모리 할당을 못 받을 경우 -ENOMEM 에러 코드를 반환하며 함수 실행을 종료합니다.
 
다음은 14~18 번째 줄 코드입니다.
14 action->handler = handler;      /* 인터럽트 핸들러 */
15 action->thread_fn = thread_fn;  /* 스레드 핸들러*/
16 action->flags = irqflags;     /* 인터럽트 속성 플래그*/
17 action->name = devname;       /* 인터럽트 이름*/
18 action->dev_id = dev_id;    /* 인터럽트 핸들러 매개인자*/

request_irq() 함수로 전달된 인자를 struct irqaction 구조체 필드에 저장합니다. 
각 필드에 저장하는 인터럽트 속성은 위 코드 주석문을 참고합시다.
 

"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

# Reference (인터럽트 처리)

인터럽트 소개  
   * 리눅스 커널에서의 인터럽트 처리 흐름    
인터럽트 컨텍스트  
인터럽트 핸들러는 언제 호출될까?  
인터럽트 핸들러는 어떻게 등록할까?  
인터럽트 디스크립터  
인터럽트 디버깅  


# Reference: 리눅스 커널

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2





핑백

덧글

댓글 입력 영역