IRQ 스레드 생성 예제 코드 분석 

이번에는 IRQ 스레드를 생성하는 예제 코드를 소개합니다. 실제 request_threaded_irq() 함수를 호출해서 IRQ 스레드를 생성하는 과정을 살펴보겠습니다.

분석할 코드는 다음과 같습니다.
1 static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
3 {
4 struct dwc3 *dwc = gadget_to_dwc(g);
5 unsigned long flags;
6 int ret = 0;
7 int irq;
8
9 irq = dwc->irq_gadget;
10 ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
11 IRQF_SHARED, "dwc3", dwc->ev_buf);

먼저 request_threaded_irq() 함수에 전달하는 인자를 살펴봅시다.
   - irq: 인터럽트 번호
   - dwc3_interrupt: 인터럽트 핸들러
   - dwc3_thread_interrupt: 인터럽트 스레드 핸들 함수
   - IRQF_SHARED: 인터럽트 플래그
   - "dwc3": 인터럽트 이름
   - dwc->ev_buf: 인터럽트 핸들러와 인터럽트 스레드 핸들 함수에 전달하는 매개인자

5장에서 인터럽트 핸들러를 설정할 때 썼던 request_irq() 함수와 유사해 보입니다. request_irq() 함수를 호출할 때 비슷한 타입의 인자를 전달하는, request_threaded_irq() 함수는 IRQ 스레드 핸들러인 dwc3_thread_interrupt() 함수를 추가합니다.

request_threaded_irq() 함수를 호출하면 해당 인터럽트에 대한 전용 IRQ 스레드가 생성됩니다. 

리눅스 커널에서 IRQ 스레드 이름을 어떻게 결정할까요?? 위 인터럽트 번호가 47이면 IRQ 스레드 이름은 "irq/47-dwc3" 입니다.

인터럽트 발생 후 dwc3_interrupt() 이란 인터럽트 핸들러에서 인터럽트에 대한 처리를 한 다음 "irq/47-dwc3" IRQ 스레드를 깨울지 결정합니다. 이후 "irq/47-dwc3" IRQ 스레드가 깨어나면 스레드 핸들러인 dwc3_thread_interrupt() 함수가 호출되는 구조입니다.

이번에는 인터럽트 핸들러인 dwc3_thread_interrupt() 함수를 보면서 세부 동작을 확인합시다. 
static irqreturn_t dwc3_interrupt(int irq, void *_evt)
{
struct dwc3_event_buffer *evt = _evt;
return dwc3_check_event_buf(evt);
}

dwc 인터럽트가 발생하면 dwc3_interrupt() 이란 인터럽트 핸들러가 실행됩니다. dwc3_interrupt() 함수는 특별한 동작을 하지 않습니다. 바로 dwc3_check_event_buf() 함수를 호출합니다.

dwc3_check_event_buf() 함수 구현부는 다음과 같습니다.
1 static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
2 {
3 struct dwc3 *dwc = evt->dwc;
4 u32 amount;
5 u32 count;
6 u32 reg;
7
8 if (pm_runtime_suspended(dwc->dev)) {
9 pm_runtime_get(dwc->dev);
10 disable_irq_nosync(dwc->irq_gadget);
11 dwc->pending_events = true;
12 return IRQ_HANDLED;
13 }
...
14 if (amount < count)
15 memcpy(evt->cache, evt->buf, count - amount);
16
17 dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
18
19 return IRQ_WAKE_THREAD;
20}

위 함수를 눈여겨보면 시스템 상태에 따라 IRQ_HANDLED와 IRQ_WAKE_THREAD를 리턴합니다. 인터럽트가 발생한 후 일을 더 할 필요가 없을 때는 다음 12 번째 줄 코드와 같이 IRQ_HANDLED를 반환합니다.
8 if (pm_runtime_suspended(dwc->dev)) {
9 pm_runtime_get(dwc->dev);
10 disable_irq_nosync(dwc->irq_gadget);
11 dwc->pending_events = true;
12 return IRQ_HANDLED;
13 }

그런데 IRQ 스레드가 해당 인터럽트 핸들러 실행 이후 후속 처리를 수행해야 할 때는 IRQ_WAKE_THREAD를 반환합니다.
17 dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
18
19 return IRQ_WAKE_THREAD;
20}

이후 IRQ 스레드가 깨어난 후 IRQ 스레드 핸들러인 dwc3_thread_interrupt() 함수가 실행됩니다. 이 함수에서 인터럽트 핸들러에서 바로 처리하지 못한 일을 수행합니다. 


+ Recent posts