TWD/FW_Transfer/FwEvent.c
author shahar_levi@ti.com
Tue, 29 Jun 2010 12:34:26 +0100
changeset 0 10c42ec6c05f
permissions -rw-r--r--
version WiLink_Driver_6.1.1.0.8

/*
 * FwEvent.c
 *
 * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved.      
 * All rights reserved.      
 * 
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 or BSD License which accompanies
 * this distribution. The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html and the BSD License is as below.                                   
 *                                                                       
 * Redistribution and use in source and binary forms, with or without    
 * modification, are permitted provided that the following conditions    
 * are met:                                                              
 *                                                                       
 *  * Redistributions of source code must retain the above copyright     
 *    notice, this list of conditions and the following disclaimer.      
 *  * Redistributions in binary form must reproduce the above copyright  
 *    notice, this list of conditions and the following disclaimer in    
 *    the documentation and/or other materials provided with the         
 *    distribution.                                                      
 *  * Neither the name Texas Instruments nor the names of its            
 *    contributors may be used to endorse or promote products derived    
 *    from this software without specific prior written permission.      
 *                                                                       
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/** \file  FwEvent.c
 *  \brief Handle firmware events
 * 
 *   
 * \par Description
 *      Call the appropriate event handler.
 *
 *  \see FwEvent.h
 */

#define __FILE_ID__  FILE_ID_104
#include "tidef.h"
#include "report.h"
#include "context.h"
#include "osApi.h"
#include "TWDriver.h"
#include "TWDriverInternal.h"
#include "FwEvent.h" 
#include "txResult_api.h"
#include "CmdMBox_api.h"
#include "rxXfer_api.h" 
#include "txXfer_api.h" 
#include "txHwQueue_api.h"
#include "eventMbox_api.h"
#include "TwIf.h"
#ifdef TI_DBG
    #include "tracebuf_api.h"
#endif

#ifdef _VLCT_
extern int trigger_another_read;
#endif

#define USE_SDIO_24M_WORKAROUND
#define FW_STATUS_MEM_ADDRESS   0x40400


#define TXN_FW_EVENT_SET_MASK_ADDR(pFwEvent)    pFwEvent->tMaskTxn.tTxnStruct.uHwAddr = HINT_MASK;
#define TXN_FW_EVENT_SET_UNMASK_ADDR(pFwEvent)  pFwEvent->tUnMaskTxn.tTxnStruct.uHwAddr = HINT_MASK;
#define TXN_FW_EVENT_SET_STATUS_ADDR(pFwEvent)  pFwEvent->tFwStatusTxn.tTxnStruct.uHwAddr = ACX_REG_INTERRUPT_CLEAR;
#define TXN_FW_EVENT_SET_FW_MEM_ADDR(pFwEvent)  pFwEvent->tMemFwStatusTxn.tTxnStruct.uHwAddr = FW_STATUS_MEM_ADDRESS;
/********************* static function declerations *************************/

/*
 * \brief	Call FwEvent client's event handler
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * 
 * \sa fwEvent_ReadCompleteCb
 */
static void fwEvent_CallHandler (TI_HANDLE hFwEvent);


/*
 * \brief	Create the FwEvent module object
 * 
 * \param  hOs  - OS module object handle
 * \return Handle to the created object
 * 
 * \par Description
 * Calling this function creates a FwEvent object
 * 
 * \sa fwEvent_Destroy
 */
TI_HANDLE fwEvent_Create (TI_HANDLE hOs)
{
    TfwEvent *pFwEvent;

    pFwEvent = os_memoryAlloc (hOs, sizeof(TfwEvent),MemoryNormal);
    if (pFwEvent == NULL)
    {
        return NULL;
    }

    os_memoryZero (hOs, pFwEvent, sizeof(TfwEvent));

    /* Allocation of Data */
    pFwEvent->tMaskTxn.pData = os_memoryAlloc (hOs, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ,MemoryDMA);
    if (pFwEvent->tMaskTxn.pData == NULL) 
    {
        return NULL;
    }
    os_memoryZero (hOs, pFwEvent->tMaskTxn.pData, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ);
    pFwEvent->tMaskTxn.pData += WSPI_PAD_LEN_READ;

    /* Allocation of Data */
    pFwEvent->tUnMaskTxn.pData = os_memoryAlloc (hOs, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ,MemoryDMA);
    if (pFwEvent->tUnMaskTxn.pData == NULL) 
    {
        return NULL;
    }
    os_memoryZero (hOs, pFwEvent->tUnMaskTxn.pData, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ);
    pFwEvent->tUnMaskTxn.pData += WSPI_PAD_LEN_READ;

     /* Allocation of FW Status buffer */
    pFwEvent->tFwStatusTxn.pFwStatus = os_memoryAlloc (hOs, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ,MemoryDMA);
    if (pFwEvent->tFwStatusTxn.pFwStatus == NULL) 
    {
        return NULL;
    }
    os_memoryZero (hOs, pFwEvent->tFwStatusTxn.pFwStatus, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ);
    pFwEvent->tFwStatusTxn.pFwStatus += WSPI_PAD_LEN_READ;

     /* Allocation of FW Status buffer */
    pFwEvent->tMemFwStatusTxn.pFwStatus = os_memoryAlloc (hOs, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ,MemoryDMA);
    if (pFwEvent->tMemFwStatusTxn.pFwStatus == NULL) 
    {
        return NULL;
    }
    os_memoryZero (hOs, pFwEvent->tMemFwStatusTxn.pFwStatus, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ);
    pFwEvent->tMemFwStatusTxn.pFwStatus += WSPI_PAD_LEN_READ;


    pFwEvent->hOs = hOs;

    return (TI_HANDLE)pFwEvent;
}


/*
 * \brief	Destroys the FwEvent object
 * 
 * \param  hFwEvent  - The object to free
 * \return TI_OK
 * 
 * \par Description
 * Calling this function destroys a FwEvent object
 * 
 * \sa fwEvent_Create
 */
TI_STATUS fwEvent_Destroy (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    if (pFwEvent->tMaskTxn.pData)
    {
        os_memoryFree (pFwEvent->hOs, pFwEvent->tMaskTxn.pData - WSPI_PAD_LEN_READ, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ);
    }

    if (pFwEvent->tUnMaskTxn.pData)
    {
        os_memoryFree (pFwEvent->hOs, pFwEvent->tUnMaskTxn.pData - WSPI_PAD_LEN_READ, sizeof (TI_UINT32) + WSPI_PAD_LEN_READ);
    }

    if (pFwEvent->tFwStatusTxn.pFwStatus)
    {
        os_memoryFree (pFwEvent->hOs, pFwEvent->tFwStatusTxn.pFwStatus - WSPI_PAD_LEN_READ, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ);
    }

    if (pFwEvent->tMemFwStatusTxn.pFwStatus)
    {
        os_memoryFree (pFwEvent->hOs, pFwEvent->tMemFwStatusTxn.pFwStatus - WSPI_PAD_LEN_READ, sizeof (FwStatus_t) + WSPI_PAD_LEN_READ);
    }

    if (pFwEvent)
    {
        os_memoryFree (pFwEvent->hOs, pFwEvent, sizeof(TfwEvent));
    }

    return TI_OK;
}


/*
 * \brief	Config the FwEvent module object
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \param  hTWD  - Handle to TWD module
 * \return TI_OK
 * 
 * \par Description
 * From hTWD we extract : hOs, hReport, hTwIf, hContext,
 *      hHealthMonitor, hEventMbox, hCmdMbox, hRxXfer, 
 *      hTxHwQueue, hTxResult
 * In this function we also register the FwEvent to the context engine
 * 
 * \sa
 */
TI_STATUS fwEvent_Init (TI_HANDLE hFwEvent, TI_HANDLE hTWD)
{
    TfwEvent  *pFwEvent = (TfwEvent *)hFwEvent;
    TTwd      *pTWD = (TTwd *)hTWD;
    TTxnStruct* pTxn;
    
    pFwEvent->hTWD              = hTWD;
    pFwEvent->hOs               					= pTWD->hOs;
    pFwEvent->hReport           					= pTWD->hReport;
    pFwEvent->hContext          					= pTWD->hContext;
    pFwEvent->hTwIf             = pTWD->hTwIf;
    pFwEvent->hHealthMonitor    = pTWD->hHealthMonitor;
    pFwEvent->hEventMbox        = pTWD->hEventMbox;
    pFwEvent->hCmdMbox          = pTWD->hCmdMbox;
    pFwEvent->hRxXfer           = pTWD->hRxXfer;
    pFwEvent->hTxHwQueue        = pTWD->hTxHwQueue;
    pFwEvent->hTxXfer           = pTWD->hTxXfer;
    pFwEvent->hTxResult         = pTWD->hTxResult;

    pFwEvent->eFwEventState       = FW_EVENT_STATE_IDLE;
    pFwEvent->uEventMask          = 0;
    pFwEvent->uEventVector        = 0;
    pFwEvent->bFwNotificationFlag = TI_FALSE;

    pTxn = (TTxnStruct*)&pFwEvent->tMaskTxn.tTxnStruct;
    TXN_PARAM_SET(pTxn, TXN_HIGH_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
    BUILD_TTxnStruct(pTxn, HINT_MASK, pFwEvent->tMaskTxn.pData, REGISTER_SIZE, NULL, NULL)

    pTxn = (TTxnStruct*)&pFwEvent->tUnMaskTxn.tTxnStruct;
    TXN_PARAM_SET(pTxn, TXN_HIGH_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
    BUILD_TTxnStruct(pTxn, HINT_MASK, pFwEvent->tUnMaskTxn.pData, REGISTER_SIZE, NULL, NULL)

#ifdef USE_SDIO_24M_WORKAROUND /* FW_STATUS moved from registers to memory area */
    /* First txn is HINT_STT_CLR register */
    pTxn = (TTxnStruct*)&pFwEvent->tFwStatusTxn.tTxnStruct;
    TXN_PARAM_SET(pTxn, TXN_HIGH_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_INC_ADDR)
    BUILD_TTxnStruct(pTxn, ACX_REG_INTERRUPT_CLEAR, pFwEvent->tFwStatusTxn.pFwStatus, REGISTER_SIZE, NULL, NULL)
    /* FW status from memory area */
    pTxn = (TTxnStruct*)&pFwEvent->tMemFwStatusTxn.tTxnStruct;
    TXN_PARAM_SET(pTxn, TXN_HIGH_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_INC_ADDR)
    BUILD_TTxnStruct(pTxn, FW_STATUS_MEM_ADDRESS, pFwEvent->tMemFwStatusTxn.pFwStatus, sizeof(FwStatus_t)-REGISTER_SIZE, (TTxnDoneCb)fwEvent_ReadCompleteCb, hFwEvent)
#else
    pTxn = (TTxnStruct*)&pFwEvent->tFwStatusTxn.tTxnStruct;
    TXN_PARAM_SET(pTxn, TXN_HIGH_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_INC_ADDR)
    BUILD_TTxnStruct(pTxn, ACX_REG_INTERRUPT_CLEAR, pFwEvent->tFwStatusTxn.pFwStatus, sizeof(FwStatus_t), (TTxnDoneCb)fwEvent_ReadCompleteCb, hFwEvent)
#endif
    /* 
     *  Register the FwEvent to the context engine and get the client ID.
     *  The FwEvent() will be called from the context_DriverTask() after scheduled
     *    by a FW-Interrupt (see fwEvent_InterruptRequest()).
     */
    pFwEvent->uContextId = context_RegisterClient (pFwEvent->hContext,
                                                   fwEvent_Handle,
                                                   hFwEvent,
                                                   TI_FALSE,
                                                   "FW_EVENT",
                                                   sizeof("FW_EVENT"));
	
    return TI_OK;
}


/*
 * \brief	Call FwEvent client's event handler
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * 
 * \sa fwEvent_ReadCompleteCb
 */
static void fwEvent_CallHandler (TI_HANDLE hFwEvent)
{
    TfwEvent   *pFwEvent = (TfwEvent *)hFwEvent;

    if (pFwEvent->uEventVector & ACX_INTR_WATCHDOG)
    {
        /* Fw watchdog timeout has occured */
        TWD_WdExpireEvent (pFwEvent->hTWD);
    }

    if (pFwEvent->uEventVector & ACX_INTR_INIT_COMPLETE)
    {
        TRACE0(pFwEvent->hReport, REPORT_SEVERITY_INFORMATION, "fwEvent_CallHandler: INIT_COMPLETE\n");
    }
	/* Change to handle the command MBOX before the event MBOX to maintain order for WHA command response
	 * and follow command complete
	 */
    if (pFwEvent->uEventVector & ACX_INTR_CMD_COMPLETE)
    {
        /* Command Mbox completed */
        cmdMbox_CommandComplete(pFwEvent->hCmdMbox);
    }
    if (pFwEvent->uEventVector & ACX_INTR_EVENT_A)
    {
        eventMbox_Handle(pFwEvent->hEventMbox,(FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus));
    }
    if (pFwEvent->uEventVector & ACX_INTR_EVENT_B)
    {
        eventMbox_Handle(pFwEvent->hEventMbox,(FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus));
    }
    

    /* The DATA interrupt is shared by all data path events, so call all Tx and Rx clients */
    if (pFwEvent->uEventVector & ACX_INTR_DATA)
    {
        rxXfer_RxEvent (pFwEvent->hRxXfer, (FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus));

        txHwQueue_UpdateFreeResources (pFwEvent->hTxHwQueue, (FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus));

        txResult_TxCmpltIntrCb (pFwEvent->hTxResult, (FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus));
    } 

    /* After handling all raised bits, we can negate them */
    ((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->intrStatus &= pFwEvent->uEventMask;
}


/*
 * \brief	Requests the context engine to schedule the driver task
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * Called by the FW-Interrupt ISR.
 * Requests the context engine to schedule the driver task 
 * for handling the FW-Events (FwEvent callback).
 * 
 * \sa
 */
void fwEvent_InterruptRequest (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    /* Request switch to driver context for handling the FW-Interrupt event */
    context_RequestSchedule (pFwEvent->hContext, pFwEvent->uContextId);
}


/*
 * \brief	Handle the FW interrupts
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * Called from context module upon receiving FW interrupt
 * The function mask the interrupts and reads the FW status
 * 
 * \sa
 */

void fwEvent_Handle (TI_HANDLE hFwEvent)
{
    TfwEvent   *pFwEvent = (TfwEvent *)hFwEvent;
    ETxnStatus rc;

    /* NOTE: pFwEvent may be uninitialized at init stage */
    if (pFwEvent != NULL)
    {
        if (pFwEvent->eFwEventState != FW_EVENT_STATE_IDLE)
        {
	    os_InterruptServiced (pFwEvent->hOs);
            twIf_HwAvailable(pFwEvent->hTwIf);
            return;
        }

        pFwEvent->eFwEventState = FW_EVENT_STATE_READING;
        pFwEvent->bFwNotificationFlag = TI_TRUE;

        twIf_Awake(pFwEvent->hTwIf);
        twIf_HwAvailable(pFwEvent->hTwIf);

        /* Write HINT mask */
        *((TI_UINT32*)(pFwEvent->tMaskTxn.pData)) = ACX_INTR_ALL;
        TXN_FW_EVENT_SET_MASK_ADDR(pFwEvent)
        twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tMaskTxn.tTxnStruct));

#ifdef USE_SDIO_24M_WORKAROUND /* FW_STATUS moved from registers to memory area */
        /* 
         * Read first register (HINT_STT_CLR) only from registers area
         */
        TXN_FW_EVENT_SET_STATUS_ADDR(pFwEvent)
        twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tFwStatusTxn.tTxnStruct));

        /* 
         * Read other 16 registers value from memory area FW_STATUS_MEM_ADDRESS
         */
        TXN_FW_EVENT_SET_FW_MEM_ADDR(pFwEvent)
        rc = twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tMemFwStatusTxn.tTxnStruct));

        if (rc == TXN_STATUS_COMPLETE)
        {
            fwEvent_ReadCompleteCb(hFwEvent);
        }
#else

        /* Read the Fw status */
        TXN_FW_EVENT_SET_STATUS_ADDR(pFwEvent)
        rc = twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tFwStatusTxn.tTxnStruct));

        if (rc == TXN_STATUS_COMPLETE)
        {
            fwEvent_ReadCompleteCb(hFwEvent);
        }
#endif
    }
    else
    {
        os_InterruptServiced (pFwEvent->hOs);

    /* end of if */
    }
}


/*
 * \brief	Handle the Fw Status information 
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * This function is called from fwEvent_Handle on a sync read, or from TwIf as a CB on an async read.
 * It calls fwEvent_CallHandler to handle the triggered interrupts.
 * 
 * \sa fwEvent_Handle
 */
void fwEvent_ReadCompleteCb (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;
    
#ifdef USE_SDIO_24M_WORKAROUND /* FW_STATUS moved from registers to memory area */
    /* 
     * copy FW status 16 registers values from memory transaction read area to the original place 
*/
    os_memoryCopy (pFwEvent->hOs, &((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->counters, &((FwStatus_t*)(pFwEvent->tMemFwStatusTxn.pFwStatus))->counters, sizeof(FwStatus_t)-REGISTER_SIZE);
#endif
    
    os_InterruptServiced (pFwEvent->hOs);
	  
    /* If we were called because of an interrupt */
	if (pFwEvent->bFwNotificationFlag)
    {
        /* In case of level interrupt we need to clear the line */
        /*os_InterruptServiced(pFwEvent->hOs);*/

        /*
         * Sync to fw time so we can update the tx packets
         * on the delta time that they spent in the driver 
         */
        pFwEvent->uFwTimeOffset = (os_timeStampMs (pFwEvent->hOs) * 1000) - 
                                  ENDIAN_HANDLE_LONG (((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->fwLocalTime);

        pFwEvent->bFwNotificationFlag = TI_FALSE;
    }

    pFwEvent->uEventVector = ((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->intrStatus;

    /* 
     * Mask unwanted interrupts. 
     */
    pFwEvent->uEventVector &= pFwEvent->uEventMask;

    fwEvent_CallHandler(hFwEvent);

    /* Check if the state is changed in the context of the event callbacks */
    if (pFwEvent->eFwEventState == FW_EVENT_STATE_IDLE)
    {
        /*
         * When fwEvent_stop is called state is changed to IDLE
         * This is done in the context of the above events callbacks
         * Don't send the UNMASK transaction because the driver stop process includes power off
         */ 
        TRACE0(pFwEvent->hReport, REPORT_SEVERITY_WARNING, "fwEvent_ReadCompleteCb : State is IDLE ! don't send the UNMASK");
        return;
    }

    /* Write HINT unmask */
    *((TI_UINT32*)(pFwEvent->tUnMaskTxn.pData)) = ~pFwEvent->uEventMask;
    TXN_FW_EVENT_SET_UNMASK_ADDR(pFwEvent)
    twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tUnMaskTxn.tTxnStruct));

    twIf_Sleep(pFwEvent->hTwIf);
    pFwEvent->eFwEventState = FW_EVENT_STATE_IDLE;
} 


/*
 * \brief	Translate host to FW time (Usec)
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \param  uHostTime - The host time in MS to translate
 *
 * \return FW Time in Usec
 * 
 * \par Description
 * 
 * \sa
 */
TI_UINT32 fwEvent_TranslateToFwTime (TI_HANDLE hFwEvent, TI_UINT32 uHostTime)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    return ((uHostTime * 1000) - pFwEvent->uFwTimeOffset);
}


/*
 * \brief	Unmask only cmd-cmplt and events interrupts (needed for init phase)
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return Event mask
 * 
 * \par Description
 * Unmask only cmd-cmplt and events interrupts (needed for init phase)
 *                  and return interrupt enabled bit mask.
 * 
 * \sa
 */
TI_UINT32 fwEvent_GetInitMask (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    /* Unmask only the interrupts needed for the FW configuration process. */
    pFwEvent->uEventMask = ACX_INTR_CMD_COMPLETE | ACX_INTR_EVENT_A | ACX_INTR_EVENT_B;

    return pFwEvent->uEventMask;
}


/*
 * \brief	Stop & reset FwEvent (called by the driver stop process)
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return TI_OK
 * 
 * \par Description
 *
 * \sa
 */
TI_STATUS fwEvent_Stop (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    pFwEvent->eFwEventState = FW_EVENT_STATE_IDLE;
    ((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->intrStatus = 0;
    pFwEvent->uEventMask = 0;
    pFwEvent->bFwNotificationFlag = TI_FALSE;
    pFwEvent->uEventVector = 0;    
    
    return TI_OK;
}


/*
 * \brief	Unmask all interrupts, set Rx interrupt bit and call FwEvent_Handle
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * Called when driver Start or recovery process is completed.
 *              Unmask all interrupts, set Rx interrupt bit and call FwEvent_Handle 
 *                  (in case we missed an Rx interrupt in a recovery process).
 * 
 * \sa
 */
void fwEvent_EnableExternalEvents (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;

    /* Unmask all interrupts */
    pFwEvent->uEventMask    = ALL_EVENTS_VECTOR;

    /* Set Rx interrupt bit to invoke it (in case we missed it in a recovery/start process) */
    ((FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus))->intrStatus |= ACX_INTR_DATA;

    /* If in IDLE state, handle interrupts including the Rx we've just set manually */
    if (pFwEvent->eFwEventState == FW_EVENT_STATE_IDLE)
    {        
        fwEvent_GetFwStatus (hFwEvent);
    }
}


/*
 * \brief	Disable the FwEvent client of the context thread handler
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 *
 * \sa
 */
void fwEvent_DisableInterrupts(TI_HANDLE hFwEvent)
{
    TfwEvent  *pFwEvent = (TfwEvent *)hFwEvent;

    context_DisableClient (pFwEvent->hContext,pFwEvent->uContextId);
	
}


/*
 * \brief	Enable the FwEvent client of the context thread handler
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 *
 * \sa
 */
void fwEvent_EnableInterrupts(TI_HANDLE hFwEvent)
{
    TfwEvent  *pFwEvent = (TfwEvent *)hFwEvent;

    context_EnableClient (pFwEvent->hContext,pFwEvent->uContextId);
	
}


/*
 * \brief	Issue a FW status read (Not in response to any FW interrupt)
 * 
 * \param  hFwEvent  - FwEvent Driver handle
 * \return void
 * 
 * \par Description
 * Issue a FW status read (Not in response to any FW interrupt)
 * Called also from fwEvent_EnableExternalEvents after recovery.
 * 
 * \sa fwEvent_EnableExternalEvents
 */
void fwEvent_GetFwStatus (TI_HANDLE hFwEvent)
{
    TfwEvent   *pFwEvent = (TfwEvent *)hFwEvent;
    ETxnStatus rc;

    /* NOTE: pFwEvent may be uninitialized at init stage */
    if (pFwEvent != NULL)
    {
        if (pFwEvent->eFwEventState != FW_EVENT_STATE_IDLE)
        {
            twIf_HwAvailable(pFwEvent->hTwIf);
            return;
        }

        pFwEvent->eFwEventState = FW_EVENT_STATE_READING;
        
        twIf_Awake(pFwEvent->hTwIf);

        /* Write HINT mask */
        *((TI_UINT32*)(pFwEvent->tMaskTxn.pData)) = ACX_INTR_ALL;
        TXN_FW_EVENT_SET_MASK_ADDR(pFwEvent)
        twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tMaskTxn.tTxnStruct));

#ifdef USE_SDIO_24M_WORKAROUND /* FW_STATUS moved from registers to memory area */
        /* 
         * Read first register (HINT_STT_CLR) only from registers area
         */
        TXN_FW_EVENT_SET_STATUS_ADDR(pFwEvent)
        twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tFwStatusTxn.tTxnStruct));

        /* 
         * Read other 16 registers value from memory area 0x40200
         */
        TXN_FW_EVENT_SET_FW_MEM_ADDR(pFwEvent)
        rc = twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tMemFwStatusTxn.tTxnStruct));

        if (rc == TXN_STATUS_COMPLETE)
        {
            fwEvent_ReadCompleteCb(hFwEvent);
        }
#else
        /* Read the Fw status */
        TXN_FW_EVENT_SET_STATUS_ADDR(pFwEvent)
        rc = twIf_Transact(pFwEvent->hTwIf, &(pFwEvent->tFwStatusTxn.tTxnStruct));

        if (rc == TXN_STATUS_COMPLETE)
        {
            fwEvent_ReadCompleteCb(hFwEvent);
        }
#endif
    /* end of if */
    }
}


#ifdef TI_DBG

void fwEvent_PrintStat (TI_HANDLE hFwEvent)
{
    TfwEvent *pFwEvent = (TfwEvent *)hFwEvent;
    FwStatus_t *fwStat = (FwStatus_t*)(pFwEvent->tFwStatusTxn.pFwStatus); 
	int i;

    WLAN_OS_REPORT(("Print FW event module info\n"));
    WLAN_OS_REPORT(("==========================\n"));
    WLAN_OS_REPORT(("intrStatus = 0x%08x\n", fwStat->intrStatus));
    WLAN_OS_REPORT(("counters   = 0x%08x\n", fwStat->counters));
	for (i = 0; i < NUM_RX_PKT_DESC; i++)
    {
		WLAN_OS_REPORT(("rxPktsDesc[%1d] = 0x%08x\n", i, fwStat->rxPktsDesc[i]));
    }
	for (i = 0; i < NUM_TX_QUEUES; i++)
    {
		WLAN_OS_REPORT(("txReleasedBlks[%1d] = 0x%08x\n", i, fwStat->txReleasedBlks[i]));
    }
    WLAN_OS_REPORT(("fwLocalTime = 0x%08x\n", fwStat->fwLocalTime));
}

#endif  /* TI_DBG */