Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

11105
637
415734


[라즈베리파이] 인터럽트 핸들러 등록(2) - #CS 5. 인터럽트

이번에는 다른 코드를 입력해서 인터럽트 핸들러와 인터럽트 디스크립터에 대해 알아볼게요.

다음 패치는 인터럽트 핸들러를 등록한 후 바로 해당 인터럽트 번호로 인터럽트 디스크립터를 커널 함수로 읽어와 인터럽트 설정 정보를 출력합니다. 제대로 인터럽트 핸들러를 등록했는지 점검하고 싶을 때 활용하면 좋은 코드입니다. 파라미터가 인터럽트 번호이므로 인터럽트 번호만 대입하면 다른 함수에서도 호출할 수 있죠.

이제부터 코드를 어떻게 작성했는지 함께 살펴볼까요? 우선 dwc_otg_driver_probe 함수 위에 다음 코드를 입력합니다.
1 static void interrupt_debug_irq_desc(int irq_num)
2 {
3 struct irqaction *action;
4 struct irq_desc *desc;
5
6 desc = irq_to_desc(irq_num);
7
8 if (!desc ) {
9 pr_err("invalid desc at %s line: %d\n", __func__, __LINE__);
10 return;
11 }
12
13 action = desc->action;
14
15 if (!action ) {
16 pr_err("invalid action at %s line:%d \n", __func__, __LINE__);
17 return;
18 }
19
20 printk("[+] irq_desc debug start \n");
21
22 printk("irq num: %d name: %8s \n", action->irq , action->name);
23 printk("dev_id:0x%x \n", (unsigned int)action->dev_id);
24
25      if (action->handler) {
26 printk("interrupt handler: %pF \n", action->handler);
27 }
28
29 printk("[-] irq_desc debug end \n");
30}
 
/**
 * This function is called when an lm_device is bound to a
 * dwc_otg_driver. It creates the driver components required to
 * control the device (CIL, HCD, and PCD) and it initializes the
 * device. The driver components are stored in a dwc_otg_device
 * structure. A reference to the dwc_otg_device is saved in the
 * lm_device. This allows the driver to access the dwc_otg_device
 * structure on subsequent calls to driver methods for this device.
 *
 * @param _dev Bus device
 */
static int dwc_otg_driver_probe(
#ifdef LM_INTERFACE
       struct lm_device *_dev

이제 패치 코드를 조금 더 분석해볼까요?

우선 여섯 번째 줄 코드부터 볼게요. irq_to_desc 란 함수를 써서 세부 인터럽트 정보가 있는 인터럽트 디스크립터를 읽어옵니다. 
6 desc = irq_to_desc(irq_num);
 
여기서 인터럽트 디스크립터 구조체는 struct irq_desc 이니 눈여겨보세요. 조금 난해한 함수로 보이지만 함수 이름처럼 인터럽트 번호를 받아 인터럽트 디스크립터를 알려줍니다.

이번에는 13번째 줄 코드입니다.
13 action = desc->action;

인터럽트 디스크립터에서 struct irq_desc.action란 멤버가 인터럽트 핸들러, 인터럽트 번호, 인터럽트 플레그 정보를 담고 있습니다. 이 정보를 출력하려고 desc->action 이란 포인터를 struct irqaction 구조체인 action이란 지역 변수로 읽어 옵니다.

22번째부터 25번째까지 코드를 볼 차례입니다. 인터럽트에서 가장 중요한 정보를 출력하는 코드이니 조금 더 집중해서 볼 필요가 있습니다. 
22 printk("irq num: %d name: %8s \n", action->irq , action->name);
23 printk("dev_id:0x%x \n", (unsigned int)action->dev_id);
24
25      if (action->handler) {
26 printk("interrupt handler: %pF \n", action->handler);
27 }
 
인터럽트 이름, 인터럽트 번호, 디바이스 핸들 정보인 action->dev_id 그리고 인터럽트 핸들러를 출력합니다. 참고로 printk로 함수 포인터를 심볼 정보로 보고 싶을 때는 %pF로 옵션을 주면 됩니다.

이번에는 interrupt_debug_irq_desc 함수를 호출할 함수를 살펴볼게요. 이전에dwc_otg_driver_probe 함수에서 dwc_otg_common_irq 인터럽트 핸들러를 등록할 때 request_irq 함수를 호출했었죠? 이 코드 다음에 이 함수를 호출하면 됩니다.
static int dwc_otg_driver_probe(
#ifdef LM_INTERFACE
       struct lm_device *_dev
#elif defined(PCI_INTERFACE)
       struct pci_dev *_dev,
       const struct pci_device_id *id
#elif  defined(PLATFORM_INTERFACE)
                                       struct platform_device *_dev
#endif
    )
{
int retval = 0;
dwc_otg_device_t *dwc_otg_device;
//...
DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
    devirq);
dev_dbg(&_dev->dev, "Calling request_irq(%d)\n", devirq);
retval = request_irq(devirq, dwc_otg_common_irq,
                             IRQF_SHARED,
                             "dwc_otg", dwc_otg_device);
        interrupt_debug_irq_desc(devirq);

interrupt_debug_irq_desc 함수를 호출할 때 전달하는 devirq 지역 변수는 인터럽트 번호를 담고 있습니다.

위 디버깅 코드를 입력하고 라즈베이란을 구동시키면 다음과 같은 커널 로그를 확인할 수 있습니다.
[0.496039] [+] irq_desc debug start 
[0.496048] irq num: 62 name:  dwc_otg 
[0.496055] dev_id:0xb9355c40 
[0.496065] interrupt handler: dwc_otg_common_irq+0x0/0x28 
[0.496071] [-] irq_desc debug end

다음 코드에서 인터럽트 이름은 dwc_otg, 62번 인터럽트 번호로 인터럽트를 등록한다는 정보를 알 수 있죠. 
retval = request_irq(devirq, dwc_otg_common_irq,
                             IRQF_SHARED,
                             "dwc_otg", dwc_otg_device);

이렇게 우리가 알고 싶은 함수에 적절한 디버깅 코드를 입력하면 해당 코드가 어느 시점에 호출하는지 알 수 있습니다. 리눅스 커널을 익힐 때 코드만 보는 것보다 이렇게 로그를 추가해서 시스템을 돌려보면 더 많은 정보를 얻을 수 있습니다.



핑백

덧글

댓글 입력 영역