/* * txMgmtQueue.c * * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Texas Instruments nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** \file txMgmtQueue.c * \brief The Tx Mgmt Queues module. * * DESCRIPTION: * ============ * The Management-Queues module is responsible for the following tasks: * 1. Queue the driver generated Tx packets, including management, * EAPOL and null packets until they are transmitted. * The management packets are buffered in the management-queue, * and the others in the EAPOL-queue. * 2. Maintain a state machine that follows the queues state and * the connection states and enables specific transmission types * accordingly (e.g. only management). * 3. Gain access to the Tx path when the management queues are not * empty, and return the access to the data queues when the * management queues are empty. * 4. Schedule packets transmission with strict priority of the * management queue over the EAPOL queue, and according to the * backpressure controls from the Port (all queues) and from the * Tx-Ctrl (per queue). * * \see txMgmtQueue.h */ #define __FILE_ID__ FILE_ID_61 #include "tidef.h" #include "paramOut.h" #include "osApi.h" #include "TWDriver.h" #include "DataCtrl_Api.h" #include "report.h" #include "queue.h" #include "context.h" #include "DrvMainModules.h" #define MGMT_QUEUES_TID MAX_USER_PRIORITY typedef enum { QUEUE_TYPE_MGMT, /* Mgmt-queue - high-priority, for mgmt packets only. */ QUEUE_TYPE_EAPOL, /* EAPOL-queue - low-priority, for other internal packets (EAPOL, NULL, IAPP). */ NUM_OF_MGMT_QUEUES } EMgmtQueueTypes; /* State-Machine Events */ typedef enum { SM_EVENT_CLOSE, /* All Tx types should be closed. */ SM_EVENT_MGMT, /* Allow only mgmt packets. */ SM_EVENT_EAPOL, /* Allow mgmt and EAPOL packets. */ SM_EVENT_OPEN, /* Allow all packets. */ SM_EVENT_QUEUES_EMPTY, /* Mgmt-aQueues are now both empty. */ SM_EVENT_QUEUES_NOT_EMPTY /* At least one of the Mgmt-aQueues is now not empty. */ } ESmEvent; /* State-Machine States */ typedef enum { SM_STATE_CLOSE, /* All Tx path is closed. */ SM_STATE_MGMT, /* Only mgmt Tx is permitted. */ SM_STATE_EAPOL, /* Only mgmt and EAPOL Tx is permitted. */ SM_STATE_OPEN_MGMT, /* All Tx permitted and Mgmt aQueues are currently active (date disabled). */ SM_STATE_OPEN_DATA /* All Tx permitted and Data aQueues are currently active (mgmt disabled). */ } ESmState; /* State-Machine Actions */ typedef enum { SM_ACTION_NULL, SM_ACTION_ENABLE_DATA, SM_ACTION_ENABLE_MGMT, SM_ACTION_RUN_SCHEDULER } ESmAction; /* TI_TRUE if both aQueues are empty. */ #define ARE_ALL_MGMT_QUEUES_EMPTY(aQueues) ( (que_Size(aQueues[QUEUE_TYPE_MGMT] ) == 0) && \ (que_Size(aQueues[QUEUE_TYPE_EAPOL]) == 0) ) typedef struct { TI_UINT32 aEnqueuePackets[NUM_OF_MGMT_QUEUES]; TI_UINT32 aDequeuePackets[NUM_OF_MGMT_QUEUES]; TI_UINT32 aRequeuePackets[NUM_OF_MGMT_QUEUES]; TI_UINT32 aDroppedPackets[NUM_OF_MGMT_QUEUES]; TI_UINT32 aXmittedPackets[NUM_OF_MGMT_QUEUES]; } TDbgCount; /* The module object. */ typedef struct { /* Handles */ TI_HANDLE hOs; TI_HANDLE hReport; TI_HANDLE hTxCtrl; TI_HANDLE hTxPort; TI_HANDLE hContext; TI_HANDLE hTWD; TI_BOOL bMgmtPortEnable;/* Port open for mgmt-aQueues or not. */ ESmState eSmState; /* The current state of the SM. */ ETxConnState eTxConnState; /* See typedef in module API. */ TI_UINT32 uContextId; /* ID allocated to this module on registration to context module */ /* Mgmt aQueues */ TI_HANDLE aQueues[NUM_OF_MGMT_QUEUES]; /* The mgmt-aQueues handles. */ TI_BOOL aQueueBusy[NUM_OF_MGMT_QUEUES]; /* Related AC is busy. */ TI_BOOL aQueueEnabledBySM[NUM_OF_MGMT_QUEUES]; /* Queue is enabled by the SM. */ /* Debug Counters */ TDbgCount tDbgCounters; /* Save Tx statistics per mgmt-queue. */ } TTxMgmtQ; /* The module internal functions */ static void mgmtQueuesSM (TTxMgmtQ *pTxMgmtQ, ESmEvent smEvent); static void runSchedulerNotFromSm (TTxMgmtQ *pTxMgmtQ); static void runScheduler (TTxMgmtQ *pTxMgmtQ); static void updateQueuesBusyMap (TTxMgmtQ *pTxMgmtQ, TI_UINT32 tidBitMap); /******************************************************************************* * PUBLIC FUNCTIONS IMPLEMENTATION * ********************************************************************************/ /** * \fn txMgmtQ_Create * \brief Create the module and its queues * * Create the Tx Mgmt Queue module and its queues. * * \note * \param hOs - Handle to the Os Abstraction Layer * \return Handle to the allocated Tx Mgmt Queue module (NULL if failed) * \sa */ TI_HANDLE txMgmtQ_Create (TI_HANDLE hOs) { TTxMgmtQ *pTxMgmtQ; /* allocate TxMgmtQueue module */ pTxMgmtQ = os_memoryAlloc (hOs, (sizeof(TTxMgmtQ))); if(!pTxMgmtQ) { WLAN_OS_REPORT(("Error allocating the TxMgmtQueue Module\n")); return NULL; } /* Reset TxMgmtQueue module */ os_memoryZero (hOs, pTxMgmtQ, (sizeof(TTxMgmtQ))); return (TI_HANDLE)pTxMgmtQ; } /** * \fn txMgmtQ_Init * \brief Configure module with default settings * * Get other modules handles. * Init the Tx Mgmt queues. * Register as the context-engine client. * * \note * \param pStadHandles - The driver modules handles * \return void * \sa */ void txMgmtQ_Init (TStadHandlesList *pStadHandles) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)(pStadHandles->hTxMgmtQ); TI_UINT32 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); int uQueId; /* configure modules handles */ pTxMgmtQ->hOs = pStadHandles->hOs; pTxMgmtQ->hReport = pStadHandles->hReport; pTxMgmtQ->hTxCtrl = pStadHandles->hTxCtrl; pTxMgmtQ->hTxPort = pStadHandles->hTxPort; pTxMgmtQ->hContext = pStadHandles->hContext; pTxMgmtQ->hTWD = pStadHandles->hTWD; pTxMgmtQ->bMgmtPortEnable = TI_TRUE; /* Port Default status is open (data-queues are disabled). */ pTxMgmtQ->eSmState = SM_STATE_CLOSE; /* SM default state is CLOSE. */ pTxMgmtQ->eTxConnState = TX_CONN_STATE_CLOSE; /* initialize tx Mgmt queues */ for (uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) { pTxMgmtQ->aQueues[uQueId] = que_Create (pTxMgmtQ->hOs, pTxMgmtQ->hReport, MGMT_QUEUES_DEPTH, uNodeHeaderOffset); /* If any Queues' allocation failed, print error, free TxMgmtQueue module and exit */ if (pTxMgmtQ->aQueues[uQueId] == NULL) { TRACE0(pTxMgmtQ->hReport, REPORT_SEVERITY_CONSOLE , "Failed to create queue\n"); WLAN_OS_REPORT(("Failed to create queue\n")); os_memoryFree (pTxMgmtQ->hOs, pTxMgmtQ, sizeof(TTxMgmtQ)); return; } pTxMgmtQ->aQueueBusy[uQueId] = TI_FALSE; /* aQueueBusy default is not busy. */ pTxMgmtQ->aQueueEnabledBySM[uQueId] = TI_FALSE; /* Queue is disabled by the SM (state is CLOSE). */ } /* Register to the context engine and get the client ID */ pTxMgmtQ->uContextId = context_RegisterClient (pTxMgmtQ->hContext, txMgmtQ_QueuesNotEmpty, (TI_HANDLE)pTxMgmtQ, TI_TRUE, "TX_MGMT", sizeof("TX_MGMT")); TRACE0(pTxMgmtQ->hReport, REPORT_SEVERITY_INIT, ".....Tx Mgmt Queue configured successfully\n"); } /** * \fn txMgmtQ_Destroy * \brief Destroy the module and its queues * * Clear and destroy the queues and then destroy the module object. * * \note * \param hTxMgmtQ - The module's object * \return TI_OK - Unload succesfull, TI_NOK - Unload unsuccesfull * \sa */ TI_STATUS txMgmtQ_Destroy (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TI_STATUS eStatus = TI_OK; int uQueId; /* Dequeue and free all queued packets */ txMgmtQ_ClearQueues (hTxMgmtQ); /* free Mgmt queues */ for (uQueId = 0 ; uQueId < NUM_OF_MGMT_QUEUES ; uQueId++) { if (que_Destroy(pTxMgmtQ->aQueues[uQueId]) != TI_OK) { TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "txMgmtQueue_unLoad: fail to free Mgmt Queue number: %d\n",uQueId); eStatus = TI_NOK; } } /* free Tx Mgmt Queue Module */ os_memoryFree (pTxMgmtQ->hOs, pTxMgmtQ, sizeof(TTxMgmtQ)); return eStatus; } /** * \fn txMgmtQ_ClearQueues * \brief Clear all queues * * Dequeue and free all queued packets. * * \note * \param hTxMgmtQ - The object * \return void * \sa */ void txMgmtQ_ClearQueues (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId; /* Dequeue and free all queued packets */ for (uQueId = 0 ; uQueId < NUM_OF_MGMT_QUEUES ; uQueId++) { do { context_EnterCriticalSection (pTxMgmtQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *)que_Dequeue(pTxMgmtQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxMgmtQ->hContext); if (pPktCtrlBlk != NULL) { txCtrl_FreePacket (pTxMgmtQ->hTxCtrl, pPktCtrlBlk, TI_NOK); } } while (pPktCtrlBlk != NULL); } } /** * \fn txMgmtQ_Xmit * \brief Insert non-data packet for transmission * * This function is used by the driver applications to send Tx packets other than the * regular data traffic, including the following packet types: * - Management * - EAPOL * - NULL * - IAPP * The managment packets are enqueued to the Mgmt-queue and the others to the Eapol-queue. * EAPOL packets may be inserted from the network stack context, so it requires switching * to the driver's context (after the packet is enqueued). * If the selected queue was empty before the packet insertion, the SM is called * with QUEUES_NOT_EMPTY event (in case of external context, only after the context switch). * * \note * \param hTxMgmtQ - The module's object * \param pPktCtrlBlk - Pointer to the packet CtrlBlk * \param bExternalContext - Indicates if called from non-driver context * \return TI_OK - if the packet was queued, TI_NOK - if the packet was dropped. * \sa txMgmtQ_QueuesNotEmpty */ TI_STATUS txMgmtQ_Xmit (TI_HANDLE hTxMgmtQ, TTxCtrlBlk *pPktCtrlBlk, TI_BOOL bExternalContext) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TI_STATUS eStatus; TI_UINT32 uQueId; TI_UINT32 uQueSize; /* Always set highest TID for mgmt-queues packets. */ pPktCtrlBlk->tTxDescriptor.tid = MGMT_QUEUES_TID; /* Select queue asccording to the packet type */ uQueId = (pPktCtrlBlk->tTxPktParams.uPktType == TX_PKT_TYPE_MGMT) ? QUEUE_TYPE_MGMT : QUEUE_TYPE_EAPOL ; /* Enter critical section to protect queue access */ context_EnterCriticalSection (pTxMgmtQ->hContext); /* Enqueue the packet in the appropriate Queue */ eStatus = que_Enqueue (pTxMgmtQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); /* Get number of packets in current queue */ uQueSize = que_Size (pTxMgmtQ->aQueues[uQueId]); /* Leave critical section */ context_LeaveCriticalSection (pTxMgmtQ->hContext); /* If packet enqueued successfully */ if (eStatus == TI_OK) { pTxMgmtQ->tDbgCounters.aEnqueuePackets[uQueId]++; /* If selected queue was empty before packet insertion */ if (uQueSize == 1) { /* If called from external context (EAPOL from network), request switch to the driver's context. */ if (bExternalContext) { context_RequestSchedule (pTxMgmtQ->hContext, pTxMgmtQ->uContextId); } /* If already in the driver's context, call the SM with QUEUES_NOT_EMPTY event. */ else { mgmtQueuesSM(pTxMgmtQ, SM_EVENT_QUEUES_NOT_EMPTY); } } } else { /* If the packet can't be queued so drop it */ txCtrl_FreePacket (pTxMgmtQ->hTxCtrl, pPktCtrlBlk, TI_NOK); pTxMgmtQ->tDbgCounters.aDroppedPackets[uQueId]++; } return eStatus; } /** * \fn txMgmtQ_QueuesNotEmpty * \brief Context-Engine Callback * * Context-Engine Callback for processing queues in driver's context. * Called after driver's context scheduling was requested in txMgmtQ_Xmit(). * Calls the SM with QUEUES_NOT_EMPTY event. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa txMgmtQ_Xmit */ void txMgmtQ_QueuesNotEmpty (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; /* Call the SM with QUEUES_NOT_EMPTY event. */ mgmtQueuesSM(pTxMgmtQ, SM_EVENT_QUEUES_NOT_EMPTY); } /** * \fn txMgmtQ_StopQueue * \brief Context-Engine Callback * * This function is called by the txCtrl_xmitMgmt() if the queue's backpressure indication * is set. It sets the internal queue's Busy indication. * * \note * \param hTxMgmtQ - The module's object * \param uTidBitMap - The busy TIDs bitmap * \return void * \sa txMgmtQ_UpdateBusyMap */ void txMgmtQ_StopQueue (TI_HANDLE hTxMgmtQ, TI_UINT32 uTidBitMap) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; /* Update the Queue(s) mode */ updateQueuesBusyMap (pTxMgmtQ, uTidBitMap); } /** * \fn txMgmtQ_UpdateBusyMap * \brief Update the queues busy map * * 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 queues Busy indication and calls the scheduler. * * \note * \param hTxMgmtQ - The module's object * \param uTidBitMap - The busy TIDs bitmap * \return void * \sa txMgmtQ_StopQueue */ void txMgmtQ_UpdateBusyMap (TI_HANDLE hTxMgmtQ, TI_UINT32 uTidBitMap) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; /* Update the Queue(s) busy map. */ updateQueuesBusyMap (pTxMgmtQ, uTidBitMap); /* If the queues are not empty, run the scheduler and if they become empty update the SM. */ runSchedulerNotFromSm (pTxMgmtQ); } /** * \fn txMgmtQ_StopAll * \brief Stop all queues transmission * * This function is called by the Tx-Port when the whole Mgmt-queue is stopped. * It clears the common queues enable indication. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa txMgmtQ_WakeAll */ void txMgmtQ_StopAll (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; /* Disable the Mgmt Tx port */ pTxMgmtQ->bMgmtPortEnable = TI_FALSE; } /** * \fn txMgmtQ_WakeAll * \brief Enable all queues transmission * * This function is called by the Tx-Port when the whole Mgmt-queue is enabled. * It sets the common queues enable indication and calls the scheduler. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa txMgmtQ_StopAll */ void txMgmtQ_WakeAll (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; /* Enable the Mgmt Tx port */ pTxMgmtQ->bMgmtPortEnable = TI_TRUE; /* If the queues are not empty, run the scheduler and if they become empty update the SM. */ runSchedulerNotFromSm (pTxMgmtQ); } /** * \fn txMgmtQ_SetConnState * \brief Enable all queues transmission * * Called by the connection SM and updates the connection state from Tx perspective * (i.e. which packet types are permitted). * Calls the local SM to handle this state change. * * \note * \param hTxMgmtQ - The module's object * \param eTxConnState - The new Tx connection state * \return void * \sa mgmtQueuesSM */ void txMgmtQ_SetConnState (TI_HANDLE hTxMgmtQ, ETxConnState eTxConnState) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; pTxMgmtQ->eTxConnState = eTxConnState; /* Call the SM with the current event. */ switch (eTxConnState) { case TX_CONN_STATE_CLOSE: mgmtQueuesSM(pTxMgmtQ, SM_EVENT_CLOSE); break; case TX_CONN_STATE_MGMT: mgmtQueuesSM(pTxMgmtQ, SM_EVENT_MGMT); break; case TX_CONN_STATE_EAPOL: mgmtQueuesSM(pTxMgmtQ, SM_EVENT_EAPOL); break; case TX_CONN_STATE_OPEN: mgmtQueuesSM(pTxMgmtQ, SM_EVENT_OPEN); break; default: TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, ": Unknown eTxConnState = %d\n", eTxConnState); } } /******************************************************************************* * INTERNAL FUNCTIONS IMPLEMENTATION * ********************************************************************************/ /** * \fn mgmtQueuesSM * \brief The module state-machine (static function) * * The SM follows the system management states (see ETxConnState) and the Mgmt queues * status (empty or not), and contorls the Tx queues flow accordingly (mgmt and data queues). * For detailed explanation, see the Tx-Path LLD document! * * \note To avoid recursion issues, all SM actions are done at the end of the function, * since some of them may invoke the SM again. * \param pTxMgmtQ - The module's object * \param eSmEvent - The event to act upon * \return void * \sa txMgmtQ_SetConnState */ static void mgmtQueuesSM (TTxMgmtQ *pTxMgmtQ, ESmEvent eSmEvent) { ESmState ePrevState = pTxMgmtQ->eSmState; ESmAction eSmAction = SM_ACTION_NULL; switch(eSmEvent) { case SM_EVENT_CLOSE: /* * Tx link is closed (expected in any state), so disable both mgmt queues * and if data-queues are active disable them via txPort module. */ pTxMgmtQ->eSmState = SM_STATE_CLOSE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_FALSE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE; if (ePrevState == SM_STATE_OPEN_DATA) eSmAction = SM_ACTION_ENABLE_MGMT; break; case SM_EVENT_MGMT: /* * Only Mgmt packets are permitted (expected from any state): * - Enable the mgmt queue and disable the Eapol queue. * - If data-queues are active disable them via txPort (this will run the scheduler). * - Else run the scheduler (to send mgmt packets if waiting). */ pTxMgmtQ->eSmState = SM_STATE_MGMT; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_TRUE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE; if (ePrevState == SM_STATE_OPEN_DATA) eSmAction = SM_ACTION_ENABLE_MGMT; else eSmAction = SM_ACTION_RUN_SCHEDULER; break; case SM_EVENT_EAPOL: /* * EAPOL packets are also permitted (expected in MGMT or CLOSE state), so enable the * EAPOL queue and run the scheduler (to send packets from EAPOL queue if waiting). */ if ( (ePrevState != SM_STATE_CLOSE) && (ePrevState != SM_STATE_MGMT) ) { TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Got SmEvent=EAPOL when eSmState=%d\n", ePrevState); } pTxMgmtQ->eSmState = SM_STATE_EAPOL; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_TRUE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_TRUE; eSmAction = SM_ACTION_RUN_SCHEDULER; break; case SM_EVENT_OPEN: /* * All packets are now permitted (expected in EAPOL state), so if the mgmt-queues * are empty disable them and enable the data queues via txPort module. */ if (ePrevState != SM_STATE_EAPOL) { TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Got SmEvent=OPEN when eSmState=%d\n", ePrevState); } if ( ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) ) { pTxMgmtQ->eSmState = SM_STATE_OPEN_DATA; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_FALSE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE; eSmAction = SM_ACTION_ENABLE_DATA; } else { pTxMgmtQ->eSmState = SM_STATE_OPEN_MGMT; } break; case SM_EVENT_QUEUES_EMPTY: /* * The mgmt-queues are empty, so if in OPEN_MGMT state disable the * mgmt-queues and enable the data-queues via txPort module. */ if (ePrevState == SM_STATE_OPEN_MGMT) { pTxMgmtQ->eSmState = SM_STATE_OPEN_DATA; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_FALSE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE; eSmAction = SM_ACTION_ENABLE_DATA; } else { /* This may happen so it's just a warning and not an error. */ TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_WARNING, "mgmtQueuesSM: Got SmEvent=QUEUES_EMPTY when eSmState=%d\n", ePrevState); } break; case SM_EVENT_QUEUES_NOT_EMPTY: /* A packet was inserted to the mgmt-queues */ /* * If in OPEN_DATA state, enable mgmt-queues and disable data-queues via txPort module. * * Note: The scheduler is not run here because the txPort will call * txMgmtQueue_wakeAll() which will run the scheduler. */ if (ePrevState == SM_STATE_OPEN_DATA) { pTxMgmtQ->eSmState = SM_STATE_OPEN_MGMT; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT] = TI_TRUE; pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_TRUE; eSmAction = SM_ACTION_ENABLE_MGMT; } /* * If in MGMT or EAPOL state, run the scheduler to transmit the packet. */ else if ( (ePrevState == SM_STATE_MGMT) || (ePrevState == SM_STATE_EAPOL) ) { eSmAction = SM_ACTION_RUN_SCHEDULER; } else { /* This may happen so it's just a warning and not an error. */ TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_WARNING, "mgmtQueuesSM: Got SmEvent=QUEUES_NOT_EMPTY when eSmState=%d\n", ePrevState); } break; default: TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Unknown SmEvent = %d\n", eSmEvent); break; } TRACE6( pTxMgmtQ->hReport, REPORT_SEVERITY_INFORMATION, "mgmtQueuesSM: --> nextState = %d, action = %d, MgmtQueEnbl=%d, EapolQueEnbl=%d\n", ePrevState, eSmEvent, pTxMgmtQ->eSmState, eSmAction, pTxMgmtQ->aQueueEnabledBySM[0], pTxMgmtQ->aQueueEnabledBySM[1]); /* * Execute the required action. * Note: This is done at the end of the SM because it may start a sequence that will call the SM again! */ switch (eSmAction) { case SM_ACTION_NULL: break; case SM_ACTION_ENABLE_DATA: txPort_enableData(pTxMgmtQ->hTxPort); break; case SM_ACTION_ENABLE_MGMT: txPort_enableMgmt(pTxMgmtQ->hTxPort); break; case SM_ACTION_RUN_SCHEDULER: runScheduler(pTxMgmtQ); break; default: TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, ": Unknown SmAction = %d\n", eSmAction); break; } } /** * \fn runSchedulerNotFromSm * \brief Run scheduler due to other events then from SM (static function) * * To comply with the SM behavior, this function is used for any case where the * Mgmt-Queues scheduler may have work to do due to events external to the SM. * If the queues are not empty, this function runs the scheduler. * If the scheduler emptied the queues, update the SM. * * \note * \param pTxMgmtQ - The module's object * \return void * \sa */ static void runSchedulerNotFromSm (TTxMgmtQ *pTxMgmtQ) { /* If the queues are not empty, run the scheduler. */ if ( !ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) ) { runScheduler (pTxMgmtQ); /* If the queues are now both empty, call the SM with QUEUES_EMPTY event. */ if ( ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) ) { mgmtQueuesSM (pTxMgmtQ, SM_EVENT_QUEUES_EMPTY); } } } /** * \fn runScheduler * \brief The scheduler processing (static function) * * Loops over the mgmt-queues (high priority first) and if queue enabled and * has packets, dequeue a packet and send it to the TxCtrl. * Exit if the port level is disabled or if couldn't send anything from both queues. * * \note Protect the queues access against preemption from external context (EAPOL). * \param pTxMgmtQ - The module's object * \return void * \sa */ static void runScheduler (TTxMgmtQ *pTxMgmtQ) { TI_STATUS eStatus; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId = 0; /* start from highest priority queue */ while(1) { /* If the Mgmt port is closed exit. */ if ( !pTxMgmtQ->bMgmtPortEnable ) { return; } /* Check that the current queue is not busy and is enabled by the state-machine. */ if ( !pTxMgmtQ->aQueueBusy[uQueId] && pTxMgmtQ->aQueueEnabledBySM[uQueId]) { /* Dequeue a packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pTxMgmtQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxMgmtQ->hContext); if (pPktCtrlBlk) { pTxMgmtQ->tDbgCounters.aDequeuePackets[uQueId]++; /* Send the packet */ eStatus = txCtrl_XmitMgmt (pTxMgmtQ->hTxCtrl, pPktCtrlBlk); /* In case the return status is busy it means that the packet wasn't handled so we need to requeue the packet for future try. */ if(eStatus == STATUS_XMIT_BUSY) { /* Requeue the packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); que_Requeue (pTxMgmtQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); context_LeaveCriticalSection (pTxMgmtQ->hContext); pTxMgmtQ->tDbgCounters.aRequeuePackets[uQueId]++; } /* The packet was handled by the lower Tx layers. */ else { pTxMgmtQ->tDbgCounters.aXmittedPackets[uQueId]++; /* Successful delivery so start next tx from the high priority queue (mgmt), * giving it strict priority over the lower queue. */ uQueId = 0; continue; } } } /* If we got here we couldn't deliver a packet from current queue, so progress to lower * priority queue and if already in lowest queue exit. */ uQueId++; if (uQueId < NUM_OF_MGMT_QUEUES) { continue; /* Try sending from next queue (i.e. the EAPOL queue). */ } else { /* We couldn't send from both queues so indicate end of packets burst and exit. */ TWD_txXfer_EndOfBurst (pTxMgmtQ->hTWD); return; } } /* End of while */ /* Unreachable code */ } /** * \fn updateQueuesBusyMap * \brief Update queues busy map (static function) * * Set the queues busy indication on or off according to the highest TID bit * in the tidBitMap (1 = busy). * Note that both Mgmt and Eapol queues are mapped to TID 7. * * \note * \param pTxMgmtQ - The module's object * \param uTidBitMap - The TIDs bitmap of the queue(s) to update * \return void * \sa */ static void updateQueuesBusyMap (TTxMgmtQ *pTxMgmtQ, TI_UINT32 uTidBitMap) { /* Set the queues busy indication on or off according to the highest TID bit (1 = busy). */ if(uTidBitMap & (1 << MGMT_QUEUES_TID) ) { pTxMgmtQ->aQueueBusy[QUEUE_TYPE_MGMT ] = TI_TRUE; pTxMgmtQ->aQueueBusy[QUEUE_TYPE_EAPOL] = TI_TRUE; } else { pTxMgmtQ->aQueueBusy[QUEUE_TYPE_MGMT ] = TI_FALSE; pTxMgmtQ->aQueueBusy[QUEUE_TYPE_EAPOL] = TI_FALSE; } } /******************************************************************************* * DEBUG FUNCTIONS IMPLEMENTATION * ********************************************************************************/ #ifdef TI_DBG /** * \fn txMgmtQ_PrintModuleParams * \brief Print module's parameters (debug) * * This function prints the module's parameters. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa */ void txMgmtQ_PrintModuleParams (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TI_UINT32 uQueId; WLAN_OS_REPORT(("-------------- txMgmtQueue Module Params -----------------\n")); WLAN_OS_REPORT(("==========================================================\n")); WLAN_OS_REPORT(("eSmState = %d\n", pTxMgmtQ->eSmState)); WLAN_OS_REPORT(("bMgmtPortEnable = %d\n", pTxMgmtQ->bMgmtPortEnable)); WLAN_OS_REPORT(("eTxConnState = %d\n", pTxMgmtQ->eTxConnState)); WLAN_OS_REPORT(("uContextId = %d\n", pTxMgmtQ->uContextId)); WLAN_OS_REPORT(("-------------- Queues Busy (in HW) -----------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) { WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->aQueueBusy[uQueId])); } WLAN_OS_REPORT(("-------------- Queues Enabled By SM ----------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) { WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->aQueueBusy[uQueId])); } WLAN_OS_REPORT(("-------------- Queues Info -------------------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) { WLAN_OS_REPORT(("Que %d:\n", uQueId)); que_Print (pTxMgmtQ->aQueues[uQueId]); } WLAN_OS_REPORT(("==========================================================\n\n")); } /** * \fn txMgmtQ_PrintQueueStatistics * \brief Print queues statistics (debug) * * This function prints the module's Tx statistics per Queue. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa */ void txMgmtQ_PrintQueueStatistics (TI_HANDLE hTxMgmtQ) { #ifdef REPORT_LOG TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TI_UINT32 uQueId; WLAN_OS_REPORT(("-------------- Mgmt Queues Statistics -------------------\n")); WLAN_OS_REPORT(("==========================================================\n")); WLAN_OS_REPORT(("-------------- Enqueue Packets ---------------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->tDbgCounters.aEnqueuePackets[uQueId])); WLAN_OS_REPORT(("-------------- Dequeue Packets ---------------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->tDbgCounters.aDequeuePackets[uQueId])); WLAN_OS_REPORT(("-------------- Requeue Packets ---------------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->tDbgCounters.aRequeuePackets[uQueId])); WLAN_OS_REPORT(("-------------- Xmitted Packets ---------------------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->tDbgCounters.aXmittedPackets[uQueId])); WLAN_OS_REPORT(("-------------- Dropped Packets (queue full) --------------\n")); for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++) WLAN_OS_REPORT(("Que[%d]: %d\n", uQueId, pTxMgmtQ->tDbgCounters.aDroppedPackets[uQueId])); WLAN_OS_REPORT(("==========================================================\n\n")); #endif } /** * \fn txMgmtQ_ResetQueueStatistics * \brief Reset queues statistics (debug) * * This function Resets the module's Tx statistics per Queue. * * \note * \param hTxMgmtQ - The module's object * \return void * \sa */ void txMgmtQ_ResetQueueStatistics (TI_HANDLE hTxMgmtQ) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; os_memoryZero(pTxMgmtQ->hOs, (void *)&(pTxMgmtQ->tDbgCounters), sizeof(TDbgCount)); } #endif /* TI_DBG */