| /* |
| * txDataQueue.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. |
| */ |
| |
| |
| /** \file txDataQueue.c |
| * \brief The Tx Data Queues module. |
| * |
| * \see txDataQueue.h |
| */ |
| |
| |
| #define __FILE_ID__ FILE_ID_60 |
| #include "paramOut.h" |
| #include "osApi.h" |
| #include "report.h" |
| #include "timer.h" |
| #include "queue.h" |
| #include "context.h" |
| #include "Ethernet.h" |
| #include "TWDriver.h" |
| #include "DataCtrl_Api.h" |
| #include "txDataQueue.h" |
| #include "txCtrl.h" |
| #include "DrvMainModules.h" |
| #include "bmtrace_api.h" |
| |
| |
| /* Internal Functions prototypes */ |
| static void txDataQ_RunScheduler (TI_HANDLE hTxDataQ); |
| static void txDataQ_UpdateQueuesBusyState (TTxDataQ *pTxDataQ, TI_UINT32 uTidBitMap); |
| static void txDataQ_TxSendPaceTimeout (TI_HANDLE hTxDataQ, TI_BOOL bTwdInitOccured); |
| extern void wlanDrvIf_StopTx (TI_HANDLE hOs); |
| extern void wlanDrvIf_ResumeTx (TI_HANDLE hOs); |
| |
| |
| |
| /*************************************************************************** |
| * PUBLIC FUNCTIONS IMPLEMENTATION * |
| ****************************************************************************/ |
| |
| |
| /** |
| * \fn txDataQ_Create |
| * \brief Create the module and its queues |
| * |
| * Create the Tx Data module and its queues. |
| * |
| * \note |
| * \param hOs - Handle to the Os Abstraction Layer |
| * \return Handle to the allocated Tx Data Queue module (NULL if failed) |
| * \sa |
| */ |
| TI_HANDLE txDataQ_Create(TI_HANDLE hOs) |
| { |
| TTxDataQ *pTxDataQ; |
| |
| /* allocate TxDataQueue module */ |
| pTxDataQ = os_memoryAlloc (hOs, (sizeof(TTxDataQ))); |
| |
| if (!pTxDataQ) |
| { |
| WLAN_OS_REPORT(("Error allocating the TxDataQueue Module\n")); |
| return NULL; |
| } |
| |
| /* Reset TxDataQueue module */ |
| os_memoryZero (hOs, pTxDataQ, (sizeof(TTxDataQ))); |
| |
| return (TI_HANDLE)pTxDataQ; |
| } |
| |
| |
| /** |
| * \fn txDataQ_Init |
| * \brief Save required modules handles |
| * |
| * Save other modules handles. |
| * |
| * \note |
| * \param pStadHandles - The driver modules handles |
| * \return void |
| * \sa |
| */ |
| void txDataQ_Init (TStadHandlesList *pStadHandles) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)(pStadHandles->hTxDataQ); |
| TI_UINT32 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); |
| TI_UINT8 uQueId; |
| |
| /* save modules handles */ |
| pTxDataQ->hContext = pStadHandles->hContext; |
| pTxDataQ->hTxCtrl = pStadHandles->hTxCtrl; |
| pTxDataQ->hOs = pStadHandles->hOs; |
| pTxDataQ->hReport = pStadHandles->hReport; |
| pTxDataQ->hTxMgmtQ = pStadHandles->hTxMgmtQ; |
| pTxDataQ->hTWD = pStadHandles->hTWD; |
| |
| /* Configures the Port Default status to Close */ |
| pTxDataQ->bDataPortEnable = TI_FALSE; |
| |
| /* Configures the LastQueId to zero => scheduler will strart from Queue 1*/ |
| pTxDataQ->uLastQueId = 0; |
| |
| /* init the number of the Data queue to be used */ |
| pTxDataQ->uNumQueues = MAX_NUM_OF_AC; |
| |
| /* init the max size of the Data queues */ |
| pTxDataQ->aQueueMaxSize[QOS_AC_BE] = DATA_QUEUE_DEPTH_BE; |
| pTxDataQ->aQueueMaxSize[QOS_AC_BK] = DATA_QUEUE_DEPTH_BK; |
| pTxDataQ->aQueueMaxSize[QOS_AC_VI] = DATA_QUEUE_DEPTH_VI; |
| pTxDataQ->aQueueMaxSize[QOS_AC_VO] = DATA_QUEUE_DEPTH_VO; |
| |
| /* Create the tx data queues */ |
| for (uQueId = 0; uQueId < pTxDataQ->uNumQueues; uQueId++) |
| { |
| pTxDataQ->aQueues[uQueId] = que_Create (pTxDataQ->hOs, |
| pTxDataQ->hReport, |
| pTxDataQ->aQueueMaxSize[uQueId], |
| uNodeHeaderOffset); |
| |
| /* If any Queues' allocation failed, print error, free TxDataQueue module and exit */ |
| if (pTxDataQ->aQueues[uQueId] == NULL) |
| { |
| TRACE0(pTxDataQ->hReport, REPORT_SEVERITY_CONSOLE , "Failed to create queue\n"); |
| WLAN_OS_REPORT(("Failed to create queue\n")); |
| os_memoryFree (pTxDataQ->hOs, pTxDataQ, sizeof(TTxDataQ)); |
| return; |
| } |
| |
| /* Configure the Queues default values */ |
| pTxDataQ->aQueueBusy[uQueId] = TI_FALSE; |
| pTxDataQ->aNetStackQueueStopped[uQueId] = TI_FALSE; |
| pTxDataQ->aTxSendPaceThresh[uQueId] = 1; |
| } |
| |
| pTxDataQ->hTxSendPaceTimer = tmr_CreateTimer (pStadHandles->hTimer); |
| if (pTxDataQ->hTxSendPaceTimer == NULL) |
| { |
| TRACE0(pTxDataQ->hReport, REPORT_SEVERITY_ERROR, "txDataQ_Init(): Failed to create hTxSendPaceTimer!\n"); |
| return; |
| } |
| |
| /* Register to the context engine and get the client ID */ |
| pTxDataQ->uContextId = context_RegisterClient (pTxDataQ->hContext, |
| txDataQ_RunScheduler, |
| (TI_HANDLE)pTxDataQ, |
| TI_TRUE, |
| "TX_DATA", |
| sizeof("TX_DATA")); |
| } |
| |
| |
| /** |
| * \fn txDataQ_SetDefaults |
| * \brief Configure module with default settings |
| * |
| * Init the Tx Data queues. |
| * Register as the context-engine client. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \param Other modules handles |
| * \return TI_OK on success or TI_NOK on failure |
| * \sa |
| */ |
| TI_STATUS txDataQ_SetDefaults (TI_HANDLE hTxDataQ, txDataInitParams_t *pTxDataInitParams) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TI_STATUS eStatus; |
| |
| /* configure the classifier sub-module */ |
| eStatus = txDataClsfr_Config (hTxDataQ, &pTxDataInitParams->ClsfrInitParam); |
| if (eStatus != TI_OK) |
| { |
| TRACE0(pTxDataQ->hReport, REPORT_SEVERITY_CONSOLE ,"FATAL ERROR: txDataQ_SetDefaults(): txDataClsfr_Config failed - Aborting\n"); |
| WLAN_OS_REPORT(("FATAL ERROR: txDataQ_SetDefaults(): txDataClsfr_Config failed - Aborting\n")); |
| return eStatus; |
| } |
| |
| /* Save the module's parameters settings */ |
| pTxDataQ->bStopNetStackTx = pTxDataInitParams->bStopNetStackTx; |
| pTxDataQ->aTxSendPaceThresh[QOS_AC_BE] = pTxDataInitParams->uTxSendPaceThresh; |
| pTxDataQ->aTxSendPaceThresh[QOS_AC_BK] = pTxDataInitParams->uTxSendPaceThresh; |
| pTxDataQ->aTxSendPaceThresh[QOS_AC_VI] = pTxDataInitParams->uTxSendPaceThresh; |
| pTxDataQ->aTxSendPaceThresh[QOS_AC_VO] = 1; /* Don't delay voice packts! */ |
| |
| TRACE0(pTxDataQ->hReport, REPORT_SEVERITY_INIT, ".....Tx Data Queue configured successfully\n"); |
| |
| return TI_OK; |
| } |
| |
| |
| /** |
| * \fn txDataQ_Destroy |
| * \brief Destroy the module and its queues |
| * |
| * Clear and destroy the queues and then destroy the module object. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return TI_OK - Unload succesfull, TI_NOK - Unload unsuccesfull |
| * \sa |
| */ |
| TI_STATUS txDataQ_Destroy (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TI_STATUS status = TI_OK; |
| TI_UINT32 uQueId; |
| |
| /* Dequeue and free all queued packets */ |
| txDataQ_ClearQueues (hTxDataQ); |
| |
| /* Free Data queues */ |
| for (uQueId = 0 ; uQueId < pTxDataQ->uNumQueues ; uQueId++) |
| { |
| if (que_Destroy(pTxDataQ->aQueues[uQueId]) != TI_OK) |
| { |
| TRACE1(pTxDataQ->hReport, REPORT_SEVERITY_ERROR, "txDataQueue_unLoad: fail to free Data Queue number: %d\n",uQueId); |
| status = TI_NOK; |
| } |
| } |
| |
| /* free timer */ |
| if (pTxDataQ->hTxSendPaceTimer) |
| { |
| tmr_DestroyTimer (pTxDataQ->hTxSendPaceTimer); |
| } |
| |
| /* Free Tx Data Queue Module */ |
| os_memoryFree (pTxDataQ->hOs, pTxDataQ, sizeof(TTxDataQ)); |
| |
| return status; |
| } |
| |
| |
| /** |
| * \fn txDataQ_ClearQueues |
| * \brief Clear all queues |
| * |
| * Dequeue and free all queued packets. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa |
| */ |
| void txDataQ_ClearQueues (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TTxCtrlBlk *pPktCtrlBlk; |
| TI_UINT32 uQueId; |
| |
| /* Dequeue and free all queued packets */ |
| for (uQueId = 0 ; uQueId < pTxDataQ->uNumQueues ; uQueId++) |
| { |
| do { |
| context_EnterCriticalSection (pTxDataQ->hContext); |
| pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue(pTxDataQ->aQueues[uQueId]); |
| context_LeaveCriticalSection (pTxDataQ->hContext); |
| if (pPktCtrlBlk != NULL) { |
| txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); |
| } |
| } while (pPktCtrlBlk != NULL); |
| } |
| } |
| |
| |
| /** |
| * \fn txDataQ_InsertPacket |
| * \brief Insert packet in queue and schedule task |
| * |
| * This function is called by the hard_start_xmit() callback function. |
| * If the packet it an EAPOL, forward it to the Mgmt-Queue. |
| * Otherwise, classify the packet, enqueue it and request |
| * context switch for handling it in the driver's context. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \param pPktCtrlBlk - Pointer to the packet |
| * \param uPacketDtag - The packet priority optionaly set by the OAL |
| * \return TI_OK - if the packet was queued, TI_NOK - if the packet was dropped. |
| * \sa txDataQ_Run |
| */ |
| TI_STATUS txDataQ_InsertPacket (TI_HANDLE hTxDataQ, TTxCtrlBlk *pPktCtrlBlk, TI_UINT8 uPacketDtag) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TEthernetHeader *pEthHead = (TEthernetHeader *)(pPktCtrlBlk->tTxnStruct.aBuf[0]); |
| TI_STATUS eStatus; |
| TI_UINT32 uQueId; |
| TI_UINT32 uQueSize; |
| txCtrl_t *pTxCtrl = (txCtrl_t *)(pTxDataQ->hTxCtrl); |
| TI_BOOL bRequestSchedule = TI_FALSE; |
| TI_BOOL bStopNetStack = TI_FALSE; |
| CL_TRACE_START_L3(); |
| |
| /* If packet is EAPOL or from the generic Ethertype, forward it to the Mgmt-Queue and exit */ |
| if ((HTOWLANS(pEthHead->type) == ETHERTYPE_EAPOL) || |
| (HTOWLANS(pEthHead->type) == pTxCtrl->genericEthertype)) |
| { |
| pPktCtrlBlk->tTxPktParams.uPktType = TX_PKT_TYPE_EAPOL; |
| |
| return txMgmtQ_Xmit (pTxDataQ->hTxMgmtQ, pPktCtrlBlk, TI_TRUE); |
| /* Note: The last parameter indicates that we are running in external context */ |
| } |
| |
| pPktCtrlBlk->tTxPktParams.uPktType = TX_PKT_TYPE_ETHER; |
| |
| /* Enter critical section to protect classifier data and queue access */ |
| context_EnterCriticalSection (pTxDataQ->hContext); |
| |
| /* Call the Classify function to set the TID field */ |
| if (txDataClsfr_ClassifyTxPacket (hTxDataQ, pPktCtrlBlk, uPacketDtag) != TI_OK) |
| { |
| #ifdef TI_DBG |
| pTxDataQ->uClsfrMismatchCount++; |
| TRACE0(pTxDataQ->hReport, REPORT_SEVERITY_WARNING, "txDataQueue_xmit: No matching classifier found \n"); |
| #endif /* TI_DBG */ |
| } |
| |
| /* Enqueue the packet in the appropriate Queue */ |
| uQueId = aTidToQueueTable[pPktCtrlBlk->tTxDescriptor.tid]; |
| eStatus = que_Enqueue (pTxDataQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); |
| |
| /* Get number of packets in current queue */ |
| uQueSize = que_Size (pTxDataQ->aQueues[uQueId]); |
| |
| /* If the current queue is not stopped */ |
| if (pTxDataQ->aQueueBusy[uQueId] == TI_FALSE) |
| { |
| /* If the queue has the desired number of packets, request switch to driver context for handling them */ |
| if (uQueSize == pTxDataQ->aTxSendPaceThresh[uQueId]) |
| { |
| tmr_StopTimer (pTxDataQ->hTxSendPaceTimer); |
| bRequestSchedule = TI_TRUE; |
| } |
| /* If below Tx-Send pacing threshold, start timer to trigger packets handling if expired */ |
| else if (uQueSize < pTxDataQ->aTxSendPaceThresh[uQueId]) |
| { |
| tmr_StartTimer (pTxDataQ->hTxSendPaceTimer, |
| txDataQ_TxSendPaceTimeout, |
| hTxDataQ, |
| TX_SEND_PACE_TIMEOUT_MSEC, |
| TI_FALSE); |
| } |
| } |
| |
| /* If allowed to stop network stack and the queue is full, indicate to stop network and |
| to schedule Tx handling (both are executed below, outside the critical section!) */ |
| if ((pTxDataQ->bStopNetStackTx) && (uQueSize == pTxDataQ->aQueueMaxSize[uQueId])) |
| { |
| pTxDataQ->aNetStackQueueStopped[uQueId] = TI_TRUE; |
| bRequestSchedule = TI_TRUE; |
| bStopNetStack = TI_TRUE; |
| } |
| |
| /* Leave critical section */ |
| context_LeaveCriticalSection (pTxDataQ->hContext); |
| |
| /* If needed, schedule Tx handling */ |
| if (bRequestSchedule) |
| { |
| context_RequestSchedule (pTxDataQ->hContext, pTxDataQ->uContextId); |
| } |
| |
| /* If needed, stop the network stack Tx */ |
| if (bStopNetStack) |
| { |
| /* Stop the network stack from sending Tx packets as we have at least one date queue full. |
| Note that in some of the OS's (e.g Win Mobile) it is implemented by blocking the thread*/ |
| wlanDrvIf_StopTx (pTxDataQ->hOs); |
| } |
| |
| if (eStatus != TI_OK) |
| { |
| /* If the packet can't be queued drop it */ |
| txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uDroppedPacket++; |
| #endif /* TI_DBG */ |
| } |
| else |
| { |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uEnqueuePacket++; |
| #endif /* TI_DBG */ |
| } |
| |
| CL_TRACE_END_L3 ("tiwlan_drv.ko", "INHERIT", "TX", ""); |
| |
| return eStatus; |
| } |
| |
| |
| /** |
| * \fn txDataQ_StopQueue |
| * \brief Set queue's busy indication |
| * |
| * This function is called by the txCtrl_xmitData() if the queue's backpressure |
| * indication is set. |
| * It sets the internal queue's Busy indication. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \param uTidBitMap - The changed TIDs busy bitmap |
| * \return void |
| * \sa txDataQ_UpdateBusyMap |
| */ |
| void txDataQ_StopQueue (TI_HANDLE hTxDataQ, TI_UINT32 uTidBitMap) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| /* Set the relevant queue(s) busy flag */ |
| txDataQ_UpdateQueuesBusyState (pTxDataQ, uTidBitMap); |
| } |
| |
| |
| /** |
| * \fn txDataQ_UpdateBusyMap |
| * \brief Set queue's busy indication |
| * |
| * This function is called by the txCtrl if the backpressure map per TID is changed. |
| * This could be as a result of Tx-Complete, admission change or association. |
| * The function modifies the internal queue's Busy indication and calls the scheduler. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \param uTidBitMap - The changed TIDs busy bitmap |
| * \return void |
| * \sa txDataQ_StopQueue |
| */ |
| void txDataQ_UpdateBusyMap (TI_HANDLE hTxDataQ, TI_UINT32 tidBitMap) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| /* Update the Queue(s) mode */ |
| txDataQ_UpdateQueuesBusyState (pTxDataQ, tidBitMap); |
| |
| /* Run the scheduler */ |
| txDataQ_RunScheduler (hTxDataQ); |
| } |
| |
| |
| /** |
| * \fn txDataQ_StopAll |
| * \brief Disable Data-Queue module access to Tx path. |
| * |
| * Called by the Tx-Port when the data-queue module can't access the Tx path. |
| * Sets stop-all-queues indication. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa txDataQ_WakeAll |
| */ |
| void txDataQ_StopAll (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| /* Disable the data Tx port */ |
| pTxDataQ->bDataPortEnable = TI_FALSE; |
| } |
| |
| |
| /** |
| * \fn txDataQ_WakeAll |
| * \brief Enable Data-Queue module access to Tx path. |
| * |
| * Called by the Tx-Port when the data-queue module can access the Tx path. |
| * Clears the stop-all-queues indication and calls the scheduler. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa txDataQ_StopAll |
| */ |
| void txDataQ_WakeAll (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| /* Enable the data Tx port */ |
| pTxDataQ->bDataPortEnable = TI_TRUE; |
| |
| /* Run the scheduler */ |
| txDataQ_RunScheduler (hTxDataQ); |
| } |
| |
| |
| /*************************************************************************** |
| * DEBUG FUNCTIONS IMPLEMENTATION * |
| ****************************************************************************/ |
| |
| #ifdef TI_DBG |
| |
| /** |
| * \fn txDataQ_PrintModuleParams |
| * \brief Print Module Parameters |
| * |
| * Print Module Parameters |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa |
| */ |
| void txDataQ_PrintModuleParams (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TI_UINT32 qIndex; |
| |
| WLAN_OS_REPORT(("--------- txDataQueue_printModuleParams ----------\n\n")); |
| |
| WLAN_OS_REPORT(("bStopNetStackTx = %d\n",pTxDataQ->bStopNetStackTx)); |
| WLAN_OS_REPORT(("bDataPortEnable = %d\n",pTxDataQ->bDataPortEnable)); |
| WLAN_OS_REPORT(("uNumQueues = %d\n",pTxDataQ->uNumQueues)); |
| WLAN_OS_REPORT(("uLastQueId = %d\n",pTxDataQ->uLastQueId)); |
| WLAN_OS_REPORT(("uContextId = %d\n",pTxDataQ->uContextId)); |
| |
| for (qIndex = 0; qIndex < pTxDataQ->uNumQueues; qIndex++) |
| { |
| WLAN_OS_REPORT(("aQueueBusy[%d] = %d\n", qIndex, pTxDataQ->aQueueBusy[qIndex])); |
| } |
| for (qIndex = 0; qIndex < pTxDataQ->uNumQueues; qIndex++) |
| { |
| WLAN_OS_REPORT(("aQueueMaxSize[%d] = %d\n", qIndex, pTxDataQ->aQueueMaxSize[qIndex])); |
| } |
| for (qIndex = 0; qIndex < pTxDataQ->uNumQueues; qIndex++) |
| { |
| WLAN_OS_REPORT(("aTxSendPaceThresh[%d] = %d\n", qIndex, pTxDataQ->aTxSendPaceThresh[qIndex])); |
| } |
| for (qIndex = 0; qIndex < pTxDataQ->uNumQueues; qIndex++) |
| { |
| WLAN_OS_REPORT(("aNetStackQueueStopped[%d] = %d\n", qIndex, pTxDataQ->aNetStackQueueStopped[qIndex])); |
| } |
| |
| WLAN_OS_REPORT(("-------------- Queues Info -----------------------\n")); |
| for (qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| { |
| WLAN_OS_REPORT(("Que %d:\n", qIndex)); |
| que_Print (pTxDataQ->aQueues[qIndex]); |
| } |
| |
| WLAN_OS_REPORT(("--------------------------------------------------\n\n")); |
| } |
| |
| |
| /** |
| * \fn txDataQ_PrintQueueStatistics |
| * \brief Print queues statistics |
| * |
| * Print queues statistics |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa |
| */ |
| void txDataQ_PrintQueueStatistics (TI_HANDLE hTxDataQ) |
| { |
| #ifdef REPORT_LOG |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TI_UINT32 qIndex; |
| |
| WLAN_OS_REPORT(("-------------- txDataQueue_printStatistics -------\n\n")); |
| |
| WLAN_OS_REPORT(("uClsfrMismatchCount = %d\n",pTxDataQ->uClsfrMismatchCount)); |
| WLAN_OS_REPORT(("uTxSendPaceTimeoutsCount = %d\n",pTxDataQ->uTxSendPaceTimeoutsCount)); |
| |
| WLAN_OS_REPORT(("-------------- Enqueue to queues -----------------\n")); |
| for(qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| WLAN_OS_REPORT(("Que[%d]: = %d\n",qIndex, pTxDataQ->aQueueCounters[qIndex].uEnqueuePacket)); |
| |
| WLAN_OS_REPORT(("-------------- Dequeue from queues ---------------\n")); |
| for(qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| WLAN_OS_REPORT(("Que[%d]: = %d\n",qIndex, pTxDataQ->aQueueCounters[qIndex].uDequeuePacket)); |
| |
| WLAN_OS_REPORT(("-------------- Requeue to queues -----------------\n")); |
| for(qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| WLAN_OS_REPORT(("Que[%d]: = %d\n",qIndex, pTxDataQ->aQueueCounters[qIndex].uRequeuePacket)); |
| |
| WLAN_OS_REPORT(("-------------- Sent to TxCtrl --------------------\n")); |
| for(qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| WLAN_OS_REPORT(("Que[%d]: = %d\n",qIndex, pTxDataQ->aQueueCounters[qIndex].uXmittedPacket)); |
| |
| WLAN_OS_REPORT(("-------------- Dropped - Queue Full --------------\n")); |
| for(qIndex = 0; qIndex < MAX_NUM_OF_AC; qIndex++) |
| WLAN_OS_REPORT(("Que[%d]: = %d\n",qIndex, pTxDataQ->aQueueCounters[qIndex].uDroppedPacket)); |
| |
| WLAN_OS_REPORT(("--------------------------------------------------\n\n")); |
| #endif |
| } |
| |
| |
| /** |
| * \fn txDataQ_ResetQueueStatistics |
| * \brief Reset queues statistics |
| * |
| * Reset queues statistics |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa |
| */ |
| void txDataQ_ResetQueueStatistics (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| os_memoryZero(pTxDataQ->hOs, &pTxDataQ->aQueueCounters, sizeof(pTxDataQ->aQueueCounters)); |
| pTxDataQ->uTxSendPaceTimeoutsCount = 0; |
| } |
| |
| |
| #endif /* TI_DBG */ |
| |
| |
| |
| /*************************************************************************** |
| * INTERNAL FUNCTIONS IMPLEMENTATION * |
| ****************************************************************************/ |
| |
| |
| /** |
| * \fn txDataQ_RunScheduler |
| * \brief The module's Tx scheduler |
| * |
| * This function is the Data-Queue scheduler. |
| * It selects a packet to transmit from the tx queues and sends it to the TxCtrl. |
| * The queues are selected in a round-robin order. |
| * The function is called by one of: |
| * txDataQ_Run() |
| * txDataQ_UpdateBusyMap() |
| * txDataQ_WakeAll() |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \return void |
| * \sa |
| */ |
| static void txDataQ_RunScheduler (TI_HANDLE hTxDataQ) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| TI_UINT32 uIdleIterationsCount = 0; /* Count iterations without packet transmission (for exit criteria) */ |
| TI_UINT32 uQueId = pTxDataQ->uLastQueId; /* The last iteration queue */ |
| EStatusXmit eStatus; /* The return status of the txCtrl_xmitData function */ |
| TTxCtrlBlk *pPktCtrlBlk; /* Pointer to the packet to be dequeued and sent */ |
| |
| while(1) |
| { |
| /* If the Data port is closed or the scheduler couldn't send packets from |
| all queues, indicate end of current packets burst and exit */ |
| if ( !pTxDataQ->bDataPortEnable || (uIdleIterationsCount >= pTxDataQ->uNumQueues) ) |
| { |
| TWD_txXfer_EndOfBurst (pTxDataQ->hTWD); |
| return; |
| } |
| |
| /* Selecting the next queue */ |
| uQueId++; |
| if (uQueId == pTxDataQ->uNumQueues) |
| { |
| uQueId = 0; |
| } |
| pTxDataQ->uLastQueId = uQueId; |
| |
| /* Increment the idle iterations counter */ |
| uIdleIterationsCount++; |
| |
| /* If the queue is busy (AC is full), continue to next queue. */ |
| if (pTxDataQ->aQueueBusy[uQueId]) |
| { |
| continue; |
| } |
| |
| /* Dequeue a packet in a critical section */ |
| context_EnterCriticalSection (pTxDataQ->hContext); |
| pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pTxDataQ->aQueues[uQueId]); |
| context_LeaveCriticalSection (pTxDataQ->hContext); |
| |
| /* If the queue was empty, continue to the next queue */ |
| if (pPktCtrlBlk == NULL) |
| { |
| if ((pTxDataQ->bStopNetStackTx) && pTxDataQ->aNetStackQueueStopped[uQueId]) |
| { |
| pTxDataQ->aNetStackQueueStopped[uQueId] = TI_FALSE; |
| /*Resume the TX process as our date queues are empty*/ |
| wlanDrvIf_ResumeTx (pTxDataQ->hOs); |
| } |
| |
| continue; |
| } |
| |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uDequeuePacket++; |
| #endif /* TI_DBG */ |
| |
| /* Send the packet */ |
| eStatus = txCtrl_XmitData (pTxDataQ->hTxCtrl, pPktCtrlBlk); |
| |
| /* |
| * If the return status is busy it means that the packet was not sent |
| * so we need to requeue it for future try. |
| */ |
| if(eStatus == STATUS_XMIT_BUSY) |
| { |
| TI_STATUS eQueStatus; |
| |
| /* Requeue the packet in a critical section */ |
| context_EnterCriticalSection (pTxDataQ->hContext); |
| eQueStatus = que_Requeue (pTxDataQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); |
| if (eQueStatus != TI_OK) |
| { |
| /* If the packet can't be queued drop it */ |
| /* Note: may happen only if this thread was preempted between the |
| dequeue and requeue and new packets were inserted into this quque */ |
| txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uDroppedPacket++; |
| #endif /* TI_DBG */ |
| } |
| context_LeaveCriticalSection (pTxDataQ->hContext); |
| |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uRequeuePacket++; |
| #endif /* TI_DBG */ |
| |
| continue; |
| } |
| |
| /* If we reach this point, a packet was sent successfully so reset the idle iterations counter. */ |
| uIdleIterationsCount = 0; |
| |
| #ifdef TI_DBG |
| pTxDataQ->aQueueCounters[uQueId].uXmittedPacket++; |
| #endif /* TI_DBG */ |
| |
| } /* End of while */ |
| |
| /* Unreachable code */ |
| } |
| |
| |
| /** |
| * \fn txDataQ_UpdateQueuesBusyState |
| * \brief Update queues' busy state |
| * |
| * Update the Queues Mode to Busy according to the input TidBitMap. |
| * Each Tid that is set indicates that the related Queue is Busy. |
| * |
| * \note |
| * \param hTxDataQ - The object |
| * \param uTidBitMap - The changed TIDs busy bitmap |
| * \return void |
| * \sa |
| */ |
| static void txDataQ_UpdateQueuesBusyState (TTxDataQ *pTxDataQ, TI_UINT32 uTidBitMap) |
| { |
| TI_UINT32 uTidIdx; |
| |
| /* Go over the TidBitMap and update the related queue busy state */ |
| for (uTidIdx = 0; uTidIdx < MAX_NUM_OF_802_1d_TAGS; uTidIdx++, uTidBitMap >>= 1) |
| { |
| if (uTidBitMap & 0x1) /* this Tid is busy */ |
| { |
| pTxDataQ->aQueueBusy[aTidToQueueTable[uTidIdx]] = TI_TRUE; |
| } |
| else |
| { |
| pTxDataQ->aQueueBusy[aTidToQueueTable[uTidIdx]] = TI_FALSE; |
| } |
| } |
| } |
| |
| |
| /* |
| * \brief Handle Tx-Send-Pacing timeout. |
| * |
| * \param hTxDataQ - Module handle |
| * \param bTwdInitOccured - Indicate if TWD restart (recovery) occured |
| * \return void |
| * |
| * \par Description |
| * Call the Tx scheduler to handle the queued packets. |
| * |
| * \sa |
| */ |
| static void txDataQ_TxSendPaceTimeout (TI_HANDLE hTxDataQ, TI_BOOL bTwdInitOccured) |
| { |
| TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; |
| |
| pTxDataQ->uTxSendPaceTimeoutsCount++; |
| |
| txDataQ_RunScheduler (hTxDataQ); |
| } |