/*
 * @brief Callback function which is registerd to DF layer
 * @param
 *     msg_id    [in] Exception ID
 * @return
 *     None
 */
void ccci_df_to_ccci_exception_callback(KAL_UINT32 msg_id)
{
    DEBUG_LOG_FUNCTION_ENTRY;
    DBGLOG(EXPT, TRA, "CCCI Exception Callback (%d)", msg_id);
    switch (msg_id) {
        case EX_INIT:
        {
            //reset exception statistics
            eemcs_expt_reset_statistics();
            
            //set eemcs state to EEMCS_EXCEPTION
            change_device_state(EEMCS_EXCEPTION);

            //set a 5s timer to check if exception flow complete
            mod_timer(&g_except_inst.md_ex_monitor, jiffies+EE_INIT_TIMER);

            //update eemcs_exception state : EEMCS_EX_NONE -> EEMCS_EX_INIT or EEMCS_EX_INIT_DONE-> EEMCS_EX_INIT
            set_exception_mode(EEMCS_EX_INIT);

            //indicate state change to upper layer
            eemcs_exec_expt_callback(EEMCS_EX_INIT);

            //Send message to mdinit
            eemcs_cdev_msg(CCCI_PORT_CTRL, CCCI_MD_MSG_EXCEPTION, 0);

            //snapeshot traffic
            eemcs_ccci_expt_log_df_q_pkts();

            break;
        }
        case EX_DHL_DL_RDY:
            //1. update eemcs_exception state : EEMCS_EX_INIT -> EEMCS_EX_DHL_DL_RDY
            set_exception_mode(EEMCS_EX_DHL_DL_RDY);

            //2. indicate state change to upper layer
            eemcs_exec_expt_callback(EEMCS_EX_DHL_DL_RDY);
            break;
        case EX_INIT_DONE:
            //1. update eemcs_exception state : EEMCS_EX_DHL_DL_RDY -> EEMCS_EX_INIT_DONE
            set_exception_mode(EEMCS_EX_INIT_DONE);

            //2. set up timer for waiting MD_EX msg
            mod_timer(&g_except_inst.md_ex_monitor, jiffies+EE_HS1_TIMER);

            //3. flush all pkts in exception list
            eemcs_expt_flush();
            
            //4. indicate state change to upper layer
            eemcs_exec_expt_callback(EEMCS_EX_INIT_DONE);
            break;
        default:
            DBGLOG(EXPT, ERR, "Unknown exception callback id %d", msg_id);
    }
	
    DEBUG_LOG_FUNCTION_LEAVE;
    return;
}
/*
 * @brief Initialization of exception handling
 * @param
 *     None
 * @return
 *     This function returns KAL_SUCCESS always.
 */
KAL_INT32 eemcs_expt_mod_init(void)
{
    KAL_UINT32 i = 0;
    //KAL_UINT32 except_txq = 0, except_rxq = 0;
    //KAL_UINT32 nonstop_rxq = 0; /* for Log path to output as much as possible */
    KAL_INT32  ret;
    ccci_port_cfg *log_queue_config;
    DEBUG_LOG_FUNCTION_ENTRY;

    //Init md exception type
    g_except_inst.md_ex_type = 0;

    /* init expt_cb and expt_cb_lock*/
    spin_lock_init(&g_except_inst.expt_cb_lock);
    for(i = 0; i < CCCI_PORT_NUM_MAX; i++) {
        g_except_inst.expt_cb[i] = NULL;

        if(TR_Q_INVALID != ccci_expt_port_info[i].expt_txq_id)
        {
            set_bit(SDIO_TXQ(ccci_expt_port_info[i].expt_txq_id), (unsigned long *)&except_txq);
        }
        if(TR_Q_INVALID != ccci_expt_port_info[i].expt_rxq_id)
        {
            set_bit(SDIO_RXQ(ccci_expt_port_info[i].expt_rxq_id), (unsigned long *)&except_rxq);
        }
    }

    eemcs_expt_ut_init();

    log_queue_config = ccci_get_port_info(CCCI_PORT_MD_LOG);
    set_bit(SDIO_RXQ(log_queue_config->rxq_id), (unsigned long *)&nonstop_rxq);
    hif_except_init(nonstop_rxq, (except_txq << 16) | except_rxq);

    ret = hif_reg_expt_cb(ccci_df_to_ccci_exception_callback);
    KAL_ASSERT(ret == KAL_SUCCESS);
    
    DBGLOG(EXPT, TRA, "nonstop_txq=%d, nonstop_rxq=%d, exp_txq=%d, exp_rxq=%d",
        0, nonstop_rxq, except_txq, except_rxq);


    /* Init Tx Q list */
    for (i = 0; i < SDIO_TX_Q_NUM; i++) {
        g_except_inst.txq[i].id = -1;
        atomic_set(&g_except_inst.txq[i].pkt_cnt, 0);
        skb_queue_head_init(&g_except_inst.txq[i].skb_list);
    }
    /* Init Rx Q list */
    for (i = 0; i < SDIO_RX_Q_NUM; i++) {
        g_except_inst.rxq[i].id = -1;
        atomic_set(&g_except_inst.rxq[i].pkt_cnt, 0);
        skb_queue_head_init(&g_except_inst.rxq[i].skb_list);
    }
    /* Init port list */
    for (i = 0; i < CCCI_PORT_NUM; i++) {
        atomic_set(&g_except_inst.port[i].pkt_cnt, 0);
        skb_queue_head_init(&g_except_inst.port[i].skb_list);
    }
    /* initialize drop count */
    eemcs_expt_reset_statistics();

    /* initialize exception*/
    eemcs_exception_state = EEMCS_EX_NONE;

    /* initialize exception timer*/
    init_timer(&g_except_inst.md_ex_monitor);
    g_except_inst.md_ex_monitor.function = ex_monitor_func;
    g_except_inst.md_ex_monitor.data = (unsigned long)&g_except_inst;
    
#ifdef ENABLE_MD_WDT_PROCESS
    eemcs_ccci_register_WDT_callback(eemcs_wdt_reset_callback);
#endif

    DEBUG_LOG_FUNCTION_LEAVE;

    return KAL_SUCCESS;
}