diff -r 000000000000 -r 10c42ec6c05f TWD/TwIf/TwIf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TWD/TwIf/TwIf.c Tue Jun 29 12:34:26 2010 +0100 @@ -0,0 +1,1081 @@ +/* + * TwIf.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 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 */ + TI_UINT8 *pData; /* The addresses partitioning configuration data */ + +} TPartitionTxn; + +/* The addresses partitioning configuration Txn */ +typedef struct +{ + TTxnStruct tHdr; /* The generic transaction structure */ + TI_UINT8 *pData; /* 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 + +} 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),MemoryNormal); + 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),MemoryNormal); + /* Zero the allocated memory to be certain that unused fields will be initialized */ + os_memoryZero (pTwIf->hOs, pPartitionRegTxn, 7*sizeof(TPartitionRegTxn)); + pTxnHdr = &(pPartitionRegTxn->tHdr); + + for (i = 0; i < 7; i++) + { + pPartitionRegTxn[i].pData = os_memoryAlloc (pTwIf->hOs, sizeof(TI_UINT32) + WSPI_PAD_LEN_READ, MemoryDMA); + os_memoryZero (pTwIf->hOs, pPartitionRegTxn[i].pData, sizeof(TI_UINT32) + WSPI_PAD_LEN_READ); + pPartitionRegTxn[i].pData += WSPI_PAD_LEN_READ; + } + + + /* Prepare partition transaction data */ + *((TI_UINT32*)(pPartitionRegTxn[0].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr1); + *((TI_UINT32*)(pPartitionRegTxn[1].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemSize1); + *((TI_UINT32*)(pPartitionRegTxn[2].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr2); + *((TI_UINT32*)(pPartitionRegTxn[3].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemSize2); + *((TI_UINT32*)(pPartitionRegTxn[4].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemAddr3); + *((TI_UINT32*)(pPartitionRegTxn[5].pData)) = ENDIAN_HANDLE_LONG(pTwIf->uMemSize3); + *((TI_UINT32*)(pPartitionRegTxn[6].pData)) = 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].pData, REGISTER_SIZE, 0, 0) + twIf_SendTransaction (pTwIf, pTxnHdr); + + /* Memory size */ + pTxnHdr = &(pPartitionRegTxn[1].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+0, pPartitionRegTxn[1].pData, REGISTER_SIZE, 0, 0) + twIf_SendTransaction (pTwIf, pTxnHdr); + + /* Registers address */ + pTxnHdr = &(pPartitionRegTxn[2].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+12, pPartitionRegTxn[2].pData, REGISTER_SIZE, 0, 0) + twIf_SendTransaction (pTwIf, pTxnHdr); + + /* Registers size */ + pTxnHdr = &(pPartitionRegTxn[3].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+8, pPartitionRegTxn[3].pData, REGISTER_SIZE, 0, 0) + eStatus = twIf_SendTransaction (pTwIf, pTxnHdr); + + + /* Registers address */ + pTxnHdr = &(pPartitionRegTxn[4].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+20, pPartitionRegTxn[4].pData, REGISTER_SIZE, 0, 0) + twIf_SendTransaction (pTwIf, pTxnHdr); + + /* Registers size */ + pTxnHdr = &(pPartitionRegTxn[5].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+16, pPartitionRegTxn[5].pData, REGISTER_SIZE, 0, 0) + eStatus = twIf_SendTransaction (pTwIf, pTxnHdr); + + /* Registers address */ + pTxnHdr = &(pPartitionRegTxn[6].tHdr); + BUILD_TTxnStruct(pTxnHdr, PARTITION_REGISTERS_ADDR+24, pPartitionRegTxn[6].pData, 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) + { + for (i = 0; i < 7; i++) + { + os_memoryFree (pTwIf->hOs, pPartitionRegTxn[i].pData - WSPI_PAD_LEN_READ, sizeof(TI_UINT32) + WSPI_PAD_LEN_READ); + } + /* Release the memory for the 4 partition transactions */ + os_memoryFree (pTwIf->hOs, pPartitionRegTxn, 7 * sizeof(TPartitionRegTxn)); + } +} + + +static void twIf_PartitionTxnDoneCb (TI_HANDLE hTwIf, void *hTxn) +{ + TTwIfObj *pTwIf = (TTwIfObj*) hTwIf; + TPartitionRegTxn *pPartitionTxn; + int i; + + pPartitionTxn = (TPartitionRegTxn *)((char *)hTxn - (6 * sizeof(TPartitionRegTxn))); + + /* Free the partition transaction buffer after completed (see transaction above) */ + for (i = 0; i < 7; i++) + { + os_memoryFree (pTwIf->hOs, pPartitionTxn[i].pData - WSPI_PAD_LEN_READ, sizeof(TI_UINT32) + WSPI_PAD_LEN_READ); + } + 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 + /* Verify that the Txn HW-Address is 4-bytes aligned + - in QOS it should be 2 bytes aligned */ + if (pTxn->uHwAddr & 0x3) + { + TRACE2(pTwIf->hReport, REPORT_SEVERITY_WARNING, "twIf_SendTransaction: Unaligned HwAddr! might be QOS: HwAddr=0x%x, Params=0x%x\n", pTxn->uHwAddr, pTxn->uTxnParams); + } + /* Verify that the host addresses lengths are 4-bytes aligned */ + if ((pTxn->aLen[0] & 0x3) || (pTxn->aLen[1] & 0x3)) + { + TRACE6(pTwIf->hReport, REPORT_SEVERITY_WARNING, "twIf_SendTransaction: Unaligned Length! might be QOS: Len0=%d, Len1=%d, Len2=%d, Len3=%d, Params=0x%x, HwAddr=0x%x\n", pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3], pTxn->uTxnParams, pTxn->uHwAddr); + + } + /* + * Note: We may add other checks here, like length 2 & 3 and host addresses alignment. + * Note that the host address for read transaction may be unaligned (for Rx-Xfer QoS packets) + */ +#endif + + + /* 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++; } + TRACE8(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_SendTransaction: Status = %d, Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d, Data0=%x \n", eStatus, pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3], *((TI_UINT32*)pTxn->aBuf[0])); +#endif + + /* If Txn status is PENDING, increment pending Txn counter and issue Start event to the SM */ + if (eStatus == TXN_STATUS_PENDING) + { + pTwIf->uPendingTxnCount++; + twIf_HandleSmEvent (pTwIf, SM_EVENT_START); + } + + /* Else (COMPLETE or ERROR) */ + else + { + /* 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: --> 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: call RecoveryCb\n"); + pTwIf->fRecoveryCb(pTwIf->hRecoveryCb); + 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. */ + que_Enqueue (pTwIf->hTxnDoneQueue, (TI_HANDLE)pTxn); + } + else + { + /* 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--; + } + } + + /* 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; + + /* Loop while there are completed transactions to handle */ + while (1) + { + /* In critical section, dequeue completed transaction from the TxnDoneQ. */ + CONTEXT_ENTER_CRITICAL_SECTION (pTwIf->hContext); + pTxn = (TTxnStruct *) que_Dequeue (pTwIf->hTxnDoneQueue); + CONTEXT_LEAVE_CRITICAL_SECTION (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--; + } + + 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_ENTER_CRITICAL_SECTION (pTwIf->hContext); + pTxn = (TTxnStruct *) que_Dequeue (pTwIf->hTxnDoneQueue); + CONTEXT_LEAVE_CRITICAL_SECTION (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) +{ + 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(("uFWStatuSize = 0x%x\n", pTwIf->uMemSize3 )); + WLAN_OS_REPORT(("uFWStatuAddr = 0x%x\n", pTwIf->uMemSize3 )); + WLAN_OS_REPORT(("uFWMemAddr = 0x%x\n", pTwIf->uMemAddr4 )); + 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")); +} + + +void twIf_PrintQueues (TI_HANDLE hTwIf) +{ + TTwIfObj *pTwIf = (TTwIfObj*)hTwIf; + + txnQ_PrintQueues(pTwIf->hTxnQ); +} + + +#endif /* TI_DBG */ + + + +