blob: 22977cd4b1efba7e18381f06f4d68a0d2ca8070c [file] [log] [blame]
/*
* txCtrl.c
*
* Copyright(c) 1998 - 2010 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: txCtrl.c */
/* PURPOSE: The central Tx path module. */
/* Prepares Tx packets sent from the data-queue and mgmt-queue */
/* for copy to the FW, including building the header and the */
/* Tx-descriptor. */
/* */
/*******************************************************************************/
#define __FILE_ID__ FILE_ID_56
#include "tidef.h"
#include "paramOut.h"
#include "osApi.h"
#include "TWDriver.h"
#include "DataCtrl_Api.h"
#include "802_11Defs.h"
#include "Ethernet.h"
#include "report.h"
#include "timer.h"
#include "TI_IPC_Api.h"
#include "EvHandler.h"
#include "qosMngr_API.h"
#include "healthMonitor.h"
#include "txCtrl.h"
#include "txCtrl_Api.h"
#include "DrvMainModules.h"
#ifdef XCC_MODULE_INCLUDED
#include "XCCMngr.h"
#endif
#include "bmtrace_api.h"
/*
* Module internal functions prototypes:
*/
/* Note: put here and not in txCtrl.h to avoid warning in the txCtrl submodules that include txCtrl.h */
static void txCtrl_TxCompleteCb (TI_HANDLE hTxCtrl, TxResultDescriptor_t *pTxResultInfo);
static void txCtrl_BuildDataPkt (txCtrl_t *pTxCtrl, TTxCtrlBlk *pPktCtrlBlk,
TI_UINT32 uAc, TI_UINT32 uBackpressure);
static void txCtrl_BuildMgmtPkt (txCtrl_t *pTxCtrl, TTxCtrlBlk *pPktCtrlBlk, TI_UINT32 uAc);
static void txCtrl_UpdateHighestAdmittedAcTable (txCtrl_t *pTxCtrl);
static void txCtrl_UpdateAcToTidMapping (txCtrl_t *pTxCtrl);
static void txCtrl_UpdateBackpressure (txCtrl_t *pTxCtrl, TI_UINT32 freedAcBitmap);
static void txCtrl_UpdateTxCounters (txCtrl_t *pTxCtrl,
TxResultDescriptor_t *pTxResultInfo,
TTxCtrlBlk *pPktCtrlBlk,
TI_UINT32 ac,
TI_BOOL bIsDataPkt);
#ifdef XCC_MODULE_INCLUDED /* Needed only for XCC-V4 */
static void txCtrl_SetTxDelayCounters (txCtrl_t *pTxCtrl,
TI_UINT32 ac,
TI_UINT32 fwDelay,
TI_UINT32 driverDelay,
TI_UINT32 mediumDelay);
#endif /* XCC_MODULE_INCLUDED */
/********************************************************************************
* *
* MACROS and INLINE FUNCTIONS *
* *
*********************************************************************************/
/* Get the highest admitted AC equal or below the requested one. */
/* AC to TID translation is bivalent so update TID only if the AC was changed. */
#define SELECT_AC_FOR_TID(ptc,tid,ac) \
ac = ptc->highestAdmittedAc[WMEQosTagToACTable[tid]]; \
if (ac != WMEQosTagToACTable[tid]) \
tid = WMEQosAcToTid[ac]
/* Update packet length in the descriptor according to HW interface requirements */
static inline TI_UINT16 txCtrl_TranslateLengthToFw (TTxCtrlBlk *pPktCtrlBlk)
{
TI_UINT16 uPktLen = pPktCtrlBlk->tTxDescriptor.length;
TI_UINT16 uLastWordPad;
TI_UINT32 uBufNum;
uPktLen = (uPktLen + 3) & 0xFFFC; /* Add alignment bytes if needed */
uLastWordPad = uPktLen - pPktCtrlBlk->tTxDescriptor.length; /* Find number of alignment bytes added */
uPktLen = uPktLen >> 2; /* Convert length to words */
pPktCtrlBlk->tTxDescriptor.length = ENDIAN_HANDLE_WORD(uPktLen);/* Save FW format length in descriptor */
/* Find last buffer (last buffer in use is pointed by uBufNum-1) */
for (uBufNum = 1; uBufNum < MAX_XFER_BUFS; uBufNum++)
{
if (pPktCtrlBlk->tTxnStruct.aLen[uBufNum] == 0)
{
break;
}
}
/* Add last word alignment pad also to the last buffer length */
pPktCtrlBlk->tTxnStruct.aLen[uBufNum - 1] += uLastWordPad;
return uLastWordPad;
}
/* Translate packet timestamp to FW time, and update also lifeTime and uDriverDelay */
static inline void txCtrl_TranslateTimeToFw (txCtrl_t *pTxCtrl, TTxCtrlBlk *pPktCtrlBlk, TI_UINT16 uLifeTime)
{
TI_UINT32 uPktStartTime = pPktCtrlBlk->tTxDescriptor.startTime; /* Contains host start time */
/* Save host packet handling time until this point (for statistics) */
pPktCtrlBlk->tTxPktParams.uDriverDelay = os_timeStampMs (pTxCtrl->hOs) - uPktStartTime;
/* Translate packet timestamp to FW time and undate descriptor */
uPktStartTime = TWD_TranslateToFwTime (pTxCtrl->hTWD, uPktStartTime);
pPktCtrlBlk->tTxDescriptor.startTime = ENDIAN_HANDLE_LONG (uPktStartTime);
pPktCtrlBlk->tTxDescriptor.lifeTime = ENDIAN_HANDLE_WORD (uLifeTime);
}
/********************************************************************************
* *
* PUBLIC FUNCTIONS IMPLEMENTATION *
* *
*********************************************************************************/
/*************************************************************************
* txCtrl_Create *
**************************************************************************
* DESCRIPTION: This function initializes the Tx module.
*
* INPUT: hOs - handle to Os Abstraction Layer
*
* OUTPUT:
*
* RETURN: Handle to the allocated Tx data control block
*************************************************************************/
TI_HANDLE txCtrl_Create (TI_HANDLE hOs)
{
txCtrl_t *pTxCtrl;
/* allocate Tx module control block */
pTxCtrl = os_memoryAlloc(hOs, (sizeof(txCtrl_t)));
if (!pTxCtrl)
return NULL;
/* reset tx control object */
os_memoryZero(hOs, pTxCtrl, (sizeof(txCtrl_t)));
pTxCtrl->TxEventDistributor = DistributorMgr_Create(hOs, MAX_TX_NOTIF_REQ_ELMENTS);
pTxCtrl->hOs = hOs;
return pTxCtrl;
}
/***************************************************************************
* txCtrl_Init *
****************************************************************************
* DESCRIPTION: This function configures the TxCtrl module.
***************************************************************************/
void txCtrl_Init (TStadHandlesList *pStadHandles)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)(pStadHandles->hTxCtrl);
TI_UINT32 ac;
/* Save other modules handles */
pTxCtrl->hOs = pStadHandles->hOs;
pTxCtrl->hReport = pStadHandles->hReport;
pTxCtrl->hCtrlData = pStadHandles->hCtrlData;
pTxCtrl->hTWD = pStadHandles->hTWD;
pTxCtrl->hTxDataQ = pStadHandles->hTxDataQ;
pTxCtrl->hTxMgmtQ = pStadHandles->hTxMgmtQ;
pTxCtrl->hEvHandler = pStadHandles->hEvHandler;
pTxCtrl->hHealthMonitor = pStadHandles->hHealthMonitor;
pTxCtrl->hTimer = pStadHandles->hTimer;
pTxCtrl->hStaCap = pStadHandles->hStaCap;
pTxCtrl->hXCCMngr = pStadHandles->hXCCMngr;
pTxCtrl->hQosMngr = pStadHandles->hQosMngr;
pTxCtrl->hRxData = pStadHandles->hRxData;
/* Set Tx parameters to defaults */
pTxCtrl->headerConverMode = HDR_CONVERT_NONE;
pTxCtrl->currentPrivacyInvokedMode = DEF_CURRENT_PRIVACY_MODE;
pTxCtrl->eapolEncryptionStatus = DEF_EAPOL_ENCRYPTION_STATUS;
pTxCtrl->encryptionFieldSize = 0;
pTxCtrl->currBssType = BSS_INFRASTRUCTURE;
pTxCtrl->busyAcBitmap = 0;
pTxCtrl->dbgPktSeqNum = 0;
pTxCtrl->bCreditCalcTimerRunning = TI_FALSE;
pTxCtrl->genericEthertype = ETHERTYPE_EAPOL;
for (ac = 0; ac < MAX_NUM_OF_AC; ac++)
{
pTxCtrl->aMsduLifeTimeTu[ac] = MGMT_PKT_LIFETIME_TU;
pTxCtrl->ackPolicy[ac] = ACK_POLICY_LEGACY;
pTxCtrl->admissionState[ac] = AC_ADMITTED;
pTxCtrl->admissionRequired[ac] = ADMISSION_NOT_REQUIRED;
pTxCtrl->useAdmissionAlgo[ac] = TI_FALSE;
pTxCtrl->mediumTime[ac] = 0;
pTxCtrl->lastCreditCalcTimeStamp[ac] = 0;
pTxCtrl->credit[ac] = 0;
}
/* Reset counters */
txCtrlParams_resetCounters (pStadHandles->hTxCtrl);
#ifdef TI_DBG
txCtrlParams_resetDbgCounters (pStadHandles->hTxCtrl);
#endif
/* Register the Tx-Complete callback function. */
TWD_RegisterCb (pTxCtrl->hTWD,
TWD_EVENT_TX_RESULT_SEND_PKT_COMPLETE,
(void*)txCtrl_TxCompleteCb,
pStadHandles->hTxCtrl);
/* Register the Update-Busy-Map callback function. */
TWD_RegisterCb (pTxCtrl->hTWD,
TWD_EVENT_TX_HW_QUEUE_UPDATE_BUSY_MAP,
(void *)txCtrl_UpdateBackpressure,
pStadHandles->hTxCtrl);
TRACE0(pTxCtrl->hReport, REPORT_SEVERITY_INIT, ".....Tx Data configured successfully\n");
}
/*************************************************************************
* txCtrl_SetDefaults *
**************************************************************************
* DESCRIPTION:
*
* INPUT:
* txDataInitParams - Tx Data creation parameters
*
* OUTPUT:
*
* RETURN:
*************************************************************************/
TI_STATUS txCtrl_SetDefaults (TI_HANDLE hTxCtrl, txDataInitParams_t *txDataInitParams)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
pTxCtrl->creditCalculationTimeout = txDataInitParams->creditCalculationTimeout;
pTxCtrl->bCreditCalcTimerEnabled = txDataInitParams->bCreditCalcTimerEnabled;
/* Update queues mapping (AC/TID/Backpressure) after module init. */
txCtrl_UpdateQueuesMapping (hTxCtrl);
/* allocate timer for credit calculation */
pTxCtrl->hCreditTimer = tmr_CreateTimer (pTxCtrl->hTimer);
if (pTxCtrl->hCreditTimer == NULL)
{
TRACE0(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_SetDefaults(): Failed to create hCreditTimer!\n");
return TI_NOK;
}
return TI_OK;
}
/***************************************************************************
* txCtrl_Unload *
****************************************************************************
* DESCRIPTION: This function unload the tx ctrl module.
*
***************************************************************************/
TI_STATUS txCtrl_Unload (TI_HANDLE hTxCtrl)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
if (pTxCtrl == NULL)
{
return TI_NOK;
}
DistributorMgr_Destroy (pTxCtrl->TxEventDistributor);
if (pTxCtrl->hCreditTimer)
{
tmr_DestroyTimer (pTxCtrl->hCreditTimer);
}
/* free Tx Data control block */
os_memoryFree (pTxCtrl->hOs, pTxCtrl, sizeof(txCtrl_t));
return TI_OK;
}
/*******************************************************************************
* txCtrl_XmitData *
********************************************************************************
* DESCRIPTION: Get a packet from the data-queue, allocate HW resources and CtrlBlk,
* build header and descriptor, and send it to HW by TxXfer.
*
* RETURNS: STATUS_XMIT_SUCCESS - Packet sent succesfully
* STATUS_XMIT_BUSY - Packet dropped due to lack of HW resources, retransmit later.
* STATUS_XMIT_ERROR - Packet dropped due to an unexpected problem (bug).
********************************************************************************/
TI_STATUS txCtrl_XmitData (TI_HANDLE hTxCtrl, TTxCtrlBlk *pPktCtrlBlk)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
ETxnStatus eStatus; /* The Xfer return value (different than this function's return values). */
TI_UINT32 uAc;
TI_UINT32 uBackpressure = 0; /* HwQueue's indication when the current queue becomes busy. */
ETxHwQueStatus eHwQueStatus;
CL_TRACE_START_L3();
/* Get an admitted AC corresponding to the packet TID.
* If downgraded due to admission limitation, the TID is downgraded as well.
*/
SELECT_AC_FOR_TID (pTxCtrl, pPktCtrlBlk->tTxDescriptor.tid, uAc);
#ifdef TI_DBG
TRACE3(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_XmitData(): Pkt Tx, DescID=%d, AC=%d, Len=%d\n", pPktCtrlBlk->tTxDescriptor.descID, uAc, pPktCtrlBlk->tTxDescriptor.length );
pTxCtrl->dbgCounters.dbgNumPktsSent[uAc]++;
#endif
/* Call TxHwQueue for Hw resources allocation. */
{
CL_TRACE_START_L4();
eHwQueStatus = TWD_txHwQueue_AllocResources (pTxCtrl->hTWD, pPktCtrlBlk);
CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TX", ".allocResources");
}
/* If the current AC can't get more packets, stop it in data-queue module. */
if (eHwQueStatus == TX_HW_QUE_STATUS_STOP_NEXT)
{
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsBackpressure[uAc]++;
TRACE2(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_XmitData(): Backpressure = 0x%x, queue = %d\n", uBackpressure, uAc);
#endif
uBackpressure = 1 << uAc;
pTxCtrl->busyAcBitmap |= uBackpressure; /* Set the busy bit of the current AC. */
txDataQ_StopQueue (pTxCtrl->hTxDataQ, pTxCtrl->admittedAcToTidMap[uAc]);
}
/* If current packet can't be transmitted due to lack of resources, return with BUSY value. */
else if (eHwQueStatus == TX_HW_QUE_STATUS_STOP_CURRENT)
{
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsBusy[uAc]++;
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_XmitData(): Queue busy - Packet dropped, queue = %d\n", uAc);
#endif
txDataQ_StopQueue (pTxCtrl->hTxDataQ, pTxCtrl->admittedAcToTidMap[uAc]);
CL_TRACE_END_L3("tiwlan_drv.ko", "INHERIT", "TX", "");
return STATUS_XMIT_BUSY;
}
/* Prepare the packet control-block including the Tx-descriptor. */
{
CL_TRACE_START_L4();
txCtrl_BuildDataPkt(pTxCtrl, pPktCtrlBlk, uAc, uBackpressure);
CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TX", ".FillCtrlBlk");
}
/* Call the Tx-Xfer to start packet transfer to the FW and return its result. */
{
CL_TRACE_START_L4();
eStatus = TWD_txXfer_SendPacket (pTxCtrl->hTWD, pPktCtrlBlk);
CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TX", ".XferSendPacket");
}
if (eStatus == TXN_STATUS_ERROR)
{
#ifdef TI_DBG
TRACE2(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_XmitData(): Xfer Error, queue = %d, Status = %d\n", uAc, eStatus);
pTxCtrl->dbgCounters.dbgNumPktsError[uAc]++;
#endif
/* Free the packet resources (packet and CtrlBlk) */
txCtrl_FreePacket (pTxCtrl, pPktCtrlBlk, TI_NOK);
CL_TRACE_END_L3("tiwlan_drv.ko", "INHERIT", "TX", "");
return STATUS_XMIT_ERROR;
}
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsSuccess[uAc]++;
#endif
CL_TRACE_END_L3("tiwlan_drv.ko", "INHERIT", "TX", "");
return STATUS_XMIT_SUCCESS;
}
/*******************************************************************************
* txCtrl_XmitMgmt *
********************************************************************************
* DESCRIPTION: Get a packet from the Mgmt-Queue (management, EAPOL or IAPP),
* allocate HW resources and CtrlBlk, build header if Ethernet (EAPOL),
* build descriptor, and send packet to HW by TxXfer.
*
* RETURNS: STATUS_XMIT_SUCCESS - Packet sent succesfully.
* STATUS_XMIT_BUSY - Packet dropped due to lack of HW resources, retransmit later.
* STATUS_XMIT_ERROR - Packet dropped due to an unexpected problem (bug).
********************************************************************************/
TI_STATUS txCtrl_XmitMgmt (TI_HANDLE hTxCtrl, TTxCtrlBlk *pPktCtrlBlk)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
ETxnStatus eStatus; /* The Xfer return value (different than this function's return values). */
TI_UINT32 uAc; /* The AC selected for the packet transmission. */
TI_UINT32 uBackpressure = 0;/* HwQueue's indication when the current queue becomes busy. */
ETxHwQueStatus eHwQueStatus;
/* Get an admitted AC corresponding to the packet TID.
* If downgraded due to admission limitation, the TID is downgraded as well.
*/
SELECT_AC_FOR_TID (pTxCtrl, pPktCtrlBlk->tTxDescriptor.tid, uAc);
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsSent[uAc]++;
#endif
/* Call TxHwQueue for Hw resources allocation. */
eHwQueStatus = TWD_txHwQueue_AllocResources (pTxCtrl->hTWD, pPktCtrlBlk);
/* If the used AC can't get more packets, stop it in mgmt-queue module. */
if (eHwQueStatus == TX_HW_QUE_STATUS_STOP_NEXT)
{
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsBackpressure[uAc]++;
TRACE2(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_XmitMgmt(): Backpressure = 0x%x, queue = %d\n", uBackpressure, uAc);
#endif
uBackpressure = 1 << uAc;
pTxCtrl->busyAcBitmap |= uBackpressure; /* Set the busy bit of the current AC. */
txMgmtQ_StopQueue (pTxCtrl->hTxMgmtQ, pTxCtrl->admittedAcToTidMap[uAc]);
}
/* If current packet can't be transmitted due to lack of resources, return with BUSY value. */
else if (eHwQueStatus == TX_HW_QUE_STATUS_STOP_CURRENT)
{
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsBusy[uAc]++;
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_XmitMgmt(): Queue busy - Packet dropped, queue = %d\n", uAc);
#endif
txMgmtQ_StopQueue (pTxCtrl->hTxMgmtQ, pTxCtrl->admittedAcToTidMap[uAc]);
return STATUS_XMIT_BUSY;
}
/* Prepare the packet control-block including the Tx-descriptor. */
txCtrl_BuildMgmtPkt (pTxCtrl, pPktCtrlBlk, uAc);
/* Call the Tx-Xfer to start packet transfer to the FW and return the result. */
eStatus = TWD_txXfer_SendPacket (pTxCtrl->hTWD, pPktCtrlBlk);
if (eStatus == TXN_STATUS_ERROR)
{
#ifdef TI_DBG
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_XmitMgmt(): Xfer Error, Status = %d\n", eStatus);
pTxCtrl->dbgCounters.dbgNumPktsError[uAc]++;
#endif
/* Free the packet resources (packet and CtrlBlk) */
txCtrl_FreePacket (pTxCtrl, pPktCtrlBlk, TI_NOK);
return STATUS_XMIT_ERROR;
}
#ifdef TI_DBG
pTxCtrl->dbgCounters.dbgNumPktsSuccess[uAc]++;
#endif
return STATUS_XMIT_SUCCESS;
}
/***************************************************************************
* txCtrl_UpdateQueuesMapping
****************************************************************************
* DESCRIPTION: This function should be called upon the following events:
* 1) Init
* 2) ACs admission required change (upon association)
* 3) ACs admission state change (upon association and add/delete Tspec).
* It updates the following mappings (by this oredr!):
* 1) Update mapping from requested-AC to highest-admitted-AC.
* 2) Update mapping from actual-AC to requested-TID (for backpressure mapping).
* 3) Update TID-backpressure bitmap, and if changed update data-queue and mgmt-queue.
*
***************************************************************************/
void txCtrl_UpdateQueuesMapping (TI_HANDLE hTxCtrl)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
/* Update mapping from requested-AC to highest-admitted-AC. */
txCtrl_UpdateHighestAdmittedAcTable (pTxCtrl);
/* Update mapping from actual-AC to requested-TID (for backpressure mapping). */
txCtrl_UpdateAcToTidMapping (pTxCtrl);
/* Update TID-backpressure bitmap, and if changed update data-queue and mgmt-queue. */
txCtrl_UpdateBackpressure (pTxCtrl, 0);
}
/***************************************************************************
* txCtrl_AllocPacketBuffer
****************************************************************************
* DESCRIPTION: Allocate a raw buffer for the whole Tx packet.
Used for driver generated packets and when the OAL needs to
copy the packet to a new buffer (e.g. to gather multiple buffers).
***************************************************************************/
void *txCtrl_AllocPacketBuffer (TI_HANDLE hTxCtrl, TTxCtrlBlk *pPktCtrlBlk, TI_UINT32 uPacketLen)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
void *pRawBuf = os_memoryAlloc (pTxCtrl->hOs, uPacketLen);
if (pRawBuf)
{
/* Indicate that the packet is in a raw buffer (i.e. not OS packet) and save its address and length */
pPktCtrlBlk->tTxPktParams.uFlags |= TX_CTRL_FLAG_PKT_IN_RAW_BUF;
/* Save buffer address and length for the free operation */
pPktCtrlBlk->tTxPktParams.pInputPkt = pRawBuf;
pPktCtrlBlk->tTxPktParams.uInputPktLen = uPacketLen;
TRACE2(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_AllocPacketBuffer(): pRawBuf = 0x%x, uPacketLen = %d\n", pRawBuf, uPacketLen);
}
else
{
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_AllocPacketBuffer(): uPacketLen = %d, returning NULL\n", uPacketLen);
}
return pRawBuf;
}
/***************************************************************************
* txCtrl_FreePacket
****************************************************************************
* DESCRIPTION: Free the packet resources, including the packet and the CtrlBlk
***************************************************************************/
void txCtrl_FreePacket (TI_HANDLE hTxCtrl, TTxCtrlBlk *pPktCtrlBlk, TI_STATUS eStatus)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
TRACE3(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_FreePacket(): RawBufFlag = 0x%x, pBuf = 0x%x, Len = %d\n", (pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_PKT_IN_RAW_BUF), pPktCtrlBlk->tTxPktParams.pInputPkt, pPktCtrlBlk->tTxPktParams.uInputPktLen);
/* If the packet is in a raw buffer, free its memory */
if (pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_PKT_IN_RAW_BUF)
{
os_memoryFree (pTxCtrl->hOs,
pPktCtrlBlk->tTxPktParams.pInputPkt,
pPktCtrlBlk->tTxPktParams.uInputPktLen);
}
/* If the original packet is in OS format, call the OAL to free it */
else
{
wlanDrvIf_FreeTxPacket (pTxCtrl->hOs, pPktCtrlBlk, eStatus);
}
/* Free the CtrlBlk */
TWD_txCtrlBlk_Free (pTxCtrl->hTWD, pPktCtrlBlk);
}
/********************************************************************************
* *
* LOCAL FUNCTIONS IMPLEMENTATION *
* *
*********************************************************************************/
/*************************************************************************
* txCtrl_TxCompleteCb *
**************************************************************************
* DESCRIPTION: Called by the TWD upon Tx-complete of one packet.
* Handle packet result:
* - Update counters (statistics and medium-usage)
* - Free the packet resources (Wbuf and CtrlBlk)
*
* INPUT: hTWD - The Tnetw-Driver handle.
* pTxResultInfo - The packet's Tx result information.
*
*************************************************************************/
static void txCtrl_TxCompleteCb (TI_HANDLE hTxCtrl, TxResultDescriptor_t *pTxResultInfo)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
TTxCtrlBlk *pPktCtrlBlk;
TI_UINT32 ac;
TI_BOOL bIsDataPkt;
CL_TRACE_START_L3();
/* Get packet ctrl-block by desc-ID. */
pPktCtrlBlk = TWD_txCtrlBlk_GetPointer (pTxCtrl->hTWD, pTxResultInfo->descID);
ac = WMEQosTagToACTable[pPktCtrlBlk->tTxDescriptor.tid];
#ifdef TI_DBG
/* If the pointed entry is already free, print error and exit (not expected to happen). */
if (pPktCtrlBlk->pNextFreeEntry != NULL)
{
TRACE2(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_TxCompleteCb(): Pkt already free!!, DescID=%d, AC=%d\n", pTxResultInfo->descID, ac);
CL_TRACE_END_L3("tiwlan_drv.ko", "INHERIT", "TX_Cmplt", "");
return;
}
TRACE3(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_TxCompleteCb(): Pkt Tx Complete, DescID=%d, AC=%d, Status=%d\n", pTxResultInfo->descID, ac, pTxResultInfo->status);
#endif
/* Update the TKIP/AES sequence-number according to the Tx data packet security-seq-num. */
/* Note: The FW always provides the last used seq-num so no need to check if the current
packet is data and WEP is on. */
TWD_SetSecuritySeqNum (pTxCtrl->hTWD, pTxResultInfo->lsbSecuritySequenceNumber);
bIsDataPkt = ( (pPktCtrlBlk->tTxPktParams.uPktType == TX_PKT_TYPE_ETHER) ||
(pPktCtrlBlk->tTxPktParams.uPktType == TX_PKT_TYPE_WLAN_DATA) );
#ifdef XCC_MODULE_INCLUDED
/* If it's a XCC link-test packet, call its handler. */
if (pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_LINK_TEST)
{
CL_TRACE_START_L4();
XCCMngr_LinkTestRetriesUpdate (pTxCtrl->hXCCMngr, pTxResultInfo->ackFailures);
CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TX_Cmplt", ".XCCLinkTest");
}
#endif
/* Add the medium usage time for the specific queue. */
pTxCtrl->totalUsedTime[ac] += (TI_UINT32)ENDIAN_HANDLE_WORD(pTxResultInfo->mediumUsage);
/* update TX counters for txDistributer */
{
CL_TRACE_START_L4();
txCtrl_UpdateTxCounters (pTxCtrl, pTxResultInfo, pPktCtrlBlk, ac, bIsDataPkt);
CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TX_Cmplt", ".Cntrs");
}
/* Free the packet resources (packet and CtrlBlk) */
txCtrl_FreePacket (pTxCtrl, pPktCtrlBlk, TI_OK);
CL_TRACE_END_L3("tiwlan_drv.ko", "INHERIT", "TX_Cmplt", "");
}
/***************************************************************************
* txCtrl_BuildDataPktHdr *
****************************************************************************
* DESCRIPTION: this function builds the WLAN header from ethernet format,
* including 802.11-MAC, LLC/SNAP, security padding, alignment padding.
*
* INPUTS: hTxCtrl - the object
* pPktCtrlBlk - data packet control block (Ethernet header)
*
* RETURNS: uHdrAlignPad - Num of bytes (0 or 2) added at the header's beginning for 4-bytes alignment.
***************************************************************************/
TI_UINT32 txCtrl_BuildDataPktHdr (TI_HANDLE hTxCtrl, TTxCtrlBlk *pPktCtrlBlk, AckPolicy_e eAckPolicy)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
TEthernetHeader *pEthHeader;
dot11_header_t *pDot11Header;
Wlan_LlcHeader_T *pWlanSnapHeader;
EHeaderConvertMode eQosMode = pTxCtrl->headerConverMode;
TI_UINT32 uHdrLen = 0;
TI_UINT32 uHdrAlignPad = 0;
TI_UINT16 uQosControl;
TI_UINT16 fc = 0;
TI_UINT16 typeLength;
/*
* Handle encryption if needed, for data or EAPOL (decision was done at RSN):
* - Set WEP bit in header.
* - Add padding for FW security overhead: 4 bytes for TKIP, 8 for AES.
*/
if (( (pPktCtrlBlk->tTxPktParams.uPktType == TX_PKT_TYPE_EAPOL)
&&
pTxCtrl->eapolEncryptionStatus)
||
((pPktCtrlBlk->tTxPktParams.uPktType != TX_PKT_TYPE_EAPOL)
&&
pTxCtrl->currentPrivacyInvokedMode ))
{
fc |= DOT11_FC_WEP;
uHdrLen += pTxCtrl->encryptionFieldSize;
uHdrAlignPad = pTxCtrl->encryptionFieldSize % 4;
}
/*
* Handle QoS if needed:
*/
if (eQosMode == HDR_CONVERT_QOS)
{
uHdrAlignPad = (uHdrAlignPad + HEADER_PAD_SIZE) % 4; /* Add 2 bytes pad at the header beginning for 4 bytes alignment. */
pDot11Header = (dot11_header_t *)&(pPktCtrlBlk->aPktHdr[uHdrAlignPad]);
uHdrLen += WLAN_QOS_HDR_LEN;
/* add empty 4Byte for HT control field set via the FW */
if (pTxCtrl->tTxCtrlHtControl.bHtEnable == TI_TRUE)
{
uHdrLen += WLAN_QOS_HT_CONTROL_FIELD_LEN;
fc |= DOT11_FC_ORDER;
}
/* Set Qos control fields. */
uQosControl = (TI_UINT16)(pPktCtrlBlk->tTxDescriptor.tid);
if ( TI_UNLIKELY(eAckPolicy == ACK_POLICY_NO_ACK) )
uQosControl |= DOT11_QOS_CONTROL_DONT_ACK;
COPY_WLAN_WORD(&pDot11Header->qosControl, &uQosControl); /* copy with endianess handling. */
}
else /* No QoS (legacy header, padding is not needed). */
{
pDot11Header = (dot11_header_t *)&(pPktCtrlBlk->aPktHdr[uHdrAlignPad]);
uHdrLen += WLAN_HDR_LEN;
}
uHdrLen += uHdrAlignPad;
/* Before the header translation the first buf-pointer points to the Ethernet header. */
pEthHeader = (TEthernetHeader *)(pPktCtrlBlk->tTxnStruct.aBuf[0]);
if (TI_UNLIKELY(MAC_MULTICAST(pEthHeader->dst)))
{
pPktCtrlBlk->tTxPktParams.uFlags |= TX_CTRL_FLAG_MULTICAST;
if (MAC_BROADCAST(pEthHeader->dst))
{
pPktCtrlBlk->tTxPktParams.uFlags |= TX_CTRL_FLAG_BROADCAST;
}
}
/* Set MAC header fields for Independent-BSS case. */
if ( TI_UNLIKELY(pTxCtrl->currBssType == BSS_INDEPENDENT) )
{
MAC_COPY (pDot11Header->address1, pEthHeader->dst);
MAC_COPY (pDot11Header->address2, pEthHeader->src);
MAC_COPY (pDot11Header->address3, pTxCtrl->currBssId);
if (eQosMode == HDR_CONVERT_QOS)
fc |= DOT11_FC_DATA_QOS;
else
fc |= DOT11_FC_DATA;
}
/* Set MAC header fields for Infrastructure-BSS case. */
else
{
MAC_COPY (pDot11Header->address1, pTxCtrl->currBssId);
MAC_COPY (pDot11Header->address2, pEthHeader->src);
MAC_COPY (pDot11Header->address3, pEthHeader->dst);
if (eQosMode == HDR_CONVERT_QOS)
fc |= DOT11_FC_DATA_QOS | DOT11_FC_TO_DS;
else
fc |= DOT11_FC_DATA | DOT11_FC_TO_DS;
}
COPY_WLAN_WORD(&pDot11Header->fc, &fc); /* copy with endianess handling. */
/* Set the SNAP header pointer right after the other header parts handled above. */
pWlanSnapHeader = (Wlan_LlcHeader_T *)&(pPktCtrlBlk->aPktHdr[uHdrLen]);
typeLength = HTOWLANS(pEthHeader->type);
/* Detect the packet type and decide if to create a */
/* new SNAP or leave the original LLC. */
/*------------------------------------------------------*/
if( typeLength > ETHERNET_MAX_PAYLOAD_SIZE )
{
/* Create the SNAP Header: */
/*-----------------------------*/
/*
* Make a working copy of the SNAP header
* initialised to zero
*/
pWlanSnapHeader->DSAP = SNAP_CHANNEL_ID;
pWlanSnapHeader->SSAP = SNAP_CHANNEL_ID;
pWlanSnapHeader->Control = LLC_CONTROL_UNNUMBERED_INFORMATION;
/* Check to see if the Ethertype matches anything in the translation */
/* table (Appletalk AARP or DixII/IPX). If so, add the 802.1h */
/* SNAP. */
if(( ETHERTYPE_APPLE_AARP == typeLength ) ||
( ETHERTYPE_DIX_II_IPX == typeLength ))
{
/* Fill out the SNAP Header with 802.1H extention */
pWlanSnapHeader->OUI[0] = SNAP_OUI_802_1H_BYTE0;
pWlanSnapHeader->OUI[1] = SNAP_OUI_802_1H_BYTE1;
pWlanSnapHeader->OUI[2] = SNAP_OUI_802_1H_BYTE2;
}
else
{
/* otherwise, add the RFC1042 SNAP */
pWlanSnapHeader->OUI[0] = SNAP_OUI_RFC1042_BYTE0;
pWlanSnapHeader->OUI[1] = SNAP_OUI_RFC1042_BYTE0;
pWlanSnapHeader->OUI[2] = SNAP_OUI_RFC1042_BYTE0;
}
/* set type length */
pWlanSnapHeader->Type = pEthHeader->type;
/* Add the SNAP length to the total header length. */
uHdrLen += sizeof(Wlan_LlcHeader_T);
}
/* Replace first buffer pointer and length to the descriptor and WLAN-header (instead of Ether header) */
pPktCtrlBlk->tTxnStruct.aBuf[0] = (TI_UINT8 *)&(pPktCtrlBlk->tTxDescriptor);
pPktCtrlBlk->tTxnStruct.aLen[0] = sizeof(TxIfDescriptor_t) + uHdrLen;
pPktCtrlBlk->tTxDescriptor.length += pPktCtrlBlk->tTxnStruct.aLen[0] - ETHERNET_HDR_LEN;
/* Return the number of bytes (0 or 2) added at the header's beginning for 4-bytes alignment. */
return uHdrAlignPad;
}
/***************************************************************************
* txCtrl_BuildDataPkt
****************************************************************************
* DESCRIPTION: Prepare the Data packet control-block including the Tx-descriptor.
***************************************************************************/
static void txCtrl_BuildDataPkt (txCtrl_t *pTxCtrl, TTxCtrlBlk *pPktCtrlBlk,
TI_UINT32 uAc, TI_UINT32 uBackpressure)
{
TI_UINT32 uHdrAlignPad; /* Num of bytes added between Tx-descriptor and header for 4 bytes alignment (0 or 2). */
TI_UINT16 uLastWordPad; /* Num of bytes added at the end of the packet for 4 bytes alignment */
TI_UINT16 uTxDescAttr;
AckPolicy_e eAckPolicy = pTxCtrl->ackPolicy[uAc];
/* Build packet header (including MAC, LLC/SNAP, security padding, header alignment padding). */
uHdrAlignPad = txCtrl_BuildDataPktHdr ((TI_HANDLE)pTxCtrl, pPktCtrlBlk, eAckPolicy);
/* Update packet length in the descriptor according to HW interface requirements */
uLastWordPad = txCtrl_TranslateLengthToFw (pPktCtrlBlk);
/* Set the descriptor attributes */
uTxDescAttr = pTxCtrl->dataPktDescAttrib;
uTxDescAttr |= uLastWordPad << TX_ATTR_OFST_LAST_WORD_PAD;
uTxDescAttr |= pTxCtrl->dataRatePolicy[uAc] << TX_ATTR_OFST_RATE_POLICY;
if (uHdrAlignPad)
{
uTxDescAttr |= TX_ATTR_HEADER_PAD;
}
if (uBackpressure)
{
uTxDescAttr |= TX_ATTR_TX_CMPLT_REQ; /* Request immediate Tx-Complete from FW if the AC is busy */
}
if (TI_UNLIKELY(pTxCtrl->currBssType == BSS_INDEPENDENT) &&
(pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_MULTICAST))
{
/* If packet is Broadcast in IBSS, overwrite rate policy with mgmt value. */
uTxDescAttr &= ~TX_ATTR_RATE_POLICY;
uTxDescAttr |= pTxCtrl->mgmtRatePolicy[uAc] << TX_ATTR_OFST_RATE_POLICY;
}
pPktCtrlBlk->tTxDescriptor.txAttr = ENDIAN_HANDLE_WORD(uTxDescAttr);
/* Translate packet timestamp to FW time (also updates lifeTime and driverHandlingTime) */
txCtrl_TranslateTimeToFw (pTxCtrl, pPktCtrlBlk, pTxCtrl->aMsduLifeTimeTu[uAc]);
/* Indicate that the packet is transfered to the FW, and the descriptor fields are in FW format! */
pPktCtrlBlk->tTxPktParams.uFlags |= TX_CTRL_FLAG_SENT_TO_FW;
#ifdef TI_DBG
pTxCtrl->dbgPktSeqNum++;
pTxCtrl->dbgCounters.dbgNumPktsXfered[uAc]++; /* Count packets sent to Xfer. */
#endif
}
/***************************************************************************
* txCtrl_BuildMgmtPkt
****************************************************************************
* DESCRIPTION: Prepare the Mgmt-Queue packet control-block including the Tx-descriptor.
***************************************************************************/
static void txCtrl_BuildMgmtPkt (txCtrl_t *pTxCtrl, TTxCtrlBlk *pPktCtrlBlk, TI_UINT32 uAc)
{
TI_UINT32 uHdrAlignPad; /* Num of bytes added between Tx-descriptor and header for alignment (0 or 2). */
TI_UINT16 uLastWordPad; /* Num of bytes added at the end of the packet for 4 bytes alignment */
TI_UINT16 uTxDescAttr;
TI_UINT16 uRatePolicy;
TI_UINT8 uPktType = pPktCtrlBlk->tTxPktParams.uPktType;
dot11_header_t *pDot11Header;
/* If EAPOL packet (Ethernet), build header (including MAC,SNAP,security pad & alignment pad). */
if (uPktType == TX_PKT_TYPE_EAPOL)
{
uHdrAlignPad = txCtrl_BuildDataPktHdr ((TI_HANDLE)pTxCtrl, pPktCtrlBlk, ACK_POLICY_LEGACY);
uRatePolicy = pTxCtrl->dataRatePolicy[uAc];
}
/* Other types are already in WLAN format so copy header from Wbuf to Ctrl-Blk. */
else
{
TI_UINT32 uHdrLen = pPktCtrlBlk->tTxnStruct.aLen[0];
TI_UINT32 uHdrLenDelta; /* Add the header pad (2 bytes) and Tx-Descriptor length */
/*
* Update the length fields to include the header pad and the Tx-Descriptor.
* Note: The mgmt-queue provides the header length without the alignment pad, so if
* it's not 4-byte aligned, a 2-bytes pad was added at the header beginning.
*/
uHdrAlignPad = (uHdrLen & ALIGN_4BYTE_MASK) ? HEADER_PAD_SIZE : 0;
uHdrLenDelta = uHdrAlignPad + sizeof(TxIfDescriptor_t);
pPktCtrlBlk->tTxnStruct.aBuf[0] -= uHdrLenDelta;
pPktCtrlBlk->tTxnStruct.aLen[0] += uHdrLenDelta;
pPktCtrlBlk->tTxDescriptor.length += uHdrLenDelta;
uRatePolicy = pTxCtrl->mgmtRatePolicy[uAc];
if (uPktType == TX_PKT_TYPE_WLAN_DATA)
{
/* If QoS mode, update TID in QoS header in case it was downgraded. */
/* Note: Qos-hdr update for EAPOL is done in txCtrl_BuildDataPktHeader() and doesn't exist in mgmt. */
if (pTxCtrl->headerConverMode == HDR_CONVERT_QOS)
{
TI_UINT16 tidWord = (TI_UINT16)pPktCtrlBlk->tTxDescriptor.tid;
pDot11Header = (dot11_header_t *)&(pPktCtrlBlk->aPktHdr[uHdrAlignPad]);
COPY_WLAN_WORD(&pDot11Header->qosControl, &tidWord); /* copy with endianess handling. */
}
}
}
/* Update packet length in the descriptor according to HW interface requirements */
uLastWordPad = txCtrl_TranslateLengthToFw (pPktCtrlBlk);
/* Set fields in the descriptor attributes bitmap. */
uTxDescAttr = uRatePolicy << TX_ATTR_OFST_RATE_POLICY;
uTxDescAttr |= pTxCtrl->txSessionCount << TX_ATTR_OFST_SESSION_COUNTER;
uTxDescAttr |= uLastWordPad << TX_ATTR_OFST_LAST_WORD_PAD;
uTxDescAttr |= TX_ATTR_TX_CMPLT_REQ;
if (uHdrAlignPad)
{
uTxDescAttr |= TX_ATTR_HEADER_PAD;
}
pPktCtrlBlk->tTxDescriptor.txAttr = ENDIAN_HANDLE_WORD(uTxDescAttr);
/* Translate packet timestamp to FW time (also updates lifeTime and driverHandlingTime) */
txCtrl_TranslateTimeToFw (pTxCtrl, pPktCtrlBlk, MGMT_PKT_LIFETIME_TU);
/* Indicate that the packet is transfered to the FW, and the descriptor fields are in FW format! */
pPktCtrlBlk->tTxPktParams.uFlags |= TX_CTRL_FLAG_SENT_TO_FW;
#ifdef TI_DBG
pTxCtrl->dbgPktSeqNum++;
pTxCtrl->dbgCounters.dbgNumPktsXfered[uAc]++; /* Count packets sent to Xfer. */
#endif
}
/***************************************************************************
* txCtrl_UpdateHighestAdmittedAcTable
****************************************************************************
* DESCRIPTION: This function updates the table that provides for each requested AC
* the highest AC that can be currently used, as follows:
* If requested AC is admitted use it.
* If not, find highest AC below it that doesn't require admission.
* This function should be called opon the following events:
* 1) Init
* 2) ACs admission required change (upon association)
* 3) ACs admission state change (upon association and add/delete Tspec).
*
***************************************************************************/
static void txCtrl_UpdateHighestAdmittedAcTable (txCtrl_t *pTxCtrl)
{
int inputIdx;
int outputIdx;
EAcTrfcType inputAc;
EAcTrfcType outputAc;
/* Loop over all ACs in priority order (BE is higher priority than BK). */
for (inputIdx = 0; inputIdx < MAX_NUM_OF_AC; inputIdx++)
{
inputAc = priorityOrderedAc[inputIdx];
/* If input AC is admitted, use it. */
if(pTxCtrl->admissionState[inputAc] == AC_ADMITTED)
pTxCtrl->highestAdmittedAc[inputAc] = inputAc;
/* If input AC is not admitted, find next highest priority AC that doesn't require admission. */
else
{
/* Loop from input AC downward by priority order. */
for (outputIdx = inputIdx; outputIdx >= 0; outputIdx--)
{
outputAc = priorityOrderedAc[outputIdx]; /* Get priority ordered AC. */
/* Break with first (highest) AC that doesn't require admission (we don't want to
* redirect traffic to an AC that requires admission even if admitted for other traffic).
*/
if(pTxCtrl->admissionRequired[outputAc] == ADMISSION_NOT_REQUIRED)
break;
}
/* If we've found a valid AC insert it, else use BE as default. */
if (outputIdx >= 0)
pTxCtrl->highestAdmittedAc[inputAc] = outputAc;
else
pTxCtrl->highestAdmittedAc[inputAc] = QOS_AC_BE;
}
}
}
/***************************************************************************
* txCtrl_UpdateAcToTidMapping
****************************************************************************
* DESCRIPTION: This function updates the table that provides per each AC
* a bitmap of the TIDs that are mapped to it when transitting packets.
* Note that this mapping considers the ACs admission states.
* It is used for mapping ACs backpressure to TIDs (for updating the data/mgmt queues)
*
* This table is updated after txCtrl_UpdateHighestAdmittedAcTable() is called!
* It may also effect the backpressure picture seen by the Data-Queue and
* Mgmt-Queue, so they should be updated subsequently.
*
***************************************************************************/
static void txCtrl_UpdateAcToTidMapping (txCtrl_t *pTxCtrl)
{
TI_UINT32 tid;
EAcTrfcType inputAc;
EAcTrfcType admittedAc;
os_memoryZero(pTxCtrl->hOs, (void *)&(pTxCtrl->admittedAcToTidMap[0]), sizeof(pTxCtrl->admittedAcToTidMap));
/* Loop over all TIDs. */
for (tid = 0; tid < MAX_NUM_OF_802_1d_TAGS; tid++)
{
/* Find the AC that is used for transmitting this TID. */
inputAc = (EAcTrfcType)WMEQosTagToACTable[tid]; /* Standard translation from TID to AC. */
admittedAc = pTxCtrl->highestAdmittedAc[inputAc]; /* The actual AC that is used for Tx. */
/* Set the bit related to the TID in the correlated AC. */
pTxCtrl->admittedAcToTidMap[admittedAc] |= 1 << tid;
}
}
/***************************************************************************
* txCtrl_UpdateBackpressure
****************************************************************************
* DESCRIPTION: This function is called whenever the busy-TIDs bitmap may change,
* (except on packet-xmit - handled separately for performance).
* This includes:
* 1) Init
* 2) ACs admission required change (upon association)
* 3) ACs admission state change (upon association and add/delete Tspec).
* 4) Tx-Complete - provides also freed ACs.
*
* It updates the local bitmap, and the data-queue and mgmt-queue.
*
***************************************************************************/
static void txCtrl_UpdateBackpressure (txCtrl_t *pTxCtrl, TI_UINT32 freedAcBitmap)
{
TI_UINT32 busyAcBitmap = pTxCtrl->busyAcBitmap;
TI_UINT32 busyTidBitmap = 0;
TI_UINT32 ac = 0;
busyAcBitmap &= ~freedAcBitmap; /* Clear backpressure bits of freed ACs. */
pTxCtrl->busyAcBitmap = busyAcBitmap; /* Save new bitmap before manipulating it. */
/* Loop while there are busy ACs. */
while (busyAcBitmap)
{
/* If the AC is busy, add its related TIDs to the total busy TIDs bitmap. */
if (busyAcBitmap & 1)
busyTidBitmap |= pTxCtrl->admittedAcToTidMap[ac];
/* Move to next AC. */
busyAcBitmap = busyAcBitmap >> 1;
ac++;
}
TRACE6(pTxCtrl->hReport, REPORT_SEVERITY_INFORMATION, "txCtrl_UpdateBackpressure(): busyTidBitmap = 0x%x, busyAcBitmap = 0x%x, HighestAdmittedAc[3,2,1,0] = %d, %d, %d, %d\n", busyTidBitmap, pTxCtrl->busyAcBitmap, pTxCtrl->highestAdmittedAc[3], pTxCtrl->highestAdmittedAc[2], pTxCtrl->highestAdmittedAc[1], pTxCtrl->highestAdmittedAc[0]);
/* Save new bitmap and update the data-queue and mgmt-queue. */
pTxCtrl->busyTidBitmap = busyTidBitmap;
txDataQ_UpdateBusyMap (pTxCtrl->hTxDataQ, busyTidBitmap);
txMgmtQ_UpdateBusyMap (pTxCtrl->hTxMgmtQ, busyTidBitmap);
}
/****************************************************************************
* txCtrl_SetTxDelayCounters()
****************************************************************************
* DESCRIPTION: Update transmission path delay counters.
*
* INPUTS: hTxCtrl - the object
* ac - the AC to count delay for
* fwDelay - the time consumed in FW for packet transmission
* driverDelay - the time consumed in driver for packet transmission
*
* OUTPUT:
*
* RETURNS:
****************************************************************************/
#ifdef XCC_MODULE_INCLUDED /* Needed only for XCC-V4 */
static void txCtrl_SetTxDelayCounters (txCtrl_t *pTxCtrl,
TI_UINT32 ac,
TI_UINT32 fwDelay,
TI_UINT32 driverDelay,
TI_UINT32 mediumDelay)
{
int rangeIndex;
TI_UINT32 totalTxDelayUsec = fwDelay + driverDelay;
/* Increment the delay range counter that the current packet Tx delay falls in. */
for (rangeIndex = TX_DELAY_RANGE_MIN; rangeIndex <= TX_DELAY_RANGE_MAX; rangeIndex++)
{
if ( (totalTxDelayUsec >= txDelayRangeStart[rangeIndex]) &&
(totalTxDelayUsec <= txDelayRangeEnd [rangeIndex]) )
{
pTxCtrl->txDataCounters[ac].txDelayHistogram[rangeIndex]++;
break;
}
}
/* Update total delay and FW delay sums and packets number for average delay calculation. */
/* Note: Accumulate Total-Delay in usec to avoid division per packet (convert to msec
only when results are requested by user). */
if (pTxCtrl->SumTotalDelayUs[ac] < 0x7FFFFFFF) /* verify we are not close to the edge. */
{
pTxCtrl->txDataCounters[ac].NumPackets++;
pTxCtrl->SumTotalDelayUs[ac] += totalTxDelayUsec;
pTxCtrl->txDataCounters[ac].SumFWDelayUs += fwDelay;
pTxCtrl->txDataCounters[ac].SumMacDelayUs += mediumDelay;
}
else /* If we get close to overflow, restart average accumulation. */
{
pTxCtrl->txDataCounters[ac].NumPackets = 1;
pTxCtrl->SumTotalDelayUs[ac] = totalTxDelayUsec;
pTxCtrl->txDataCounters[ac].SumFWDelayUs = fwDelay;
pTxCtrl->txDataCounters[ac].SumMacDelayUs = mediumDelay;
}
}
#endif /* XCC_MODULE_INCLUDED */
/***************************************************************************
* txCtrl_UpdateTxCounters
****************************************************************************
* DESCRIPTION: Update Tx statistics counters according to the transmitted packet.
***************************************************************************/
static void txCtrl_UpdateTxCounters (txCtrl_t *pTxCtrl,
TxResultDescriptor_t *pTxResultInfo,
TTxCtrlBlk *pPktCtrlBlk,
TI_UINT32 ac,
TI_BOOL bIsDataPkt)
{
TI_UINT32 pktLen;
TI_UINT32 dataLen;
TI_UINT32 retryHistogramIndex;
TI_UINT16 EventMask = 0;
pktLen = (TI_UINT32)ENDIAN_HANDLE_WORD(pPktCtrlBlk->tTxDescriptor.length);
pktLen = pktLen << 2;
#ifdef TI_DBG
/* update debug counters. */
pTxCtrl->dbgCounters.dbgNumTxCmplt[ac]++;
if (pTxResultInfo->status == TX_SUCCESS)
{
pTxCtrl->dbgCounters.dbgNumTxCmpltOk[ac]++;
pTxCtrl->dbgCounters.dbgNumTxCmpltOkBytes[ac] += pktLen;
}
else
{
pTxCtrl->dbgCounters.dbgNumTxCmpltError[ac]++;
if (pTxResultInfo->status == TX_HW_ERROR ||
pTxResultInfo->status == TX_KEY_NOT_FOUND ||
pTxResultInfo->status == TX_PEER_NOT_FOUND)
{
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_ERROR, "txCtrl_UpdateTxCounters(): TxResult = %d !!!\n", pTxResultInfo->status);
}
else
{
TRACE1(pTxCtrl->hReport, REPORT_SEVERITY_WARNING, "txCtrl_UpdateTxCounters(): TxResult = %d !!!\n", pTxResultInfo->status);
}
}
#endif /* TI_DBG */
/* If it's not a data packet, exit (the formal statistics are only on network stack traffic). */
if ( !bIsDataPkt )
return;
if (pTxResultInfo->status == TX_SUCCESS)
{
/* update the retry histogram */
retryHistogramIndex = (pTxResultInfo->ackFailures >= TX_RETRY_HISTOGRAM_SIZE) ?
(TX_RETRY_HISTOGRAM_SIZE - 1) : pTxResultInfo->ackFailures;
pTxCtrl->txDataCounters[ac].RetryHistogram[retryHistogramIndex]++;
#ifdef XCC_MODULE_INCLUDED
/* update delay histogram */
txCtrl_SetTxDelayCounters (pTxCtrl,
ac,
ENDIAN_HANDLE_LONG(pTxResultInfo->fwHandlingTime),
pPktCtrlBlk->tTxPktParams.uDriverDelay,
ENDIAN_HANDLE_LONG(pTxResultInfo->mediumDelay));
#endif
if (pTxCtrl->headerConverMode == HDR_CONVERT_QOS)
{
dataLen = pktLen - (WLAN_WITH_SNAP_QOS_HEADER_MAX_SIZE - ETHERNET_HDR_LEN);
}
else
{
dataLen = pktLen - (WLAN_WITH_SNAP_HEADER_MAX_SIZE - ETHERNET_HDR_LEN);
}
if (pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_MULTICAST)
{
if (pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_BROADCAST)
{
/* Broadcast frame */
pTxCtrl->txDataCounters[ac].BroadcastFramesXmit++;
pTxCtrl->txDataCounters[ac].BroadcastBytesXmit += dataLen;
EventMask |= BROADCAST_BYTES_XFER;
EventMask |= BROADCAST_FRAMES_XFER;
}
else
{
/* Multicast Address */
pTxCtrl->txDataCounters[ac].MulticastFramesXmit++;
pTxCtrl->txDataCounters[ac].MulticastBytesXmit += dataLen;
EventMask |= MULTICAST_BYTES_XFER;
EventMask |= MULTICAST_FRAMES_XFER;
}
}
else
{
/* Save last data Tx rate for applications' query */
EHwBitRate eHwTxRate = ENDIAN_HANDLE_LONG((EHwBitRate)(pTxResultInfo->rate));
rate_PolicyToDrv (eHwTxRate, &pTxCtrl->eCurrentTxRate);
/* Directed frame statistics */
pTxCtrl->txDataCounters[ac].DirectedFramesXmit++;
pTxCtrl->txDataCounters[ac].DirectedBytesXmit += dataLen;
EventMask |= DIRECTED_BYTES_XFER;
EventMask |= DIRECTED_FRAMES_XFER;
}
pTxCtrl->txDataCounters[ac].XmitOk++;
EventMask |= XFER_OK;
/* update the max consecutive retry failures (if needed) */
if (pTxCtrl->currentConsecutiveRetryFail > pTxCtrl->txDataCounters[ac].MaxConsecutiveRetryFail)
{
pTxCtrl->txDataCounters[ac].MaxConsecutiveRetryFail = pTxCtrl->currentConsecutiveRetryFail;
}
pTxCtrl->currentConsecutiveRetryFail = 0;
if(pTxCtrl->TxEventDistributor)
{
DistributorMgr_EventCall(pTxCtrl->TxEventDistributor, EventMask, dataLen);
}
}
else /* Handle Errors */
{
/*
NOTE: if the FW sets more then 1 error bit at a time change the error handling
code below
*/
if (pTxResultInfo->status == TX_RETRY_EXCEEDED)
{
pTxCtrl->txDataCounters[ac].RetryFailCounter++;
pTxCtrl->currentConsecutiveRetryFail++;
}
else if (pTxResultInfo->status == TX_TIMEOUT)
{
pTxCtrl->txDataCounters[ac].TxTimeoutCounter++;
}
else
{
pTxCtrl->txDataCounters[ac].OtherFailCounter++;
}
}
}
/***************************************************************************
* txCtrl_notifyFwReset *
****************************************************************************
* DESCRIPTION: Go over all CtrlBlk entries and free the active ones including the packet.
***************************************************************************/
TI_STATUS txCtrl_NotifyFwReset (TI_HANDLE hTxCtrl)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
TI_UINT32 entry;
TTxCtrlBlk *pPktCtrlBlk;
pTxCtrl->busyAcBitmap = 0; /* clean busy bitmap */
txCtrl_UpdateBackpressure(pTxCtrl, 0);
for (entry = 0; entry < CTRL_BLK_ENTRIES_NUM-1; entry++)
{
/* Get packet ctrl-block by desc-ID. */
pPktCtrlBlk = TWD_txCtrlBlk_GetPointer(pTxCtrl->hTWD, entry);
if (pPktCtrlBlk->pNextFreeEntry == 0)
{
/* Don't free if the packet still in tx input queues */
if ((pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_SENT_TO_FW))
{
/* Free the packet resources (packet and CtrlBlk) */
txCtrl_FreePacket (pTxCtrl, pPktCtrlBlk, TI_NOK);
}
}
}
return TI_OK;
} /* txCtrl_notifyFwReset */
/***************************************************************************
* txCtrl_CheckForTxStuck *
****************************************************************************
* DESCRIPTION: Check if there are stale packets in the TxCtrlTable.
* The criterion for staleness is function of life time (2 times the longest life time)
* Note that only packets that were not sent to the FW are checked for simplicity!
***************************************************************************/
TI_STATUS txCtrl_CheckForTxStuck (TI_HANDLE hTxCtrl)
{
txCtrl_t *pTxCtrl = (txCtrl_t *)hTxCtrl;
TI_UINT32 entry;
TTxCtrlBlk *pPktCtrlBlk;
TI_UINT32 uPktAge; /* Time in uSec since packet start time. */
for (entry = 0; entry < CTRL_BLK_ENTRIES_NUM-1; entry++)
{
/* Get packet ctrl-block by desc-ID. */
pPktCtrlBlk = TWD_txCtrlBlk_GetPointer(pTxCtrl->hTWD, entry);
/* If entry is in use */
if (pPktCtrlBlk->pNextFreeEntry == 0)
{
/* If the packet wasn't sent to the FW yet (time is in host format) */
if ((pPktCtrlBlk->tTxPktParams.uFlags & TX_CTRL_FLAG_SENT_TO_FW) == 0)
{
/* If packet age is more than twice the maximum lifetime, return NOK */
uPktAge = os_timeStampMs (pTxCtrl->hOs) - pPktCtrlBlk->tTxDescriptor.startTime;
if (uPktAge > ((MGMT_PKT_LIFETIME_TU << SHIFT_BETWEEN_TU_AND_USEC) * 2))
{
return TI_NOK; /* call for recovery */
}
}
}
}
return TI_OK;
} /* txCtrl_FailureTest */