/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*        http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

/**
* @file:    isr.c
* @brief:   the general management of system isr
* @version: 1.0
* @author:  AIIT XUOS Lab
* @date:    2020/3/15
*
*/

#include <xiuos.h>
#include <xs_hook.h>

struct InterruptServiceRoutines isrManager = { 0} ;

#ifdef ARCH_SMP
extern int GetCpuId(void);
#endif
/**
 * This functionwill get the isr nest level.
 *
 * @return isr nest level
 */
 uint16 GetIsrCounter()
{
    uint16 ret = 0;

#ifdef ARCH_SMP
    ret = isrManager.isr_count[GetCpuId()];
#else
    ret = isrManager.isr_count;
#endif
    return ret;
}

 void IncIsrCounter()
{
#ifdef ARCH_SMP
     isrManager.isr_count[GetCpuId()] ++ ;
#else
     isrManager.isr_count ++;
#endif
    return ;
}

 void DecIsrCounter()
{

#ifdef ARCH_SMP
     isrManager.isr_count[GetCpuId()] -- ;
#else
     isrManager.isr_count --;
#endif
    return ;
}

 x_bool IsInIsr()
{
#ifdef ARCH_SMP
    return ( isrManager.isr_count[GetCpuId()] != 0 ? RET_TRUE : RET_FALSE ) ;
#else
    return ( isrManager.isr_count != 0 ? RET_TRUE : RET_FALSE ) ;
#endif

}
/**
 * This function will register a new irq.
 *
 * @param irq_num the number of the irq
 * @param handler the callback of the interrupt
 * @param arg param of thge callback
 *
 * @return EOK on success; ERROR on failure
 */
int32 RegisterHwIrq(uint32 irq_num, IsrHandlerType handler, void *arg)
{
    if (irq_num >=   ARCH_MAX_IRQ_NUM )
        return -ERROR;

    struct IrqDesc *desc = &isrManager.irq_table[irq_num];

    desc->handler = handler;
    desc->param = arg;

    return EOK;
}
/**
 * This function will free a irq.
 *
 * @param irq_num the number of the irq
 *
 * @return EOK on success; ERROR on failure
 */
int32 FreeHwIrq(uint32 irq_num)
{
    if (irq_num >=   ARCH_MAX_IRQ_NUM  )
        return -ERROR;

    memset(&isrManager.irq_table[irq_num], 0, sizeof(struct IrqDesc));

    return EOK;
}

/**
 * This function will enable a irq.
 *
 * @param irq_num the number of the irq
 *
 * @return EOK on success; ERROR on failure
 */
int32 EnableHwIrq(uint32 irq_num)
{
    if (irq_num >=   ARCH_MAX_IRQ_NUM  )
        return -ERROR;

    return ArchEnableHwIrq(irq_num);
}
/**
 * This function will disable a irq.
 *
 * @param irq_num the number of the irq
 *
 * @return EOK on success; ERROR on failure
 */

int32 DisableHwIrq(uint32 irq_num)
{
    if (irq_num >=   ARCH_MAX_IRQ_NUM  )
        return -ERROR;

    return ArchDisableHwIrq(irq_num);
}

/* called from arch-specific ISR wrapper */
void IsrCommon(uint32 irq_num)
{
    struct IrqDesc *desc = &isrManager.irq_table[irq_num];

    if (desc->handler == NONE) {
        SYS_KDEBUG_LOG(KDBG_IRQ, ("Spurious interrupt: IRQ No. %d\n", irq_num));
        while (1) {}
    }
    desc->handler(irq_num, desc->param);

}

 void SetIsrSwitchTrigerFlag()
 {

#ifdef ARCH_SMP
    isrManager.isr_switch_trigger_flag[GetCpuId()] = 1;
#else
    isrManager.isr_switch_trigger_flag = 1;
#endif

}

 void ClearIsrSwitchTrigerFlag()
 {

#ifdef ARCH_SMP
    isrManager.isr_switch_trigger_flag[GetCpuId()] = 0;
#else
    isrManager.isr_switch_trigger_flag = 0;
#endif
}

 uint8 GetIsrSwitchTrigerFlag()
 {
   
#ifdef ARCH_SMP
   return  isrManager.isr_switch_trigger_flag[GetCpuId()];
#else
   return isrManager.isr_switch_trigger_flag ;
#endif

}

struct IsrDone isrDone = {
    IsInIsr,
    RegisterHwIrq ,
    FreeHwIrq,
    EnableHwIrq,
    DisableHwIrq,
    IsrCommon,
    GetIsrCounter,
    IncIsrCounter,
    DecIsrCounter,
    GetIsrSwitchTrigerFlag,
    SetIsrSwitchTrigerFlag,
    ClearIsrSwitchTrigerFlag
} ;

void SysInitIsrManager()
{
    extern int __isrtbl_idx_start;
    extern int __isrtbl_start;
    extern int __isrtbl_end;
    memset(&isrManager,0,sizeof(struct InterruptServiceRoutines));
    isrManager.done = &isrDone;

    uint32 *index = (uint32 *)&__isrtbl_idx_start;
    struct IrqDesc *desc = (struct IrqDesc *)&__isrtbl_start;

    while (desc != (struct IrqDesc *)&__isrtbl_end)
        isrManager.irq_table[*index++] = *desc++;
}
