/** 
 *  @brief This function handles the major job in WLAN driver.
 *  it handles the event generated by firmware, rx data received
 *  from firmware and tx data sent from kernel.
 *  
 *  @param data    A pointer to wlan_thread structure
 *  @return        WLAN_STATUS_SUCCESS
 */
static int
wlan_service_main_thread(void *data)
{
    wlan_thread *thread = data;
    wlan_private *priv = thread->priv;
    wlan_adapter *Adapter = priv->adapter;
    wait_queue_t wait;
    u8 ireg = 0;
    unsigned long driver_flags;

    OS_INTERRUPT_SAVE_AREA;

    ENTER();

    wlan_activate_thread(thread);

    init_waitqueue_entry(&wait, current);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
    current->flags |= PF_NOFREEZE;
#endif

    wlan_meas_init(priv);

    PRINTM(INFO, "11H: init 11H\n");
    wlan_11h_init(priv);

    wmm_init(priv);

    for (;;) {
#define MAX_MAIN_THREAD_LOOPS (10000)
        if (++Adapter->main_thread_loops > MAX_MAIN_THREAD_LOOPS) {
            panic("main-thread dead loop detected: %u\n"
                  "IntCnt=%d\n"
                  "Adapter->HisRegCpy=%#x\n"
                  "CurCmd=%p CmdPending=%s\n"
                  "Connect=%s\n"
                  "cmd_sent=%s\n"
                  "data_sent=%s\n"
                  "IsDeepSleep=%d\n"
                  "PSMode=%d PSState=%d\n"
                  "WakeupDevReq=%d\n"
                  "HS_Activated=%d\n"
                  "WakeupTries=%d\n", Adapter->main_thread_loops,
                  Adapter->IntCounter,
                  Adapter->HisRegCpy,
                  Adapter->CurCmd,
                  list_empty(&Adapter->CmdPendingQ) ? "N" : "Y",
                  (Adapter->MediaConnectStatus ==
                   WlanMediaStateConnected) ? "Y" : "N",
                  (priv->wlan_dev.cmd_sent) ? "TRUE" : "FALSE",
                  (priv->wlan_dev.data_sent) ? "TRUE" : "FALSE",
                  Adapter->IsDeepSleep, Adapter->PSMode, Adapter->PSState,
                  Adapter->bWakeupDevRequired, Adapter->HS_Activated,
                  Adapter->WakeupTries);
        }

        add_wait_queue(&thread->waitQ, &wait);
        OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);

        TX_DISABLE;

        if ((Adapter->WakeupTries) ||
            (Adapter->PSState == PS_STATE_SLEEP
             && !Adapter->bWakeupDevRequired) ||
            (!Adapter->IntCounter &&
             Adapter->PSState == PS_STATE_PRE_SLEEP) ||
            (!Adapter->IntCounter
             && (priv->wlan_dev.data_sent || Adapter->TxLockFlag
                 || wmm_lists_empty(priv) || wmm_is_queue_stopped(priv))
             && (priv->wlan_dev.cmd_sent || Adapter->CurCmd ||
                 list_empty(&Adapter->CmdPendingQ))
            )
            ) {
            PRINTM(INFO, "main-thread sleeping... "
                   "HS_Act=%s WakeupReq=%s Conn=%s PS_Mode=%d PS_State=%d\n",
                   (Adapter->HS_Activated) ? "Y" : "N",
                   (Adapter->bWakeupDevRequired) ? "Y" : "N",
                   (Adapter->MediaConnectStatus) ? "Y" : "N",
                   Adapter->PSMode, Adapter->PSState);

            TX_RESTORE;
            Adapter->main_thread_loops = 0;
            schedule();
            PRINTM(INFO, "main-thread waking up: IntCnt=%d "
                   "CurCmd=%s CmdPending=%s\n"
                   "                       Connect=%s "
                   "cmd_sent=%s data_sent=%s\n",
                   Adapter->IntCounter,
                   (Adapter->CurCmd) ? "Y" : "N",
                   list_empty(&Adapter->CmdPendingQ) ? "N" : "Y",
                   (Adapter->MediaConnectStatus) ? "Y" : "N",
                   (priv->wlan_dev.cmd_sent) ? "TRUE" : "FALSE",
                   (priv->wlan_dev.data_sent) ? "TRUE" : "FALSE");
        } else {
            TX_RESTORE;
        }

        OS_SET_THREAD_STATE(TASK_RUNNING);
        remove_wait_queue(&thread->waitQ, &wait);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
        if ((thread->state == WLAN_THREAD_STOPPED) || Adapter->SurpriseRemoved)
#else
        if (kthread_should_stop() || Adapter->SurpriseRemoved)
#endif
        {
            PRINTM(INFO, "main-thread: break from main thread: "
                   "SurpriseRemoved=0x%x\n", Adapter->SurpriseRemoved);
            break;
        }

        if (Adapter->IntCounter) {
            OS_INT_DISABLE(priv, driver_flags);
            Adapter->IntCounter = 0;
            OS_INT_RESTORE(priv, driver_flags);

            if (sbi_get_int_status(priv, &ireg)) {
                PRINTM(ERROR,
                       "main-thread: reading HOST_INT_STATUS_REG failed\n");
                continue;
            }

            /* If we have a valid interrupt then we have something to do, so
               we are not running in a dead loop */
            if (ireg)
                Adapter->main_thread_loops = 0;

            OS_INT_DISABLE(priv, driver_flags);
            Adapter->HisRegCpy |= ireg;
            OS_INT_RESTORE(priv, driver_flags);
            PRINTM(INTR, "INT: status = 0x%x\n", Adapter->HisRegCpy);
        } else if (Adapter->bWakeupDevRequired) {
            Adapter->WakeupTries++;
            PRINTM(CMND,
                   "Wakeup device... WakeupReq=%s Conn=%s PS_Mode=%d PS_State=%d @ %lu\n",
                   (Adapter->bWakeupDevRequired) ? "Y" : "N",
                   (priv->adapter->MediaConnectStatus) ? "Y" : "N",
                   priv->adapter->PSMode, priv->adapter->PSState,
                   os_time_get());

            /* Wake up device */
            if (sbi_exit_deep_sleep(priv))
                PRINTM(MSG, "main-thread: wakeup device failed\n");
            continue;
        }

        /* Command response? */
        if (Adapter->HisRegCpy & HIS_CmdUpLdRdy) {
            OS_INT_DISABLE(priv, driver_flags);
            Adapter->HisRegCpy &= ~HIS_CmdUpLdRdy;
            OS_INT_RESTORE(priv, driver_flags);

            wlan_process_cmdresp(priv);
        }

        /* Any Card Event */
        if (Adapter->HisRegCpy & HIS_CardEvent) {
            OS_INT_DISABLE(priv, driver_flags);
            Adapter->HisRegCpy &= ~HIS_CardEvent;
            OS_INT_RESTORE(priv, driver_flags);

            wlan_process_event(priv);
        }

        /* Check if we need to confirm Sleep Request received previously */
        if (Adapter->PSState == PS_STATE_PRE_SLEEP) {
            if (!priv->wlan_dev.cmd_sent && !Adapter->CurCmd) {
                ASSERT(Adapter->MediaConnectStatus == WlanMediaStateConnected);
                wlan_ps_cond_check(priv, (u16) Adapter->PSMode);
            }
        }

        /* The PS state is changed during processing of Sleep Request event
           above */
        if ((Adapter->PSState == PS_STATE_SLEEP)
            || (Adapter->PSState == PS_STATE_PRE_SLEEP))
            continue;

        if (Adapter->IsDeepSleep)
            continue;

        /* The HS_Activated flag is changed during processing of HS_Activate
           command resp */
        /* We cannot send command or data if HS_Activated and
           WakeupDevRequired are TRUE */
        if (Adapter->HS_Activated && Adapter->bWakeupDevRequired) {
            PRINTM(INFO, "main-thread: cannot send command or data, "
                   "HS_Activated=%d\n", Adapter->HS_Activated);
            continue;
        }

        /* Execute the next command */
        if (!priv->wlan_dev.cmd_sent && !Adapter->CurCmd)
            wlan_exec_next_cmd(priv);

        if (!priv->wlan_dev.data_sent
            && !wmm_lists_empty(priv) && !wmm_is_queue_stopped(priv)) {
            if ((Adapter->PSState == PS_STATE_FULL_POWER)
                || (Adapter->sleep_period.period == 0)
                || (Adapter->TxLockFlag == FALSE))
                wmm_process_tx(priv);

        }

    }

    wlan_deactivate_thread(thread);

    LEAVE();
    return WLAN_STATUS_SUCCESS;
}
/**
 *  @brief The main process
 *
 *  @param pmlan_adapter	A pointer to mlan_adapter structure
 *
 *  @return			MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status
mlan_main_process(IN t_void * pmlan_adapter)
{
    mlan_status ret = MLAN_STATUS_SUCCESS;
    mlan_adapter *pmadapter = (mlan_adapter *) pmlan_adapter;
    pmlan_callbacks pcb;

    ENTER();

    MASSERT(pmlan_adapter);

    pcb = &pmadapter->callbacks;

    pcb->moal_spin_lock(pmadapter->pmoal_handle,
                        pmadapter->pmain_proc_lock);

    /* Check if already processing */
    if (pmadapter->mlan_processing) {
        pmadapter->more_task_flag = MTRUE;
        pcb->moal_spin_unlock(pmadapter->pmoal_handle,
                              pmadapter->pmain_proc_lock);
        goto exit_main_proc;
    } else {
        pmadapter->mlan_processing = MTRUE;
        pcb->moal_spin_unlock(pmadapter->pmoal_handle,
                              pmadapter->pmain_proc_lock);
    }
process_start:
    do {
        pcb->moal_spin_lock(pmadapter->pmoal_handle,
                            pmadapter->pmain_proc_lock);
        pmadapter->more_task_flag = MFALSE;
        pcb->moal_spin_unlock(pmadapter->pmoal_handle,
                              pmadapter->pmain_proc_lock);
        /* Is MLAN shutting down or not ready? */
        if ((pmadapter->hw_status == WlanHardwareStatusClosing) ||
                (pmadapter->hw_status == WlanHardwareStatusNotReady))
            break;
        if (util_scalar_read
                (pmadapter->pmoal_handle, &pmadapter->rx_pkts_queued,
                 pmadapter->callbacks.moal_spin_lock,
                 pmadapter->callbacks.moal_spin_unlock) > HIGH_RX_PENDING) {
            PRINTM(MEVENT, "Pause\n");
            pmadapter->delay_task_flag = MTRUE;
            break;
        }
        /* Handle pending SDIO interrupts if any */
        if (pmadapter->sdio_ireg) {
            if (pmadapter->hs_activated == MTRUE)
                wlan_process_hs_config(pmadapter);
            wlan_process_int_status(pmadapter);
            if (pmadapter->data_received && pmadapter->rx_work_flag)
                wlan_recv_event(wlan_get_priv
                                (pmadapter, MLAN_BSS_ROLE_ANY),
                                MLAN_EVENT_ID_DRV_DEFER_RX_WORK,
                                MNULL);
        }
        /* Need to wake up the card ? */
        if ((pmadapter->ps_state == PS_STATE_SLEEP) &&
                (pmadapter->pm_wakeup_card_req &&
                 !pmadapter->pm_wakeup_fw_try) &&
                (util_peek_list
                 (pmadapter->pmoal_handle, &pmadapter->cmd_pending_q,
                  pcb->moal_spin_lock, pcb->moal_spin_unlock)
                 || !wlan_bypass_tx_list_empty(pmadapter)
                 || !wlan_wmm_lists_empty(pmadapter)
                )) {
            wlan_pm_wakeup_card(pmadapter);
            pmadapter->pm_wakeup_fw_try = MTRUE;
            continue;
        }
        if (IS_CARD_RX_RCVD(pmadapter)) {
            pmadapter->data_received = MFALSE;
            if (pmadapter->hs_activated == MTRUE) {
                pmadapter->is_hs_configured = MFALSE;
                wlan_host_sleep_activated_event(wlan_get_priv
                                                (pmadapter,
                                                 MLAN_BSS_ROLE_ANY),
                                                MFALSE);
            }
            pmadapter->pm_wakeup_fw_try = MFALSE;
            if (pmadapter->ps_state == PS_STATE_SLEEP)
                pmadapter->ps_state = PS_STATE_AWAKE;
        } else {
            /* We have tried to wakeup the card already */
            if (pmadapter->pm_wakeup_fw_try)
                break;
            if (pmadapter->ps_state != PS_STATE_AWAKE ||
                    (pmadapter->tx_lock_flag == MTRUE))
                break;

            if (pmadapter->scan_processing
                    || pmadapter->data_sent
                    || (wlan_bypass_tx_list_empty(pmadapter) &&
                        wlan_wmm_lists_empty(pmadapter))
                    || wlan_11h_radar_detected_tx_blocked(pmadapter)
               ) {
                if (pmadapter->cmd_sent || pmadapter->curr_cmd
                        ||
                        (!util_peek_list
                         (pmadapter->pmoal_handle,
                          &pmadapter->cmd_pending_q,
                          pcb->moal_spin_lock,
                          pcb->moal_spin_unlock))) {
                    break;
                }
            }
        }

        /* Check for Cmd Resp */
        if (pmadapter->cmd_resp_received) {
            pmadapter->cmd_resp_received = MFALSE;
            wlan_process_cmdresp(pmadapter);

            /* call moal back when init_fw is done */
            if (pmadapter->hw_status == WlanHardwareStatusInitdone) {
                pmadapter->hw_status = WlanHardwareStatusReady;
                wlan_init_fw_complete(pmadapter);
            }
        }

        /* Check for event */
        if (pmadapter->event_received) {
            pmadapter->event_received = MFALSE;
            wlan_process_event(pmadapter);
        }

        /* Check if we need to confirm Sleep Request received
           previously */
        if (pmadapter->ps_state == PS_STATE_PRE_SLEEP) {
            if (!pmadapter->cmd_sent && !pmadapter->curr_cmd) {
                wlan_check_ps_cond(pmadapter);
            }
        }

        /*
         * The ps_state may have been changed during processing of
         * Sleep Request event.
         */
        if ((pmadapter->ps_state == PS_STATE_SLEEP)
                || (pmadapter->ps_state == PS_STATE_PRE_SLEEP)
                || (pmadapter->ps_state == PS_STATE_SLEEP_CFM)
                || (pmadapter->tx_lock_flag == MTRUE)
           )
            continue;

        if (!pmadapter->cmd_sent && !pmadapter->curr_cmd) {
            if (wlan_exec_next_cmd(pmadapter) ==
                    MLAN_STATUS_FAILURE) {
                ret = MLAN_STATUS_FAILURE;
                break;
            }
        }

        if (!pmadapter->scan_processing
                && !pmadapter->data_sent &&
                !wlan_11h_radar_detected_tx_blocked(pmadapter) &&
                !wlan_bypass_tx_list_empty(pmadapter)) {
            PRINTM(MINFO, "mlan_send_pkt(): deq(bybass_txq)\n");
            wlan_process_bypass_tx(pmadapter);
            if (pmadapter->hs_activated == MTRUE) {
                pmadapter->is_hs_configured = MFALSE;
                wlan_host_sleep_activated_event(wlan_get_priv
                                                (pmadapter,
                                                 MLAN_BSS_ROLE_ANY),
                                                MFALSE);
            }
        }

        if (!pmadapter->scan_processing
                && !pmadapter->data_sent && !wlan_wmm_lists_empty(pmadapter)
                && !wlan_11h_radar_detected_tx_blocked(pmadapter)
           ) {
            wlan_wmm_process_tx(pmadapter);
            if (pmadapter->hs_activated == MTRUE) {
                pmadapter->is_hs_configured = MFALSE;
                wlan_host_sleep_activated_event(wlan_get_priv
                                                (pmadapter,
                                                 MLAN_BSS_ROLE_ANY),
                                                MFALSE);
            }
        }

#ifdef STA_SUPPORT
        if (pmadapter->delay_null_pkt && !pmadapter->cmd_sent &&
                !pmadapter->curr_cmd && !IS_COMMAND_PENDING(pmadapter) &&
                wlan_bypass_tx_list_empty(pmadapter) &&
                wlan_wmm_lists_empty(pmadapter)) {
            if (wlan_send_null_packet
                    (wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA),
                     MRVDRV_TxPD_POWER_MGMT_NULL_PACKET |
                     MRVDRV_TxPD_POWER_MGMT_LAST_PACKET)
                    == MLAN_STATUS_SUCCESS) {
                pmadapter->delay_null_pkt = MFALSE;
            }
            break;
        }
#endif

    } while (MTRUE);

    pcb->moal_spin_lock(pmadapter->pmoal_handle,
                        pmadapter->pmain_proc_lock);
    if (pmadapter->more_task_flag == MTRUE) {
        pcb->moal_spin_unlock(pmadapter->pmoal_handle,
                              pmadapter->pmain_proc_lock);
        goto process_start;
    }
    pmadapter->mlan_processing = MFALSE;
    pcb->moal_spin_unlock(pmadapter->pmoal_handle,
                          pmadapter->pmain_proc_lock);

exit_main_proc:
    if (pmadapter->hw_status == WlanHardwareStatusClosing)
        mlan_shutdown_fw(pmadapter);
    LEAVE();
    return ret;
}