/*
* 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: <currentState = %d, event = %d> --> 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 */