C++程序  |  1065行  |  38.87 KB

/*
 * TwIf.c
 *
 * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.      
 * All rights reserved.                                                  
 *                                                                       
 * 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   TwIf.c 
 *  \brief  The TWD bottom API towards the Txn-Queue. 
 *
 * The TwIf module is the lowest WLAN-specific layer and presents a common interface to all Xfer modules.
 * As such, it is responsible for the common functionalities related to device access, which includes:
 *    - transactions submission
 *    - interface power control
 *    - address translation (paging) when needed (depends on bus attributes).
 * The TwIf has no OS, platform or bus type dependencies.
 * 
 *  \see    TwIf.h, TxnQueue.c, TxnQueue.h
 */

#define __FILE_ID__  FILE_ID_121
#include "tidef.h"
#include "report.h"
#include "context.h"
#include "TxnDefs.h"
#include "TxnQueue.h"
#include "TwIf.h"
#include "TWDriver.h"
 

/************************************************************************
 * Defines
 ************************************************************************/
#define TXN_DONE_QUE_SIZE       64  /* TxnDone-queue size */

/* Values to write to the ELP register for sleep/awake */
#define ELP_CTRL_REG_SLEEP      0
#define ELP_CTRL_REG_AWAKE      1

/* 
 * Device interface-control registers addresses (at the end ot the 17-bit address space):
 */
#define PARTITION_REGISTERS_ADDR        (0x1FFC0)   /* Four 32 bit register:                      */
                                                    /*    Memory region size            (0x1FFC0) */
                                                    /*    Memory region base address    (0x1FFC4) */
                                                    /*    Registers region size         (0x1FFC8) */
                                                    /*    Registers region base address (0x1FFCC) */

#define ELP_CTRL_REG_ADDR		        (0x1FFFC)   /* ELP control register address */



/************************************************************************
 * Types
 ************************************************************************/

/* TwIf SM States */
typedef enum
{
	SM_STATE_AWAKE,           /* HW is awake and Txn-Queue is running */
	SM_STATE_SLEEP,           /* HW is asleep and Txn-Queue is stopped */
	SM_STATE_WAIT_HW          /* Waiting for HW to wake up (after triggering it), Txn-Queue is stopped */
} ESmState;

/* TwIf SM Events */
typedef enum
{
	SM_EVENT_START,           /* Need to wake up the device to handle transactions */
	SM_EVENT_HW_AVAILABLE,    /* The device woke up */
	SM_EVENT_SLEEP            /* Need to let the device go to sleep */
} ESmEvent;

/* The addresses partitioning configuration Txn data */
typedef struct 
{
    TI_UINT32       uMemSize;        /* The HW memory region size. */
    TI_UINT32       uMemAddr;        /* The HW memory region address. */
    TI_UINT32       uRegSize;        /* The HW registers region size. */
    TI_UINT32       uRegAddr;        /* The HW registers region address. */

} TPartitionTxnData;

/* The addresses partitioning configuration Txn */
typedef struct 
{
    TTxnStruct          tHdr;        /* The generic transaction structure */
    TPartitionTxnData   tData;       /* The addresses partitioning configuration data */

} TPartitionTxn;

/* The addresses partitioning configuration Txn */
typedef struct 
{
    TTxnStruct      tHdr;           /* The generic transaction structure */
    TI_UINT32   tData;       /* The addresses partitioning configuration data for one register */

} TPartitionRegTxn;

/* The addresses partitioning configuration Txn */
typedef struct 
{
    TTxnStruct      tHdr;           /* The generic transaction structure */
    TI_UINT8        uElpData;       /* The value to write to the ELP register */

} TElpTxn;

/* The TwIf module Object */
typedef struct _TTwIfObj
{
    /* Other modules handles */
    TI_HANDLE	    hOs;		   	 
    TI_HANDLE	    hReport;
	TI_HANDLE       hContext;
	TI_HANDLE	    hTxnQ;

    ESmState        eState;          /* SM current state */
    TI_HANDLE       hTxnDoneQueue;   /* Queue for completed transactions not reported yet to the upper layer */
    TI_UINT32       uContextId;      /* The ID allocated to this module on registration to context module */
    TFailureEventCb fErrCb;          /* The upper layer CB function for error handling */
    TI_HANDLE       hErrCb;          /* The CB function handle */
    TRecoveryCb     fRecoveryCb;     /* The upper layer CB for restart complete */
    TI_HANDLE       hRecoveryCb;     /* The CB function handle */
    TI_UINT32       uAwakeReqCount;  /* Increment on awake requests and decrement on sleep requests */
    TI_UINT32       uPendingTxnCount;/* Count pending transactions (sent to TxnQ and not completed yet) */
    TElpTxn         tElpTxnSleep;    /* Transaction structure for writing sleep to ELP register  */
    TElpTxn         tElpTxnAwake;    /* Transaction structure for writing awake to ELP register  */

    /* HW Addresses partitioning */
    TI_UINT32       uMemAddr1;        /* The HW memory region start address. */
    TI_UINT32       uMemSize1;        /* The HW memory region end address. */
    TI_UINT32       uMemAddr2;        /* The HW registers region start address. */
    TI_UINT32       uMemSize2;        /* The HW registers region end address. */
    TI_UINT32       uMemAddr3;        /* The INT Status registers region start address. */
    TI_UINT32       uMemSize3;        /* The INT Status registers region end address. */
    TI_UINT32       uMemAddr4;        /* The FW Status mem registers region start address. */
    

#ifdef TI_DBG
    /* Debug counters */
    TI_UINT32       uDbgCountAwake;      /* Count calls to twIf_Awake */
    TI_UINT32       uDbgCountSleep;      /* Count calls to twIf_Sleep */
    TI_UINT32       uDbgCountTxn;        /* Count calls to twIf_SendTransaction (including TwIf internal Txns) */
    TI_UINT32       uDbgCountTxnPending; /* Count transactions that returned PENDING */
    TI_UINT32       uDbgCountTxnComplete;/* Count transactions that returned COMPLETE */
    TI_UINT32       uDbgCountTxnDoneCb;  /* Count calls to twIf_TxnDoneCb */
#endif
   
    TI_BOOL         bTxnDoneInRecovery;      
} TTwIfObj;


/************************************************************************
 * Internal functions prototypes
 ************************************************************************/
static void        twIf_WriteElpReg        (TTwIfObj *pTwIf, TI_UINT32 uValue);
static void        twIf_PartitionTxnDoneCb (TI_HANDLE hTwIf, void *hTxn);
static ETxnStatus  twIf_SendTransaction    (TTwIfObj *pTwIf, TTxnStruct *pTxn);
static void        twIf_HandleSmEvent      (TTwIfObj *pTwIf, ESmEvent eEvent);
static void        twIf_TxnDoneCb          (TI_HANDLE hTwIf, TTxnStruct *pTxn);
static void        twIf_HandleTxnDone      (TI_HANDLE hTwIf);
static void        twIf_ClearTxnDoneQueue  (TI_HANDLE hTwIf);

/************************************************************************
 *
 *   Module functions implementation
 *
 ************************************************************************/

/** 
 * \fn     twIf_Create 
 * \brief  Create the module
 * 
 * Allocate and clear the module's object.
 * 
 * \note   
 * \param  hOs - Handle to Os Abstraction Layer
 * \return Handle of the allocated object, NULL if allocation failed 
 * \sa     twIf_Destroy
 */ 
TI_HANDLE twIf_Create (TI_HANDLE hOs)
{
    TI_HANDLE  hTwIf;
    TTwIfObj  *pTwIf;

    hTwIf = os_memoryAlloc (hOs, sizeof(TTwIfObj));
    if (hTwIf == NULL)
        return NULL;
    
    pTwIf = (TTwIfObj *)hTwIf;

    os_memoryZero (hOs, hTwIf, sizeof(TTwIfObj));
    
    pTwIf->hOs = hOs;

    return pTwIf;
}


/** 
 * \fn     twIf_Destroy
 * \brief  Destroy the module. 
 * 
 * Unregister from TxnQ and free the TxnDone-queue and the module's object.
 * 
 * \note   
 * \param  The module's object
 * \return TI_OK on success or TI_NOK on failure 
 * \sa     twIf_Create
 */ 
TI_STATUS twIf_Destroy (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*)hTwIf;

    if (pTwIf)
    {
        txnQ_Close (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN);
        que_Destroy (pTwIf->hTxnDoneQueue);
        os_memoryFree (pTwIf->hOs, pTwIf, sizeof(TTwIfObj));     
    }
    return TI_OK;
}


/** 
 * \fn     twIf_Init 
 * \brief  Init module 
 * 
 * - Init required handles and module variables
 * - Create the TxnDone-queue
 * - Register to TxnQ
 * - Register to context module
 * 
 * \note    
 * \param  hTwIf     - The module's object
 * \param  hReport   - Handle to report module
 * \param  hContext  - Handle to context module
 * \param  hTxnQ     - Handle to TxnQ module
 * \return void        
 * \sa     
 */ 
void twIf_Init (TI_HANDLE hTwIf, TI_HANDLE hReport, TI_HANDLE hContext, TI_HANDLE hTxnQ, TRecoveryCb fRecoveryCb, TI_HANDLE hRecoveryCb)
{
    TTwIfObj   *pTwIf = (TTwIfObj*)hTwIf;
    TI_UINT32   uNodeHeaderOffset;
    TTxnStruct *pTxnHdr;   /* The ELP transactions header (as used in the TxnQ API) */

    pTwIf->hReport          = hReport;
    pTwIf->hContext         = hContext;
    pTwIf->hTxnQ            = hTxnQ;
    pTwIf->fRecoveryCb      = fRecoveryCb;
    pTwIf->hRecoveryCb      = hRecoveryCb;

    /* Prepare ELP sleep transaction */
    pTwIf->tElpTxnSleep.uElpData = ELP_CTRL_REG_SLEEP;
    pTxnHdr = &(pTwIf->tElpTxnSleep.tHdr);
    TXN_PARAM_SET(pTxnHdr, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
    TXN_PARAM_SET_MORE(pTxnHdr, 0);         /* Sleep is the last transaction! */
    /* NOTE: Function id for single step will be replaced to 0 by the bus driver */
    TXN_PARAM_SET_SINGLE_STEP(pTxnHdr, 1);  /* ELP write is always single step (TxnQ is topped)! */
    BUILD_TTxnStruct(pTxnHdr, ELP_CTRL_REG_ADDR, &(pTwIf->tElpTxnSleep.uElpData), sizeof(TI_UINT8), NULL, NULL)

    /* Prepare ELP awake transaction */
    pTwIf->tElpTxnAwake.uElpData = ELP_CTRL_REG_AWAKE;
    pTxnHdr = &(pTwIf->tElpTxnAwake.tHdr);
    TXN_PARAM_SET(pTxnHdr, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
    TXN_PARAM_SET_MORE(pTxnHdr, 1);         
    /* NOTE: Function id for single step will be replaced to 0 by the bus driver */
    TXN_PARAM_SET_SINGLE_STEP(pTxnHdr, 1);  /* ELP write is always single step (TxnQ is topped)! */
    BUILD_TTxnStruct(pTxnHdr, ELP_CTRL_REG_ADDR, &(pTwIf->tElpTxnAwake.uElpData), sizeof(TI_UINT8), NULL, NULL)

    /* Create the TxnDone queue. */
    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 
    pTwIf->hTxnDoneQueue = que_Create (pTwIf->hOs, pTwIf->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset);
    if (pTwIf->hTxnDoneQueue == NULL)
    {
        TRACE0(pTwIf->hReport, REPORT_SEVERITY_ERROR, "twIf_Init: TxnDone queue creation failed!\n");
    }

    /* Register to the context engine and get the client ID */
    pTwIf->uContextId = context_RegisterClient (pTwIf->hContext,
                                                twIf_HandleTxnDone,
                                                hTwIf,
                                                TI_TRUE,
                                                "TWIF",
                                                sizeof("TWIF"));

    /* Register to TxnQ */
    txnQ_Open (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN, TXN_NUM_PRIORITYS, (TTxnQueueDoneCb)twIf_TxnDoneCb, hTwIf);

    /* Restart TwIf and TxnQ modules */
    twIf_Restart (hTwIf);
}


/** 
 * \fn     twIf_Restart
 * \brief  Restart module upon driver stop or recovery
 * 
 * Called upon driver stop command or upon recovery. 
 * Calls txnQ_Restart to clear the WLAN queues and call the TxnDone CB on each tansaction.
 * If no transaction in progress, the queues are cleared immediately. 
 * If a transaction is in progress, it is done upon TxnDone.
 * The status in transactions that were dropped due to restart is TXN_STATUS_RECOVERY,
 *     and its originator (Xfer module) handles it if required (if its CB was written in the Txn).
 * 
 * \note   
 * \param  hTwIf - The module's object
 * \return COMPLETE if the WLAN queues were restarted, PENDING if waiting for TxnDone to restart queues
 * \sa     
 */ 
ETxnStatus twIf_Restart (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    pTwIf->eState           = SM_STATE_SLEEP;
    pTwIf->uAwakeReqCount   = 0;

    pTwIf->uPendingTxnCount = 0;

    /* Clear done queue */
    twIf_ClearTxnDoneQueue(hTwIf);

    /* Restart WLAN queues and return result (COMPLETE or PENDINF if completed in TxnDone context) */
    return txnQ_Restart (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN);
}


/** 
 * \fn     twIf_RegisterErrCb
 * \brief  Register Error CB
 * 
 * Register upper layer (health monitor) CB for bus error
 * 
 * \note   
 * \param  hTwIf  - The module's object
 * \param  fErrCb - The upper layer CB function for error handling 
 * \param  hErrCb - The CB function handle
 * \return void
 * \sa     
 */ 
void twIf_RegisterErrCb (TI_HANDLE hTwIf, void *fErrCb, TI_HANDLE hErrCb)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    /* Save upper layer (health monitor) CB for bus error */
    pTwIf->fErrCb = (TFailureEventCb)fErrCb;
    pTwIf->hErrCb = hErrCb;
}


/** 
 * \fn     twIf_WriteElpReg
 * \brief  write ELP register
 * 
 * \note   
 * \param  pTwIf   - The module's object
 * \param  uValue  - ELP_CTRL_REG_SLEEP or ELP_CTRL_REG_AWAKE
 * \return void
 * \sa     
 */ 
static void twIf_WriteElpReg (TTwIfObj *pTwIf, TI_UINT32 uValue)
{
    TRACE1(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_WriteElpReg:  ELP Txn data = 0x%x\n", uValue);
    /* Send ELP (awake or sleep) transaction to TxnQ */
    if (uValue == ELP_CTRL_REG_AWAKE)
    {
        txnQ_Transact (pTwIf->hTxnQ, &(pTwIf->tElpTxnAwake.tHdr));
    }
    else
    {
        txnQ_Transact (pTwIf->hTxnQ, &(pTwIf->tElpTxnSleep.tHdr));
    }
}


/** 
 * \fn     twIf_SetPartition
 * \brief  Set HW addresses partition
 * 
 * Called by the HwInit module to set the HW address ranges for download or working access.
 * Generate and configure the bus access address mapping table.
 * The partition is split between register (fixed partition of 24KB size, exists in all modes), 
 *     and memory (dynamically changed during init and gets constant value in run-time, 104KB size).
 * The TwIf configures the memory mapping table on the device by issuing write transaction to 
 *     table address (note that the TxnQ and bus driver see this as a regular transaction). 
 * 
 * \note In future versions, a specific bus may not support partitioning (as in wUART), 
 *       In this case the HwInit module shall not call this function (will learn the bus 
 *       configuration from the INI file).
 *
 * \param  hTwIf          - The module's object
 * \param  uMemAddr  - The memory partition base address
 * \param  uMemSize  - The memory partition size
 * \param  uRegAddr  - The registers partition base address
 * \param  uRegSize  - The register partition size
 * \return void
 * \sa     
 */ 

void twIf_SetPartition (TI_HANDLE hTwIf,
                        TPartition *pPartition)
{
    TTwIfObj          *pTwIf = (TTwIfObj*) hTwIf;
    TPartitionRegTxn  *pPartitionRegTxn;/* The partition transaction structure for one register */
    TTxnStruct        *pTxnHdr;         /* The partition transaction header (as used in the TxnQ API) */
    ETxnStatus         eStatus;
    int i;

    /* Save partition information for translation and validation. */
    pTwIf->uMemAddr1 = pPartition[0].uMemAdrr;
    pTwIf->uMemSize1 = pPartition[0].uMemSize;
    pTwIf->uMemAddr2 = pPartition[1].uMemAdrr;
    pTwIf->uMemSize2 = pPartition[1].uMemSize;
    pTwIf->uMemAddr3 = pPartition[2].uMemAdrr;
    pTwIf->uMemSize3 = pPartition[2].uMemSize;
    pTwIf->uMemAddr4 = pPartition[3].uMemAdrr;

    /* Allocate memory for the current 4 partition transactions */
    pPartitionRegTxn = (TPartitionRegTxn *) os_memoryAlloc (pTwIf->hOs, 7*sizeof(TPartitionRegTxn));
    pTxnHdr       = &(pPartitionRegTxn->tHdr);

    /* Zero the allocated memory to be certain that unused fields will be initialized */
    os_memoryZero(pTwIf->hOs, pPartitionRegTxn, 7*sizeof(TPartitionRegTxn));

    /* Prepare partition transaction data */
    pPartitionRegTxn[0].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr1);
    pPartitionRegTxn[1].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemSize1);
    pPartitionRegTxn[2].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr2);
    pPartitionRegTxn[3].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemSize2);
    pPartitionRegTxn[4].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr3);
    pPartitionRegTxn[5].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemSize3);
    pPartitionRegTxn[6].tData  = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr4);


    /* Prepare partition Txn header */
    for (i=0; i<7; i++)
    {
        pTxnHdr = &(pPartitionRegTxn[i].tHdr);
        TXN_PARAM_SET(pTxnHdr, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR)
        TXN_PARAM_SET_MORE(pTxnHdr, 1);         
        TXN_PARAM_SET_SINGLE_STEP(pTxnHdr, 0);
    }


    /* Memory address */
    pTxnHdr = &(pPartitionRegTxn[0].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+4,  &(pPartitionRegTxn[0].tData), REGISTER_SIZE, 0, 0)
    twIf_SendTransaction (pTwIf, pTxnHdr);

    /* Memory size */
    pTxnHdr = &(pPartitionRegTxn[1].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+0,  &(pPartitionRegTxn[1].tData), REGISTER_SIZE, 0, 0)
    twIf_SendTransaction (pTwIf, pTxnHdr);

    /* Registers address */
    pTxnHdr = &(pPartitionRegTxn[2].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+12, &(pPartitionRegTxn[2].tData), REGISTER_SIZE, 0, 0)
    twIf_SendTransaction (pTwIf, pTxnHdr);

    /* Registers size */
    pTxnHdr = &(pPartitionRegTxn[3].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+8,  &(pPartitionRegTxn[3].tData), REGISTER_SIZE, 0, 0)
    eStatus = twIf_SendTransaction (pTwIf, pTxnHdr);

 /* Registers address */
    pTxnHdr = &(pPartitionRegTxn[4].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+20,  &(pPartitionRegTxn[4].tData), REGISTER_SIZE, 0, 0)
    twIf_SendTransaction (pTwIf, pTxnHdr);

 /* Registers size */
    pTxnHdr = &(pPartitionRegTxn[5].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+16,  &(pPartitionRegTxn[5].tData), REGISTER_SIZE, 0, 0)
    eStatus = twIf_SendTransaction (pTwIf, pTxnHdr);

 /* Registers address */
    pTxnHdr = &(pPartitionRegTxn[6].tHdr);
    BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+24,  &(pPartitionRegTxn[6].tData), REGISTER_SIZE, twIf_PartitionTxnDoneCb, pTwIf)
    twIf_SendTransaction (pTwIf, pTxnHdr);

    /* If the transaction is done, free the allocated memory (otherwise freed in the partition CB) */
    if (eStatus != TXN_STATUS_PENDING) 
    {
        os_memoryFree (pTwIf->hOs, pPartitionRegTxn,7*sizeof(TPartitionRegTxn));
    }
}


static void twIf_PartitionTxnDoneCb (TI_HANDLE hTwIf, void *hTxn)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    /* Free the partition transaction buffer after completed (see transaction above) */
    os_memoryFree (pTwIf->hOs, 
                   (char *)hTxn - (6 * sizeof(TPartitionRegTxn)),  /* Move back to the first Txn start */
                   7 * sizeof(TPartitionRegTxn)); 
}


/** 
 * \fn     twIf_Awake
 * \brief  Request to keep the device awake
 * 
 * Used by the Xfer modules to request to keep the device awake until twIf_Sleep() is called.
 * Each call to this function increments AwakeReq counter. Once the device is awake (upon transaction), 
 *     the TwIf SM keeps it awake as long as this counter is not zero.
 * 
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     twIf_Sleep
 */ 
void twIf_Awake (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    /* Increment awake requests counter */
    pTwIf->uAwakeReqCount++;

#ifdef TI_DBG
    pTwIf->uDbgCountAwake++;
    TRACE1(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_Awake: uAwakeReqCount = %d\n", pTwIf->uAwakeReqCount);
#endif
}


/** 
 * \fn     twIf_Sleep
 * \brief  Remove request to keep the device awake
 * 
 * Each call to this function decrements AwakeReq counter.
 * Once this counter is zeroed, if the TxnQ is empty (no WLAN transactions), the TwIf SM is 
 *     invoked to stop the TxnQ and enable the device to sleep (write 0 to ELP register).
 * 
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     twIf_Awake
 */ 
void twIf_Sleep (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    /* Decrement awake requests counter */
    if (pTwIf->uAwakeReqCount > 0) /* in case of redundant call after recovery */
    {   
    pTwIf->uAwakeReqCount--;
    }

#ifdef TI_DBG
    pTwIf->uDbgCountSleep++;
    TRACE1(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_Sleep: uAwakeReqCount = %d\n", pTwIf->uAwakeReqCount);
#endif

    /* If Awake not required and no pending transactions in TxnQ, issue Sleep event to SM */
    if ((pTwIf->uAwakeReqCount == 0) && (pTwIf->uPendingTxnCount == 0))
    {
        twIf_HandleSmEvent (pTwIf, SM_EVENT_SLEEP);
    }
}


/** 
 * \fn     twIf_HwAvailable
 * \brief  The device is awake
 * 
 * This is an indication from the FwEvent that the device is awake.
 * Issue HW_AVAILABLE event to the SM.
 * 
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     
 */ 
void twIf_HwAvailable (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*) hTwIf;

    TRACE0(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HwAvailable: HW is Available\n");

    /* Issue HW_AVAILABLE event to the SM */
    twIf_HandleSmEvent (pTwIf, SM_EVENT_HW_AVAILABLE);
}


/** 
 * \fn     twIf_Transact
 * \brief  Issue a transaction
 * 
 * This method is used by the Xfer modules to issue all transaction types.
 * Translate HW address according to bus partition and call twIf_SendTransaction().
 * 
 * \note   
 * \param  hTwIf - The module's object
 * \param  pTxn  - The transaction object 
 * \return COMPLETE if the transaction was completed in this context, PENDING if not, ERROR if failed
 * \sa     twIf_SendTransaction
 */ 
ETxnStatus twIf_Transact (TI_HANDLE hTwIf, TTxnStruct *pTxn)
{
    TTwIfObj  *pTwIf   = (TTwIfObj*)hTwIf;

    /* Translate HW address for registers region */
    if ((pTxn->uHwAddr >= pTwIf->uMemAddr2) && (pTxn->uHwAddr <= pTwIf->uMemAddr2 + pTwIf->uMemSize2))
    {
        pTxn->uHwAddr = pTxn->uHwAddr - pTwIf->uMemAddr2 + pTwIf->uMemSize1;
    }
    /* Translate HW address for memory region */
    else 
    {
        pTxn->uHwAddr = pTxn->uHwAddr - pTwIf->uMemAddr1;
    }

    /* Regular transaction are not the last and are not single step (only ELP write is) */
    TXN_PARAM_SET_MORE(pTxn, 1);
    TXN_PARAM_SET_SINGLE_STEP(pTxn, 0);

    /* Send the transaction to the TxnQ and update the SM if needed. */
    return twIf_SendTransaction (pTwIf, pTxn);
}

ETxnStatus twIf_TransactReadFWStatus (TI_HANDLE hTwIf, TTxnStruct *pTxn)
{
    TTwIfObj  *pTwIf   = (TTwIfObj*)hTwIf;

    /* Regular transaction are not the last and are not single step (only ELP write is) */
    TXN_PARAM_SET_MORE(pTxn, 1);         
    TXN_PARAM_SET_SINGLE_STEP(pTxn, 0);

    /* Send the transaction to the TxnQ and update the SM if needed. */  
    return twIf_SendTransaction (pTwIf, pTxn);
}


/** 
 * \fn     twIf_SendTransaction
 * \brief  Send a transaction to the device
 * 
 * This method is used by the Xfer modules and the TwIf to send all transaction types to the device.
 * Send the transaction to the TxnQ and update the SM if needed.
 * 
 * \note   
 * \param  pTwIf - The module's object
 * \param  pTxn  - The transaction object 
 * \return COMPLETE if the transaction was completed in this context, PENDING if not, ERROR if failed
 * \sa     
 */ 
static ETxnStatus twIf_SendTransaction (TTwIfObj *pTwIf, TTxnStruct *pTxn)
{
    ETxnStatus eStatus;
#ifdef TI_DBG
    TI_UINT32  data = 0;

    /* Verify that the Txn HW-Address is 4-bytes aligned */
    if (pTxn->uHwAddr & 0x3)
    {
        TRACE2(pTwIf->hReport, REPORT_SEVERITY_ERROR, "twIf_SendTransaction: Unaligned HwAddr! HwAddr=0x%x, Params=0x%x\n", pTxn->uHwAddr, pTxn->uTxnParams);
		return TXN_STATUS_ERROR;
    }	
#endif

    context_EnterCriticalSection (pTwIf->hContext);
    /* increment pending Txn counter */
    pTwIf->uPendingTxnCount++;
    context_LeaveCriticalSection (pTwIf->hContext);

    /* Send transaction to TxnQ */
    eStatus = txnQ_Transact(pTwIf->hTxnQ, pTxn);

#ifdef TI_DBG
    pTwIf->uDbgCountTxn++;
    if      (eStatus == TXN_STATUS_COMPLETE) { pTwIf->uDbgCountTxnComplete++; }
    else if (eStatus == TXN_STATUS_PENDING ) { pTwIf->uDbgCountTxnPending++;  }

	COPY_WLAN_LONG(&data,&(pTxn->aBuf[0]));
    TRACE8(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_SendTransaction: Status = %d, Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d, Data=0x%x \n", eStatus, pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3],data);
#endif

    /* If Txn status is PENDING issue Start event to the SM */
    if (eStatus == TXN_STATUS_PENDING)
    {
        twIf_HandleSmEvent (pTwIf, SM_EVENT_START);
    }

    /* Else (COMPLETE or ERROR) */
    else
    {
        context_EnterCriticalSection (pTwIf->hContext);
        /* decrement pending Txn counter in case of sync transact*/
        pTwIf->uPendingTxnCount--;
        context_LeaveCriticalSection (pTwIf->hContext);

        /* If Awake not required and no pending transactions in TxnQ, issue Sleep event to SM */
        if ((pTwIf->uAwakeReqCount == 0) && (pTwIf->uPendingTxnCount == 0))
        {
            twIf_HandleSmEvent (pTwIf, SM_EVENT_SLEEP);
        }

        /* If Txn failed and error CB available, call it to initiate recovery */
        if (eStatus == TXN_STATUS_ERROR)
        {
            TRACE6(pTwIf->hReport, REPORT_SEVERITY_ERROR, "twIf_SendTransaction: Txn failed!!  Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]);

            if (pTwIf->fErrCb)
            {
                pTwIf->fErrCb (pTwIf->hErrCb, BUS_FAILURE);
            }
        }
    }

    /* Return the Txn status (COMPLETE if completed in this context, PENDING if not, ERROR if failed) */
    return eStatus;
}

/** 
 * \fn     twIf_HandleSmEvent
 * \brief  The TwIf SM implementation
 * 
 * Handle SM event.
 * Control the device awake/sleep states and the TxnQ run/stop states according to the event.
 *  
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     
 */ 
static void twIf_HandleSmEvent (TTwIfObj *pTwIf, ESmEvent eEvent)
{
	ESmState eState = pTwIf->eState;  /* The state before handling the event */

    /* Switch by current state and handle event */
    switch (eState)
    {
    case SM_STATE_AWAKE:
        /* SLEEP event:  AWAKE ==> SLEEP,  stop TxnQ and set ELP reg to sleep */
        if (eEvent == SM_EVENT_SLEEP)
        {
            pTwIf->eState = SM_STATE_SLEEP;
            txnQ_Stop (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN);
            twIf_WriteElpReg (pTwIf, ELP_CTRL_REG_SLEEP);
        }
        break;
    case SM_STATE_SLEEP:
        /* START event:  SLEEP ==> WAIT_HW,  set ELP reg to wake-up */
        if (eEvent == SM_EVENT_START)
        {
            pTwIf->eState = SM_STATE_WAIT_HW;
            twIf_WriteElpReg (pTwIf, ELP_CTRL_REG_AWAKE);
        }
        /* HW_AVAILABLE event:  SLEEP ==> AWAKE,  set ELP reg to wake-up and run TxnQ */
        else if (eEvent == SM_EVENT_HW_AVAILABLE)
        {
            pTwIf->eState = SM_STATE_AWAKE;
            twIf_WriteElpReg (pTwIf, ELP_CTRL_REG_AWAKE);
            txnQ_Run (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN);
        }
        break;
    case SM_STATE_WAIT_HW:
        /* HW_AVAILABLE event:  WAIT_HW ==> AWAKE,  run TxnQ */
        if (eEvent == SM_EVENT_HW_AVAILABLE)
        {
            pTwIf->eState = SM_STATE_AWAKE;
            txnQ_Run (pTwIf->hTxnQ, TXN_FUNC_ID_WLAN);
        }
        break;
    }

	TRACE3(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HandleSmEvent: <currentState = %d, event = %d> --> nextState = %d\n", eState, eEvent, pTwIf->eState);
}


/** 
 * \fn     twIf_TxnDoneCb
 * \brief  Transaction completion CB
 * 
 * This callback is called by the TxnQ upon transaction completion, unless is was completed in
 *     the original context where it was issued.
 * It may be called from bus driver external context (TxnDone ISR) or from WLAN driver context.
 *  
 * \note   
 * \param  hTwIf - The module's object
 * \param  pTxn  - The completed transaction object 
 * \return void
 * \sa     twIf_HandleTxnDone
 */ 
static void twIf_TxnDoneCb (TI_HANDLE hTwIf, TTxnStruct *pTxn)
{
    TTwIfObj *pTwIf = (TTwIfObj*)hTwIf;

#ifdef TI_DBG
    pTwIf->uDbgCountTxnDoneCb++;
    TRACE6(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_TxnDoneCb: Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]);
#endif

    /* In case of recovery flag, Call directly restart callback */
    if (TXN_PARAM_GET_STATUS(pTxn) == TXN_PARAM_STATUS_RECOVERY)
    {
        if (pTwIf->fRecoveryCb)
        {
            TRACE0(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_TxnDoneCb: During Recovery\n");
            pTwIf->bTxnDoneInRecovery = TI_TRUE;
            /* Request schedule to continue handling in driver context (will call twIf_HandleTxnDone()) */
            context_RequestSchedule (pTwIf->hContext, pTwIf->uContextId);
            return;
        }
    }

    /* If the completed Txn is ELP, nothing to do (not counted) so exit */
    if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) 
    {
        return;
    }

    if (pTxn->fTxnDoneCb)
    {
        /* In critical section, enqueue the completed transaction in the TxnDoneQ. */
        context_EnterCriticalSection (pTwIf->hContext);
        que_Enqueue (pTwIf->hTxnDoneQueue, (TI_HANDLE)pTxn);
        context_LeaveCriticalSection (pTwIf->hContext);
    }
    else
    {
        context_EnterCriticalSection (pTwIf->hContext);
         /* Decrement pending Txn counter, It's value will be checked in twIf_HandleTxnDone() */
        if (pTwIf->uPendingTxnCount > 0) /* in case of callback on recovery after restart */
        {
            pTwIf->uPendingTxnCount--;
        }
        context_LeaveCriticalSection (pTwIf->hContext);

    }
    
    /* Request schedule to continue handling in driver context (will call twIf_HandleTxnDone()) */
    context_RequestSchedule (pTwIf->hContext, pTwIf->uContextId);
}

/** 
 * \fn     twIf_HandleTxnDone
 * \brief  Completed transactions handler
 * 
 * The completed transactions handler, called upon TxnDone event, either from the context engine
 *     or directly from twIf_TxnDoneCb() if we are already in the WLAN driver's context.
 * Dequeue all completed transactions in critical section, and call their callbacks if available.
 * If awake is not required and no pending transactions in TxnQ, issue Sleep event to SM.
 *  
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     
 */ 
static void twIf_HandleTxnDone (TI_HANDLE hTwIf)
{
    TTwIfObj   *pTwIf = (TTwIfObj*)hTwIf;
    TTxnStruct *pTxn;

    /* In case of recovery, call the recovery callback and exit */
    if (pTwIf->bTxnDoneInRecovery)
    {
        TRACE0(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HandleTxnDone: call RecoveryCb\n");
        pTwIf->bTxnDoneInRecovery = TI_FALSE;
        pTwIf->fRecoveryCb(pTwIf->hRecoveryCb);
        return;
    }

    /* Loop while there are completed transactions to handle */
    while (1) 
    {
        /* In critical section, dequeue completed transaction from the TxnDoneQ. */
        context_EnterCriticalSection (pTwIf->hContext);
        pTxn = (TTxnStruct *) que_Dequeue (pTwIf->hTxnDoneQueue);
        context_LeaveCriticalSection (pTwIf->hContext);

        /* If no more transactions to handle, exit */
        if (pTxn != NULL)
        {
            context_EnterCriticalSection (pTwIf->hContext);
            /* Decrement pending Txn counter */ 
            if (pTwIf->uPendingTxnCount > 0) /* in case of callback on recovery after restart */
            {   
                pTwIf->uPendingTxnCount--;
            }
            context_LeaveCriticalSection (pTwIf->hContext);

            TRACE4(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HandleTxnDone: Completed-Txn: Params=0x%x, HwAddr=0x%x, Len0=%d, fTxnDoneCb=0x%x\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->fTxnDoneCb);
    
            /* If Txn failed and error CB available, call it to initiate recovery */
            if (TXN_PARAM_GET_STATUS(pTxn) == TXN_PARAM_STATUS_ERROR)
            {
                TRACE6(pTwIf->hReport, REPORT_SEVERITY_ERROR, "twIf_HandleTxnDone: Txn failed!!  Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]);
    
                if (pTwIf->fErrCb)
                {
                    pTwIf->fErrCb (pTwIf->hErrCb, BUS_FAILURE);
                }
                /* in error do not continue */
		return;
            }
    
            /* If Txn specific CB available, call it (may free Txn resources and issue new Txns) */
            if (pTxn->fTxnDoneCb != NULL)
            {
                ((TTxnDoneCb)(pTxn->fTxnDoneCb)) (pTxn->hCbHandle, pTxn);
            } 
        }

        /*If uPendingTxnCount == 0 and awake not required, issue Sleep event to SM */
        if ((pTwIf->uAwakeReqCount == 0) && (pTwIf->uPendingTxnCount == 0))
        {
            twIf_HandleSmEvent (pTwIf, SM_EVENT_SLEEP);
        }

        if (pTxn == NULL)
        {
            return;
        }
    }
} 

/** 
 * \fn     twIf_ClearTxnDoneQueue
 * \brief  Clean the DoneQueue
 * 
 * Clear the specified done queue - don't call the callbacks.
 *  
 * \note   
 * \param  hTwIf - The module's object
 * \return void
 * \sa     
 */ 
static void twIf_ClearTxnDoneQueue (TI_HANDLE hTwIf)
{
    TTwIfObj   *pTwIf = (TTwIfObj*)hTwIf;
    TTxnStruct *pTxn;

    /* Loop while there are completed transactions to handle */
    while (1) 
    {
        /* In critical section, dequeue completed transaction from the TxnDoneQ. */
        context_EnterCriticalSection (pTwIf->hContext);
        pTxn = (TTxnStruct *) que_Dequeue (pTwIf->hTxnDoneQueue);
        context_LeaveCriticalSection (pTwIf->hContext);

        /* If no more transactions to handle, exit */
        if (pTxn != NULL)
        {
            /* Decrement pending Txn counter */ 
            if (pTwIf->uPendingTxnCount > 0) /* in case of callback on recovery after restart */
            {   
                pTwIf->uPendingTxnCount--;
            }
            
            /* 
             * Drop on Recovery 
             * do not call pTxn->fTxnDoneCb (pTxn->hCbHandle, pTxn) callback 
             */
        }

        if (pTxn == NULL)
        {
            return;
        }
    }
}
TI_BOOL	twIf_isValidMemoryAddr(TI_HANDLE hTwIf, TI_UINT32 Address, TI_UINT32 Length)
{
    TTwIfObj   *pTwIf = (TTwIfObj*)hTwIf;

	if ((Address >= pTwIf->uMemAddr1) &&
			(Address + Length < pTwIf->uMemAddr1 + pTwIf->uMemSize1 ))
	return TI_TRUE;

	return TI_FALSE;
}

TI_BOOL	twIf_isValidRegAddr(TI_HANDLE hTwIf, TI_UINT32 Address, TI_UINT32 Length)
{
    TTwIfObj   *pTwIf = (TTwIfObj*)hTwIf;

	if ((Address >= pTwIf->uMemAddr2 ) &&
		( Address < pTwIf->uMemAddr2 + pTwIf->uMemSize2 ))
	return TI_TRUE;

	return TI_FALSE;
}

/*******************************************************************************
*                       DEBUG  FUNCTIONS  IMPLEMENTATION					   *
********************************************************************************/

#ifdef TI_DBG

/** 
 * \fn     twIf_PrintModuleInfo
 * \brief  Print module's parameters (debug)
 * 
 * This function prints the module's parameters.
 * 
 * \note   
 * \param  hTwIf - The module's object                                          
 * \return void 
 * \sa     
 */ 
void twIf_PrintModuleInfo (TI_HANDLE hTwIf) 
{
#ifdef REPORT_LOG
	TTwIfObj *pTwIf = (TTwIfObj*)hTwIf;
	
	WLAN_OS_REPORT(("-------------- TwIf Module Info-- ------------------------\n"));
	WLAN_OS_REPORT(("==========================================================\n"));
	WLAN_OS_REPORT(("eSmState             = %d\n",   pTwIf->eState					));
	WLAN_OS_REPORT(("uContextId           = %d\n",   pTwIf->uContextId              ));
	WLAN_OS_REPORT(("fErrCb               = %d\n",   pTwIf->fErrCb                  ));
	WLAN_OS_REPORT(("hErrCb               = %d\n",   pTwIf->hErrCb                  ));
	WLAN_OS_REPORT(("uAwakeReqCount       = %d\n",   pTwIf->uAwakeReqCount          ));
	WLAN_OS_REPORT(("uPendingTxnCount     = %d\n",   pTwIf->uPendingTxnCount        ));
	WLAN_OS_REPORT(("uMemAddr             = 0x%x\n", pTwIf->uMemAddr1               ));
	WLAN_OS_REPORT(("uMemSize             = 0x%x\n", pTwIf->uMemSize1               ));
	WLAN_OS_REPORT(("uRegAddr             = 0x%x\n", pTwIf->uMemAddr2               ));
	WLAN_OS_REPORT(("uRegSize             = 0x%x\n", pTwIf->uMemSize2               ));
	WLAN_OS_REPORT(("uDbgCountAwake       = %d\n",   pTwIf->uDbgCountAwake          ));
	WLAN_OS_REPORT(("uDbgCountSleep       = %d\n",   pTwIf->uDbgCountSleep          ));
	WLAN_OS_REPORT(("uDbgCountTxn         = %d\n",   pTwIf->uDbgCountTxn            ));
	WLAN_OS_REPORT(("uDbgCountTxnPending  = %d\n",   pTwIf->uDbgCountTxnPending     ));
	WLAN_OS_REPORT(("uDbgCountTxnComplete = %d\n",   pTwIf->uDbgCountTxnComplete    ));
	WLAN_OS_REPORT(("uDbgCountTxnDone     = %d\n",   pTwIf->uDbgCountTxnDoneCb      ));
	WLAN_OS_REPORT(("==========================================================\n\n"));
#endif
} 


void twIf_PrintQueues (TI_HANDLE hTwIf)
{
    TTwIfObj *pTwIf = (TTwIfObj*)hTwIf;

    txnQ_PrintQueues(pTwIf->hTxnQ);
}

#endif /* TI_DBG */