SW 개발

winCE / kernel / 인터럽트 초기화 과정, InterruptInitialize()함수 실행과정

. . . 2009. 4. 22. 01:55
반응형

Xeno's Study Blog (http://XenoStudy.tistory.com)
- 글쓴이 : xeno
- 출처 : 나 + MSDN 
- 기타사항 : winCE5.0(Platform Builder 5) 기준 BSP 중에 MAINSTONEII 를 기준으로 자료조사
틀린사항은 댓글로 달아주세여~

인터럽트 초기화 과정에 불리는 InterruptInitialize() 함수에 대해 안에서 어떠한 일이 일어나는지 공부할겸 정리하였다.

... 시작!

인터럽트 관련 reference ..
MSDN :
http://msdn.microsoft.com/en-us/library/aa447163.aspx
 
BOOL InterruptInitialize(
     DWORD idInt,
     HANDLE hEvent,
     LPVOID pvData,
     DWORD cbData );
MSDN : http://msdn.microsoft.com/en-us/library/aa909156.aspx

app에서 InterruptInitialize() 를 호출하면 커널에서 결국은 OEMInterruptEnable() 를 호출하게 된다. 이때..커널은 hEvent 는 자신이 빼고 idInt는 OEMInterruptEnable() 으로 넘기게 된다.
인자 idInt 는 OEMInterruptEnable() 의 sysIntr로 넘어가게 된다.

d:\wince500\platform\common\src\common\intr\COMMON\oem.c 
//------------------------------------------------------------------------------
//
//  Function:  OEMInterruptEnable
//
//  This function enables the IRQ given its corresponding SysIntr value.
//  Function returns true if SysIntr is valid, else false.
//
BOOL OEMInterruptEnable(DWORD sysIntr, LPVOID pvData, DWORD cbData)
{
    BOOL rc = FALSE;
    const UINT32 *pIrqs;
    UINT32 count;

    OALMSG(OAL_INTR&&OAL_VERBOSE,
        (L"+OEMInterruptEnable(%d, 0x%x, %d)\r\n", sysIntr, pvData, cbData
    ));

    // SYSINTR_VMINI & SYSINTR_TIMING are special cases
    if (sysIntr == SYSINTR_VMINI || sysIntr == SYSINTR_TIMING) {
        rc = TRUE;
        goto cleanUp;
    }

    // Obtain the SYSINTR's underlying IRQ number
    if (!OALIntrTranslateSysIntr(sysIntr, &count, &pIrqs)) {
        // Indicate invalid SysIntr
        OALMSG(OAL_ERROR, (
            L"ERROR: OEMInterruptEnable: IRQs are undefined for SysIntr %d\r\n",
            sysIntr
        ));
        goto cleanUp;
    }

    // Enable the interrupt
    rc = OALIntrEnableIrqs(count, pIrqs);

cleanUp:    
    OALMSG(OAL_INTR&&OAL_VERBOSE, (L"-OEMInterruptEnable(rc = 1)\r\n"));
    return rc;
}

위에서 보면...OALIntrEnableIrqs() 를 호출하게 된다.
OAL은 플랫폼 종속적인 코드니까.. d:\wince500\platform 폴더 밑의 모든 소스를 find & search 해봤다.
결과..

OALIntrEnableIrqs() 의 원형은 다음과 같다.
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)

잠깐 참고자료..
http://msdn.microsoft.com/en-us/library/ms901819.aspx
The interrupt library implements interrupt processing. Support for interrupts is provided by a combination of OAL common routines and processor specific routines.
 
Depending on the CPU or SOC model, you can implement the interrupt library with hardware platform callbacks. This method is useful on hardware where you have used an external secondary interrupt controller.

For more information, see %_WINCEROOT%\Platform\Common\Src\Inc\Oal_intr.h.
각설하고.. 역시 CPU 및 SOC 에 종속적인 코드였다.

찾은 위치는 다음과 같다. ( OALIntrEnableIrqs() 이 선언되어있는 위치.. )

일단 해더파일의 내용..
d:\wince500\platform\common\src\INC\oal_intr.h
//------------------------------------------------------------------------------
//
//  Function:  OALIntrEnableIrqs/BSPIntrEnableIrqs
//
//  This function enable list of interrupts identified by IRQ. If
//  implementation uses platform callbacks OALIntrEnableIrqs must call
//  BSPIntrEnableIrq for IRQ before it enables this IRQ. The BSPIntrEnableIrq
//  can do board specific action and optionaly modify IRQ.
//
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs);
UINT32 BSPIntrEnableIrq(UINT32 irq);


d:\wince500\platform\common\src\arm\intel\pxa27x\INTR\intr.c
d:\wince500\platform\common\src\arm\samsung\s3c2410x\INTR\intr.c
d:\wince500\platform\common\src\mips\amd\au1\INTR\intr.c
d:\wince500\platform\common\src\mips\broadcom\bcm11\INTR\intr.c
d:\wince500\platform\common\src\mips\nec\vr4131\INTR\intr.c
d:\wince500\platform\common\src\mips\nec\vrc5477\INTR\intr.c

부가설명..
intr.c 소스에 포함되어있는 함수들..
BOOL OALIntrInit()
BOOL OALIntrRequestIrqs(DEVICE_LOCATION *pDevLoc, UINT32 *pCount, UINT32 *pIrq)
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)
VOID OALIntrDisableIrqs(UINT32 count, const UINT32 *pIrqs)
VOID OALIntrDoneIrqs(UINT32 count, const UINT32 *pIrqs)
위의 함수는 각 아키텍쳐마다 공통적으로 포함되어있는 함수들.
===============================================================================
아래 함수는 아키텍쳐마다 포함 혹은 미포함되어있다.
(아무래도 아키텍쳐마다 인터럽 처리 플로우가 약간 틀린듯..)
ULONG OEMInterruptHandler(ULONG ra)
UINT32 OALIntr0Handler()
UINT32 OALIntr1Handler()
UINT32 OALIntr2Handler()
UINT32 OALIntrHandler (PULONG pStsReg)

다시 돌아와서..

경로를 보면 알겠지만.. OALIntrEnableIrqs()는 아키텍쳐 별로 일단 다 들어가있다.
OALIntrEnableIrqs()함수 내용을 잠깐 살펴보면..

초반에 BSPIntrEnableIrq()를 호출하게 된다.
BSPIntrEnableIrq() 에서 irq를 리턴받아서 해당 irq에 따라서 적절하게 레지스터 값을 변경한다.
다음 함수들을 이용해서 레지스터를 세팅하게 된다.

OUTREGxx(), SETREGxx() CLRREGxx() ==> 여기서 xx는 각 비트별로 숫자가 틀려진다 16 / 32

위의 함수들에 대한 설명은 여기로..
Windows :: Kernel :: winCE :: 커널소스 :: io 다루기 관련..
결국은 위의 함수를 이용해서 인터럽트 관련 레지스터를 조작하는듯..

BSPIntrEnableIrq()를 다시 찾아보면..
(플랫폼 종속적인 코드니까.. d:\wince500\platform 폴더 밑의 모든 소스를 find & search 해봤다.)

해더파일내용..
d:\wince500\platform\common\src\INC\oal_intr.h
//------------------------------------------------------------------------------
//
//  Function:  OALIntrEnableIrqs/BSPIntrEnableIrqs
//
//  This function enable list of interrupts identified by IRQ. If
//  implementation uses platform callbacks OALIntrEnableIrqs must call
//  BSPIntrEnableIrq for IRQ before it enables this IRQ. The BSPIntrEnableIrq
//  can do board specific action and optionaly modify IRQ.
//
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs);
UINT32 BSPIntrEnableIrq(UINT32 irq);

UINT32 BSPIntrEnableIrq(UINT32 irq)

d:\wince500\platform\mainstoneii\src\kernel\OAL\intr.c
d:\wince500\platform\smdk2410\src\kernel\OAL\intr.c

보면.. 역시 BSP 별로 구현되어있다.
그럼 소스내용을 잠깐 살펴보면..(d:\wince500\platform\mainstoneii\src\kernel\OAL\intr.c)
//------------------------------------------------------------------------------
//
//  Function:  BSPIntrEnableIrq
//
//  This function is called from OALIntrEnableIrq to enable interrupt on
//  secondary interrupt controller.
//
UINT32 BSPIntrEnableIrq(UINT32 irq
)
{
    OALMSG(OAL_INTR&&OAL_VERBOSE, (L"+BSPIntrEnableIrq(%d)\r\n", irq));

   
// Valid board-level interrupt?
    if (g_pBLRegs && (irq >= IRQ_MAINSTONEII_GPIO0_MIN) &&
       (irq <= IRQ_MAINSTONEII_GPIO0_MAX
))
   
{
        UINT32 TempVal = (INREG32((PULONG)&g_pBLRegs->int_msk_en) &
                         ~INTMSK_RESERVED_BITS);

        OUTREG32((PULONG)&g_pBLRegs->int_msk_en, (TempVal | FPGA_INT_BIT(irq)));

       
// Enabling the interrupt at the FPGA controller is enough -
           no need to enable the Bulverde GPIO0 interrupt.

        irq = OAL_INTR_IRQ_UNDEFINED;
   
}

    OALMSG(OAL_INTR&&OAL_VERBOSE, (L"-BSPIntrEnableIrq(irq = %d)\r\n", irq));
    return irq;
}

보면 알겠지만.. BSPIntrEnableIrq() 에서는 넘어온 irq가 해당 BSP 보드에서 유효한 irq인지 검사를 하게 된다. 해당 irq가 허용 인터럽트 라면... 무난히 irq가 리턴된다.
그럼.. 실질적으로는 irq 처리단은 OALIntrEnableIrqs() 이 된다.

그럼... OALIntrEnableIrqs()의 소스를 살펴보자.

d:\wince500\platform\common\src\arm\intel\pxa27x\INTR\intr.c
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)
{
    BOOL rc = TRUE;
    UINT32 irq, i;


    OALMSG(OAL_INTR&&OAL_FUNC,
(
        L"+OALIntrEnableIrqs(%d, 0x%08x)\r\n", count, pIrqs
    ));
   
    for (i = 0; i < count; i++)
{
#ifndef OAL_BSP_CALLBACKS
        irq = pIrqs[i];
#else
       
// Give BSP chance to enable irq on subordinate interrupt controller
        irq = BSPIntrEnableIrq(pIrqs[i]);
        if (irq == OAL_INTR_IRQ_UNDEFINED) continue;
#endif
        if (irq <= IRQ_RTCALARM)
{
           
// Enable the primary IRQ
            SETREG32(&g_pICReg->icmr, (1 << irq));
        } else
{
            rc = FALSE;
        }            
   
}

    OALMSG(OAL_INTR&&OAL_FUNC, (L"-OALIntrEnableIrqs(rc = %d)\r\n", rc));
    return rc;    
}

d:\wince500\platform\common\src\arm\samsung\s3c2410x\INTR\intr.c
BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)
{
    BOOL rc = TRUE;
    UINT32 i, mask, irq;

    OALMSG(OAL_INTR&&OAL_FUNC,
(
        L"+OALIntrEnableIrqs(%d, 0x%08x)\r\n", count, pIrqs
    ));

    for (i = 0; i < count; i++)
{
#ifndef OAL_BSP_CALLBACKS
        irq = pIrqs[i];
#else
       
// Give BSP chance to enable irq on subordinate interrupt controller
        irq = BSPIntrEnableIrq(pIrqs[i]);
#endif
        if (irq == OAL_INTR_IRQ_UNDEFINED) continue;
       
// Depending on IRQ number use internal or external mask register
        if (irq <= IRQ_ADC)
{
           
// Use interrupt mask register
            CLRREG32(&g_pIntrRegs->INTMSK, 1 << irq);
        } else if (irq <= IRQ_EINT7)
{
           
// Use external mask register
            CLRREG32(&g_pIntrRegs->INTMSK, 1 << IRQ_EINT4_7);
            CLRREG32(&g_pPortRegs->EINTMASK, 1 << (irq - IRQ_EINT4 + 4));
        } else if (irq <= IRQ_EINT23)
{
           
// Use external mask register
            mask = 1 << (irq - IRQ_EINT4 + 4);
            OUTREG32(&g_pPortRegs->EINTPEND, mask);
            CLRREG32(&g_pPortRegs->EINTMASK, mask);
            mask = 1 << IRQ_EINT8_23;
            if ((INREG32(&g_pIntrRegs->INTPND) & mask) != 0)
{
                OUTREG32(&g_pIntrRegs->INTPND, mask);
           
}
            CLRREG32( &g_pIntrRegs->INTMSK, 1 << IRQ_EINT8_23);
        } else
{
            rc = FALSE;
       
}
    }        

    OALMSG(OAL_INTR&&OAL_FUNC, (L"-OALIntrEnableIrqs(rc = %d)\r\n", rc));
    return rc;    
}

역시.. 인터럽트 처리하는게 아키텍여마다 틀린걸 알수있다.;;;
소스의 내용은 해당 irq에따라서 인터럽트 레지스터를 세팅하여 인터럽트 컨트롤러를 세팅하는 역할을 한다.

결론적으로 보면..

InterruptInitialize() ==> OALIntrEnableIrqs()

OALIntrEnableIrqs() 는 성공하면 TRUE / FALSE 를 리턴
OALIntrEnableIrqs() 는 중간에 BSPIntrEnableIrq() 를 호출하지만.. 단순히 유효한 irq인지만 확인하는 역할이다.
실질적인 모든 처리는 OALIntrEnableIrqs() 가 하게된다.
반응형