/* * 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); }