blob: ab33fafbafb32e1d5a73657de925a48b5659697f [file] [log] [blame]
/*
* 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: void
***************************************************************************/
void 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;
}
#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;
}
/* Call the SM to handle the new Tx results */
txResult_StateMachine (hTxResult);
}
/****************************************************************************
* 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 */