/* * txResult.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. */ /**************************************************************************** * * MODULE: txResult.c * * PURPOSE: Handle packets Tx results upon Tx-complete from the FW. * * DESCRIPTION: * ============ * This module is called upon Tx-complete from FW. * It retrieves the transmitted packets results from the FW TxResult table and * calls the upper layer callback function for each packet with its results. * ****************************************************************************/ #define __FILE_ID__ FILE_ID_107 #include "tidef.h" #include "osApi.h" #include "report.h" #include "TwIf.h" #include "txCtrlBlk_api.h" #include "txResult_api.h" #include "TWDriver.h" #include "FwEvent_api.h" #define TX_RESULT_QUEUE_DEPTH_MASK (TRQ_DEPTH - 1) #if (TX_RESULT_QUEUE_DEPTH_MASK & TRQ_DEPTH) #error TRQ_DEPTH should be a power of 2 !! #endif /* Callback function definition for Tx sendPacketComplete */ typedef void (* TSendPacketCompleteCb)(TI_HANDLE hCbObj, TxResultDescriptor_t *pTxResultInfo); /* Tx-Result SM states */ typedef enum { TX_RESULT_STATE_IDLE, TX_RESULT_STATE_READING } ETxResultState; /* The host Tx-results counter write transaction structure. */ typedef struct { TTxnStruct tTxnStruct; TI_UINT32 uCounter; } THostCounterWriteTxn; /* The Tx-results counters and table read transaction structure. */ typedef struct { TTxnStruct tTxnStruct; TxResultInterface_t tTxResultInfo; } TResultsInfoReadTxn; /* The TxResult module object. */ typedef struct { TI_HANDLE hOs; TI_HANDLE hReport; TI_HANDLE hTwIf; TI_UINT32 uTxResultInfoAddr; /* The HW Tx-Result Table address */ TI_UINT32 uTxResultHostCounterAddr;/* The Tx-Result host counter address in SRAM */ TI_UINT32 uHostResultsCounter; /* Number of results read by host from queue since FW-init (updated to FW) */ ETxResultState eState; /* Current eState of SM */ TSendPacketCompleteCb fSendPacketCompleteCb; /* Tx-Complete callback function */ TI_HANDLE hSendPacketCompleteHndl; /* Tx-Complete callback function handle */ THostCounterWriteTxn tHostCounterWriteTxn; /* The structure used for writing host results counter to FW */ TResultsInfoReadTxn tResultsInfoReadTxn; /* The structure used for reading Tx-results counters and table from FW */ #ifdef TI_DBG TI_UINT32 uInterruptsCounter; /* Count number of Tx-results */ #endif } TTxResultObj; static void txResult_Restart (TTxResultObj *pTxResult); static void txResult_HandleNewResults (TTxResultObj *pTxResult); static void txResult_StateMachine (TI_HANDLE hTxResult); /**************************************************************************** * txResult_Create() **************************************************************************** * DESCRIPTION: Create the Tx-Result object * * INPUTS: hOs * * OUTPUT: None * * RETURNS: The Created object ****************************************************************************/ TI_HANDLE txResult_Create(TI_HANDLE hOs) { TTxResultObj *pTxResult; pTxResult = os_memoryAlloc(hOs, sizeof(TTxResultObj)); if (pTxResult == NULL) return NULL; os_memoryZero(hOs, pTxResult, sizeof(TTxResultObj)); pTxResult->hOs = hOs; return( (TI_HANDLE)pTxResult ); } /**************************************************************************** * txResult_Destroy() **************************************************************************** * DESCRIPTION: Destroy the Tx-Result object * * INPUTS: hTxResult - The object to free * * OUTPUT: None * * RETURNS: TI_OK or TI_NOK ****************************************************************************/ TI_STATUS txResult_Destroy(TI_HANDLE hTxResult) { TTxResultObj *pTxResult = (TTxResultObj *)hTxResult; if (pTxResult) os_memoryFree(pTxResult->hOs, pTxResult, sizeof(TTxResultObj)); return TI_OK; } /**************************************************************************** * txResult_Init() **************************************************************************** DESCRIPTION: ============ Initialize the txResult module. ****************************************************************************/ TI_STATUS txResult_Init(TI_HANDLE hTxResult, TI_HANDLE hReport, TI_HANDLE hTwIf) { TTxResultObj *pTxResult = (TTxResultObj *)hTxResult; TTxnStruct *pTxn; pTxResult->hReport = hReport; pTxResult->hTwIf = hTwIf; /* Prepare Host-Results-Counter write transaction (HwAddr is filled before each transaction) */ pTxn = &pTxResult->tHostCounterWriteTxn.tTxnStruct; TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR) BUILD_TTxnStruct(pTxn, 0, &pTxResult->tHostCounterWriteTxn.uCounter, REGISTER_SIZE, NULL, NULL) /* Prepare Tx-Result counter and table read transaction (HwAddr is filled before each transaction) */ pTxn = &pTxResult->tResultsInfoReadTxn.tTxnStruct; TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_INC_ADDR) BUILD_TTxnStruct(pTxn, 0, &pTxResult->tResultsInfoReadTxn.tTxResultInfo, sizeof(TxResultInterface_t), (TTxnDoneCb)txResult_StateMachine, hTxResult) txResult_Restart (pTxResult); return TI_OK; } /**************************************************************************** * txResult_Restart() **************************************************************************** DESCRIPTION: ============ Restarts the Tx-Result module. Called upon init and recovery. Shouldn't be called upon disconnect, since the FW provides Tx-Complete for all pending packets in FW!! ****************************************************************************/ static void txResult_Restart (TTxResultObj *pTxResult) { pTxResult->uHostResultsCounter = 0; pTxResult->eState = TX_RESULT_STATE_IDLE; } /**************************************************************************** * txResult_setHwInfo() **************************************************************************** * DESCRIPTION: * Called after the HW configuration upon init or recovery. * Store the Tx-result table HW address. ****************************************************************************/ void txResult_setHwInfo(TI_HANDLE hTxResult, TDmaParams *pDmaParams) { TTxResultObj *pTxResult = (TTxResultObj *)hTxResult; pTxResult->uTxResultInfoAddr = (TI_UINT32)(pDmaParams->fwTxResultInterface); pTxResult->uTxResultHostCounterAddr = pTxResult->uTxResultInfoAddr + TI_FIELD_OFFSET(TxResultControl_t, TxResultHostCounter); txResult_Restart (pTxResult); } /**************************************************************************** * txResult_TxCmpltIntrCb() **************************************************************************** * DESCRIPTION: * ============ * Called upon DATA interrupt from the FW. * If new Tx results are available, start handling them. * * INPUTS: hTxResult - the txResult object handle. * pFwStatus - The FW status registers read by the FwEvent * * OUTPUT: None * * RETURNS: ETxnStatus ***************************************************************************/ ETxnStatus txResult_TxCmpltIntrCb (TI_HANDLE hTxResult, FwStatus_t *pFwStatus) { TTxResultObj *pTxResult = (TTxResultObj *)hTxResult; TI_UINT32 uTempCounters; FwStatCntrs_t *pFwStatusCounters; #ifdef TI_DBG pTxResult->uInterruptsCounter++; if (pTxResult->eState != TX_RESULT_STATE_IDLE) { TRACE1(pTxResult->hReport, REPORT_SEVERITY_INFORMATION, ": called in eState %d, so exit\n", pTxResult->eState); return TXN_STATUS_COMPLETE; } #endif /* If no new results - exit (may happen since Data interrupt is common to all Tx&Rx events) */ uTempCounters = ENDIAN_HANDLE_LONG(pFwStatus->counters); pFwStatusCounters = (FwStatCntrs_t *)&uTempCounters; if (pFwStatusCounters->txResultsCntr == (TI_UINT8)pTxResult->uHostResultsCounter) { TRACE0(pTxResult->hReport, REPORT_SEVERITY_INFORMATION, ": No new Tx results\n"); return TXN_STATUS_COMPLETE; } /* Call the SM to handle the new Tx results */ txResult_StateMachine (hTxResult); return TXN_STATUS_COMPLETE; } /**************************************************************************** * txResult_StateMachine() **************************************************************************** * DESCRIPTION: * * The main SM of the module. Called in IDLE eState by txResult_TxCmpltIntrCb() on * Data interrupt from the FW. * If no new results - exit (may happen since Data interrupt is common to all Tx&Rx events) * Read all Tx-Result cyclic table. * Go over the new Tx-results and call the upper layer callback function for each packet result. * At the end - write the new host counter to the FW. * * INPUTS: * * OUTPUT: * * RETURNS: None ****************************************************************************/ static void txResult_StateMachine (TI_HANDLE hTxResult) { TTxResultObj *pTxResult = (TTxResultObj *)hTxResult; ETxnStatus eTwifStatus = TXN_STATUS_COMPLETE; /* Last bus operation status: Complete (Sync) or Pending (Async). */ TTxnStruct *pTxn = &(pTxResult->tResultsInfoReadTxn.tTxnStruct); /* Loop while processing is completed in current context (sync), or until fully completed */ while (eTwifStatus == TXN_STATUS_COMPLETE) { TRACE2(pTxResult->hReport, REPORT_SEVERITY_INFORMATION, ": eState = %d, eTwifStatus = %d\n", pTxResult->eState, eTwifStatus); switch(pTxResult->eState) { case TX_RESULT_STATE_IDLE: /* Read Tx-Result queue and counters. */ pTxn->uHwAddr = pTxResult->uTxResultInfoAddr; eTwifStatus = twIf_Transact (pTxResult->hTwIf, pTxn); pTxResult->eState = TX_RESULT_STATE_READING; break; case TX_RESULT_STATE_READING: /* Process new Tx results, call upper layers to handle them and update host-index in the FW. */ txResult_HandleNewResults (pTxResult); pTxResult->eState = TX_RESULT_STATE_IDLE; return; /********* Exit after all processing is finished **********/ default: TRACE1(pTxResult->hReport, REPORT_SEVERITY_ERROR, ": Unknown eState = %d\n", pTxResult->eState); return; } } if (eTwifStatus == TXN_STATUS_ERROR) { TRACE2(pTxResult->hReport, REPORT_SEVERITY_ERROR, ": returning ERROR in eState %d, eTwifStatus=%d !!!\n", pTxResult->eState, eTwifStatus); } } /**************************************************************************** * txResult_HandleNewResults() **************************************************************************** * DESCRIPTION: * ============ * We now have the Tx Result table info from the FW so do as follows: * 1. Find the number of new results (FW counter minus host counter), and if 0 exit. * 2. Call the upper layers callback per Tx result. * 3. Update Host-Counter to be equal to the FW-Counter, and write it to the FW. ***************************************************************************/ static void txResult_HandleNewResults (TTxResultObj *pTxResult) { TI_UINT32 uNumNewResults; /* The number of new Tx-Result entries to be processed. */ TI_UINT32 uFwResultsCounter; /* The FW current results counter (accumulated). */ TI_UINT32 uTableIndex; TI_UINT32 i; TxResultDescriptor_t *pCurrentResult; TTxnStruct *pTxn = &(pTxResult->tHostCounterWriteTxn.tTxnStruct); /* The uFwResultsCounter is the accumulated number of Tx-Results provided by the FW, and the * uHostResultsCounter is the accumulated number of Tx-Results processed by the host. * The delta is the number of new Tx-results in the queue, waiting for host processing. * Since the difference is always a small positive number, a simple subtraction is good * also for wrap around case. */ uFwResultsCounter = ENDIAN_HANDLE_LONG(pTxResult->tResultsInfoReadTxn.tTxResultInfo.TxResultControl.TxResultFwCounter); uNumNewResults = uFwResultsCounter - pTxResult->uHostResultsCounter; #ifdef TI_DBG /* Verify there are new entries (was already checked in txResult_TxCmpltIntrCb) */ if (uNumNewResults == 0) { TRACE2(pTxResult->hReport, REPORT_SEVERITY_WARNING, ": No New Results although indicated by FwStatus!! HostCount=%d, FwCount=%d\n", pTxResult->uHostResultsCounter, uFwResultsCounter); return; } #endif /* Update host results-counter in FW to be equal to the FW counter (all new results were processed). */ pTxResult->tHostCounterWriteTxn.uCounter = ENDIAN_HANDLE_LONG(uFwResultsCounter); pTxn->uHwAddr = pTxResult->uTxResultHostCounterAddr; twIf_Transact(pTxResult->hTwIf, pTxn); TRACE3(pTxResult->hReport, REPORT_SEVERITY_INFORMATION, ": NumResults=%d, OriginalHostCount=%d, FwCount=%d\n", uNumNewResults, pTxResult->uHostResultsCounter, uFwResultsCounter); /* Loop over all new Tx-results and call Tx-complete callback with current entry pointer. */ /* NOTE: THIS SHOULD COME LAST because it may lead to driver-stop process!! */ for (i = 0; i < uNumNewResults; i++) { uTableIndex = pTxResult->uHostResultsCounter & TX_RESULT_QUEUE_DEPTH_MASK; pCurrentResult = &(pTxResult->tResultsInfoReadTxn.tTxResultInfo.TxResultQueue[uTableIndex]); pTxResult->uHostResultsCounter++; TRACE1(pTxResult->hReport, REPORT_SEVERITY_INFORMATION , ": call upper layer CB, Status = %d\n", pCurrentResult->status); pTxResult->fSendPacketCompleteCb (pTxResult->hSendPacketCompleteHndl, pCurrentResult); } } /**************************************************************************** * txResult_RegisterCb() **************************************************************************** * DESCRIPTION: Register the upper driver Tx-Result callback functions. ****************************************************************************/ void txResult_RegisterCb (TI_HANDLE hTxResult, TI_UINT32 uCallBackId, void *CBFunc, TI_HANDLE hCbObj) { TTxResultObj* pTxResult = (TTxResultObj*)hTxResult; switch (uCallBackId) { /* Set Tx-Complete callback */ case TWD_INT_SEND_PACKET_COMPLETE: pTxResult->fSendPacketCompleteCb = (TSendPacketCompleteCb)CBFunc; pTxResult->hSendPacketCompleteHndl = hCbObj; break; default: TRACE0(pTxResult->hReport, REPORT_SEVERITY_ERROR, ": Illegal value\n"); return; } } #ifdef TI_DBG /* Debug Functions */ /**************************************************************************** * txResult_PrintInfo() **************************************************************************** * DESCRIPTION: Prints TX result debug information. ****************************************************************************/ void txResult_PrintInfo (TI_HANDLE hTxResult) { #ifdef REPORT_LOG TTxResultObj* pTxResult = (TTxResultObj*)hTxResult; WLAN_OS_REPORT(("Tx-Result Module Information:\n")); WLAN_OS_REPORT(("=============================\n")); WLAN_OS_REPORT(("uInterruptsCounter: %d\n", pTxResult->uInterruptsCounter)); WLAN_OS_REPORT(("uHostResultsCounter: %d\n", pTxResult->uHostResultsCounter)); WLAN_OS_REPORT(("=============================\n")); #endif } /**************************************************************************** * txResult_ClearInfo() **************************************************************************** * DESCRIPTION: Clears TX result debug information. ****************************************************************************/ void txResult_ClearInfo (TI_HANDLE hTxResult) { TTxResultObj* pTxResult = (TTxResultObj*)hTxResult; pTxResult->uInterruptsCounter = 0; } #endif /* TI_DBG */