1/*
2 * txMgmtQueue.c
3 *
4 * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 *  * Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *  * Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 *  * Neither the name Texas Instruments nor the names of its
18 *    contributors may be used to endorse or promote products derived
19 *    from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34
35
36/** \file   txMgmtQueue.c
37 *  \brief  The Tx Mgmt Queues module.
38 *
39 *	DESCRIPTION:
40 *	============
41 *	The Management-Queues module is responsible for the following tasks:
42 *		1.	Queue the driver generated Tx packets, including management,
43 *			EAPOL and null packets until they are transmitted.
44 *			The management packets are buffered in the management-queue,
45 *			and the others in the EAPOL-queue.
46 *		2.	Maintain a state machine that follows the queues state and
47 *			the connection states and enables specific transmission types
48 *			accordingly (e.g. only management).
49 *		3.	Gain access to the Tx path when the management queues are not
50 *			empty, and return the access to the data queues when the
51 *			management queues are empty.
52 *		4.	Schedule packets transmission with strict priority of the
53 *			management queue over the EAPOL queue, and according to the
54 *			backpressure controls from the Port (all queues) and from the
55 *			Tx-Ctrl (per queue).
56 *
57 *  \see    txMgmtQueue.h
58 */
59
60#define __FILE_ID__  FILE_ID_61
61#include "tidef.h"
62#include "paramOut.h"
63#include "osApi.h"
64#include "TWDriver.h"
65#include "DataCtrl_Api.h"
66#include "report.h"
67#include "queue.h"
68#include "context.h"
69#include "DrvMainModules.h"
70
71
72#define MGMT_QUEUES_TID		MAX_USER_PRIORITY
73
74typedef enum
75{
76	QUEUE_TYPE_MGMT,	/* Mgmt-queue  - high-priority, for mgmt packets only. */
77	QUEUE_TYPE_EAPOL,	/* EAPOL-queue - low-priority, for other internal packets (EAPOL, NULL, IAPP). */
78	NUM_OF_MGMT_QUEUES
79} EMgmtQueueTypes;
80
81/* State-Machine Events */
82typedef enum
83{
84	SM_EVENT_CLOSE,			/* All Tx types should be closed. */
85	SM_EVENT_MGMT,			/* Allow only mgmt packets. */
86	SM_EVENT_EAPOL,			/* Allow mgmt and EAPOL packets. */
87	SM_EVENT_OPEN,			/* Allow all packets. */
88	SM_EVENT_QUEUES_EMPTY,	/* Mgmt-aQueues are now both empty. */
89	SM_EVENT_QUEUES_NOT_EMPTY /* At least one of the Mgmt-aQueues is now not empty. */
90} ESmEvent;
91
92/* State-Machine States */
93typedef enum
94{
95	SM_STATE_CLOSE,			/* All Tx path is closed. */
96	SM_STATE_MGMT,			/* Only mgmt Tx is permitted. */
97	SM_STATE_EAPOL,			/* Only mgmt and EAPOL Tx is permitted. */
98	SM_STATE_OPEN_MGMT,		/* All Tx permitted and Mgmt aQueues are currently active (date disabled). */
99	SM_STATE_OPEN_DATA		/* All Tx permitted and Data aQueues are currently active (mgmt disabled). */
100} ESmState;
101
102/* State-Machine Actions */
103typedef enum
104{
105	SM_ACTION_NULL,
106	SM_ACTION_ENABLE_DATA,
107	SM_ACTION_ENABLE_MGMT,
108	SM_ACTION_RUN_SCHEDULER
109} ESmAction;
110
111/* TI_TRUE if both aQueues are empty. */
112#define ARE_ALL_MGMT_QUEUES_EMPTY(aQueues)	( (que_Size(aQueues[QUEUE_TYPE_MGMT] ) == 0)  &&  \
113											  (que_Size(aQueues[QUEUE_TYPE_EAPOL]) == 0) )
114
115typedef struct
116{
117	TI_UINT32 aEnqueuePackets[NUM_OF_MGMT_QUEUES];
118	TI_UINT32 aDequeuePackets[NUM_OF_MGMT_QUEUES];
119	TI_UINT32 aRequeuePackets[NUM_OF_MGMT_QUEUES];
120	TI_UINT32 aDroppedPackets[NUM_OF_MGMT_QUEUES];
121	TI_UINT32 aXmittedPackets[NUM_OF_MGMT_QUEUES];
122} TDbgCount;
123
124/* The module object. */
125typedef struct
126{
127	/* Handles */
128	TI_HANDLE		hOs;
129	TI_HANDLE		hReport;
130	TI_HANDLE 		hTxCtrl;
131	TI_HANDLE 		hTxPort;
132	TI_HANDLE 		hContext;
133
134	TI_BOOL			bMgmtPortEnable;/* Port open for mgmt-aQueues or not. */
135	ESmState		eSmState;	    /* The current state of the SM. */
136	ETxConnState	eTxConnState;   /* See typedef in module API. */
137    TI_UINT32       uContextId;     /* ID allocated to this module on registration to context module */
138
139	/* Mgmt aQueues */
140	TI_HANDLE   	aQueues[NUM_OF_MGMT_QUEUES];		   /* The mgmt-aQueues handles. */
141    TI_BOOL			aQueueBusy[NUM_OF_MGMT_QUEUES];		   /* Related AC is busy. */
142	TI_BOOL			aQueueEnabledBySM[NUM_OF_MGMT_QUEUES]; /* Queue is enabled by the SM. */
143
144	/* Debug Counters */
145	TDbgCount		tDbgCounters; /* Save Tx statistics per mgmt-queue. */
146
147} TTxMgmtQ;
148
149/* The module internal functions */
150static void mgmtQueuesSM (TTxMgmtQ *pTxMgmtQ, ESmEvent smEvent);
151static void runSchedulerNotFromSm (TTxMgmtQ *pTxMgmtQ);
152static void runScheduler (TTxMgmtQ *pTxMgmtQ);
153static void updateQueuesBusyMap (TTxMgmtQ *pTxMgmtQ, TI_UINT32 tidBitMap);
154
155/*******************************************************************************
156*                       PUBLIC  FUNCTIONS  IMPLEMENTATION					   *
157********************************************************************************/
158
159
160/**
161 * \fn     txMgmtQ_Create
162 * \brief  Create the module and its queues
163 *
164 * Create the Tx Mgmt Queue module and its queues.
165 *
166 * \note
167 * \param  hOs - Handle to the Os Abstraction Layer
168 * \return Handle to the allocated Tx Mgmt Queue module (NULL if failed)
169 * \sa
170 */
171TI_HANDLE txMgmtQ_Create (TI_HANDLE hOs)
172{
173    TTxMgmtQ *pTxMgmtQ;
174
175    /* allocate TxMgmtQueue module */
176    pTxMgmtQ = os_memoryAlloc (hOs, (sizeof(TTxMgmtQ)));
177
178    if(!pTxMgmtQ)
179	{
180        WLAN_OS_REPORT(("Error allocating the TxMgmtQueue Module\n"));
181		return NULL;
182	}
183
184    /* Reset TxMgmtQueue module */
185    os_memoryZero (hOs, pTxMgmtQ, (sizeof(TTxMgmtQ)));
186
187    return (TI_HANDLE)pTxMgmtQ;
188}
189
190
191/**
192 * \fn     txMgmtQ_Init
193 * \brief  Configure module with default settings
194*
195 * Get other modules handles.
196 * Init the Tx Mgmt queues.
197 * Register as the context-engine client.
198 *
199 * \note
200 * \param  pStadHandles  - The driver modules handles
201 * \return void
202 * \sa
203 */
204void txMgmtQ_Init (TStadHandlesList *pStadHandles)
205{
206    TTxMgmtQ  *pTxMgmtQ = (TTxMgmtQ *)(pStadHandles->hTxMgmtQ);
207    TI_UINT32  uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode);
208	int        uQueId;
209
210    /* configure modules handles */
211    pTxMgmtQ->hOs		= pStadHandles->hOs;
212    pTxMgmtQ->hReport	= pStadHandles->hReport;
213    pTxMgmtQ->hTxCtrl	= pStadHandles->hTxCtrl;
214    pTxMgmtQ->hTxPort	= pStadHandles->hTxPort;
215    pTxMgmtQ->hContext	= pStadHandles->hContext;
216
217	pTxMgmtQ->bMgmtPortEnable = TI_TRUE;	/* Port Default status is open (data-queues are disabled). */
218	pTxMgmtQ->eSmState = SM_STATE_CLOSE; /* SM default state is CLOSE. */
219	pTxMgmtQ->eTxConnState = TX_CONN_STATE_CLOSE;
220
221    /* initialize tx Mgmt queues */
222	for (uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
223    {
224        pTxMgmtQ->aQueues[uQueId] = que_Create (pTxMgmtQ->hOs,
225                                                pTxMgmtQ->hReport,
226                                                MGMT_QUEUES_DEPTH,
227                                                uNodeHeaderOffset);
228
229		/* If any Queues' allocation failed, print error, free TxMgmtQueue module and exit */
230		if (pTxMgmtQ->aQueues[uQueId] == NULL)
231		{
232            TRACE0(pTxMgmtQ->hReport, REPORT_SEVERITY_CONSOLE , "Failed to create queue\n");
233			WLAN_OS_REPORT(("Failed to create queue\n"));
234			os_memoryFree (pTxMgmtQ->hOs, pTxMgmtQ, sizeof(TTxMgmtQ));
235			return;
236		}
237
238		pTxMgmtQ->aQueueBusy[uQueId]        = TI_FALSE;	/* aQueueBusy default is not busy. */
239		pTxMgmtQ->aQueueEnabledBySM[uQueId] = TI_FALSE; /* Queue is disabled by the SM (state is CLOSE). */
240    }
241
242    /* Register to the context engine and get the client ID */
243    pTxMgmtQ->uContextId = context_RegisterClient (pTxMgmtQ->hContext,
244                                                   txMgmtQ_QueuesNotEmpty,
245                                                   (TI_HANDLE)pTxMgmtQ,
246                                                   TI_TRUE,
247                                                   "TX_MGMT",
248                                                   sizeof("TX_MGMT"));
249
250TRACE0(pTxMgmtQ->hReport, REPORT_SEVERITY_INIT, ".....Tx Mgmt Queue configured successfully\n");
251}
252
253
254/**
255 * \fn     txMgmtQ_Destroy
256 * \brief  Destroy the module and its queues
257 *
258 * Clear and destroy the queues and then destroy the module object.
259 *
260 * \note
261 * \param  hTxMgmtQ - The module's object
262 * \return TI_OK - Unload succesfull, TI_NOK - Unload unsuccesfull
263 * \sa
264 */
265TI_STATUS txMgmtQ_Destroy (TI_HANDLE hTxMgmtQ)
266{
267    TTxMgmtQ  *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
268    TI_STATUS  eStatus = TI_OK;
269    int        uQueId;
270
271    /* Dequeue and free all queued packets */
272    txMgmtQ_ClearQueues (hTxMgmtQ);
273
274    /* free Mgmt queues */
275    for (uQueId = 0 ; uQueId < NUM_OF_MGMT_QUEUES ; uQueId++)
276    {
277        if (que_Destroy(pTxMgmtQ->aQueues[uQueId]) != TI_OK)
278		{
279            TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "txMgmtQueue_unLoad: fail to free Mgmt Queue number: %d\n",uQueId);
280			eStatus = TI_NOK;
281		}
282    }
283
284    /* free Tx Mgmt Queue Module */
285    os_memoryFree (pTxMgmtQ->hOs, pTxMgmtQ, sizeof(TTxMgmtQ));
286
287    return eStatus;
288}
289
290
291/**
292 * \fn     txMgmtQ_ClearQueues
293 * \brief  Clear all queues
294 *
295 * Dequeue and free all queued packets.
296 *
297 * \note
298 * \param  hTxMgmtQ - The object
299 * \return void
300 * \sa
301 */
302void txMgmtQ_ClearQueues (TI_HANDLE hTxMgmtQ)
303{
304    TTxMgmtQ   *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
305    TTxCtrlBlk *pPktCtrlBlk;
306    TI_UINT32  uQueId;
307
308    /* Dequeue and free all queued packets */
309    for (uQueId = 0 ; uQueId < NUM_OF_MGMT_QUEUES ; uQueId++)
310    {
311        do {
312            context_EnterCriticalSection (pTxMgmtQ->hContext);
313            pPktCtrlBlk = (TTxCtrlBlk *)que_Dequeue(pTxMgmtQ->aQueues[uQueId]);
314            context_LeaveCriticalSection (pTxMgmtQ->hContext);
315            if (pPktCtrlBlk != NULL) {
316                txCtrl_FreePacket (pTxMgmtQ->hTxCtrl, pPktCtrlBlk, TI_NOK);
317            }
318        } while (pPktCtrlBlk != NULL);
319    }
320}
321
322
323/**
324 * \fn     txMgmtQ_Xmit
325 * \brief  Insert non-data packet for transmission
326 *
327 * This function is used by the driver applications to send Tx packets other than the
328 *   regular data traffic, including the following packet types:
329*				- Management
330*				- EAPOL
331*				- NULL
332*				- IAPP
333 * The managment packets are enqueued to the Mgmt-queue and the others to the Eapol-queue.
334 * EAPOL packets may be inserted from the network stack context, so it requires switching
335 *   to the driver's context (after the packet is enqueued).
336 * If the selected queue was empty before the packet insertion, the SM is called
337 *   with QUEUES_NOT_EMPTY event (in case of external context, only after the context switch).
338 *
339 * \note
340 * \param  hTxMgmtQ         - The module's object
341 * \param  pPktCtrlBlk      - Pointer to the packet CtrlBlk
342 * \param  bExternalContext - Indicates if called from non-driver context
343 * \return TI_OK - if the packet was queued, TI_NOK - if the packet was dropped.
344 * \sa     txMgmtQ_QueuesNotEmpty
345 */
346TI_STATUS txMgmtQ_Xmit (TI_HANDLE hTxMgmtQ, TTxCtrlBlk *pPktCtrlBlk, TI_BOOL bExternalContext)
347{
348    TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
349	TI_STATUS eStatus;
350	TI_UINT32 uQueId;
351    TI_UINT32 uQueSize;
352
353	/* Always set highest TID for mgmt-queues packets. */
354	pPktCtrlBlk->tTxDescriptor.tid = MGMT_QUEUES_TID;
355
356    /* Select queue asccording to the packet type */
357	uQueId = (pPktCtrlBlk->tTxPktParams.uPktType == TX_PKT_TYPE_MGMT) ? QUEUE_TYPE_MGMT : QUEUE_TYPE_EAPOL ;
358
359    /* Enter critical section to protect queue access */
360    context_EnterCriticalSection (pTxMgmtQ->hContext);
361
362	/* Enqueue the packet in the appropriate Queue */
363    eStatus = que_Enqueue (pTxMgmtQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk);
364
365    /* Get number of packets in current queue */
366    uQueSize = que_Size (pTxMgmtQ->aQueues[uQueId]);
367
368    /* Leave critical section */
369    context_LeaveCriticalSection (pTxMgmtQ->hContext);
370
371	/* If packet enqueued successfully */
372	if (eStatus == TI_OK)
373	{
374		pTxMgmtQ->tDbgCounters.aEnqueuePackets[uQueId]++;
375
376        /* If selected queue was empty before packet insertion */
377        if (uQueSize == 1)
378        {
379            /* If called from external context (EAPOL from network), request switch to the driver's context. */
380            if (bExternalContext)
381            {
382                context_RequestSchedule (pTxMgmtQ->hContext, pTxMgmtQ->uContextId);
383            }
384
385            /* If already in the driver's context, call the SM with QUEUES_NOT_EMPTY event. */
386            else
387            {
388                mgmtQueuesSM(pTxMgmtQ, SM_EVENT_QUEUES_NOT_EMPTY);
389            }
390        }
391	}
392
393	else
394    {
395        /* If the packet can't be queued so drop it */
396        txCtrl_FreePacket (pTxMgmtQ->hTxCtrl, pPktCtrlBlk, TI_NOK);
397		pTxMgmtQ->tDbgCounters.aDroppedPackets[uQueId]++;
398    }
399
400    return eStatus;
401}
402
403
404/**
405 * \fn     txMgmtQ_QueuesNotEmpty
406 * \brief  Context-Engine Callback
407 *
408 * Context-Engine Callback for processing queues in driver's context.
409 * Called after driver's context scheduling was requested in txMgmtQ_Xmit().
410 * Calls the SM with QUEUES_NOT_EMPTY event.
411 *
412 * \note
413 * \param  hTxMgmtQ - The module's object
414 * \return void
415 * \sa     txMgmtQ_Xmit
416 */
417void txMgmtQ_QueuesNotEmpty (TI_HANDLE hTxMgmtQ)
418{
419    TTxMgmtQ  *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
420
421    /* Call the SM with QUEUES_NOT_EMPTY event. */
422    mgmtQueuesSM(pTxMgmtQ, SM_EVENT_QUEUES_NOT_EMPTY);
423}
424
425
426/**
427 * \fn     txMgmtQ_StopQueue
428 * \brief  Context-Engine Callback
429 *
430 * This function is called by the txCtrl_xmitMgmt() if the queue's backpressure indication
431 *   is set. It sets the internal queue's Busy indication.
432 *
433 * \note
434 * \param  hTxMgmtQ   - The module's object
435 * \param  uTidBitMap - The busy TIDs bitmap
436 * \return void
437 * \sa     txMgmtQ_UpdateBusyMap
438 */
439void txMgmtQ_StopQueue (TI_HANDLE hTxMgmtQ, TI_UINT32 uTidBitMap)
440{
441	TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
442
443	/* Update the Queue(s) mode */
444	updateQueuesBusyMap (pTxMgmtQ, uTidBitMap);
445}
446
447
448/**
449 * \fn     txMgmtQ_UpdateBusyMap
450 * \brief  Update the queues busy map
451 *
452 * This function is called by the txCtrl if the backpressure map per TID is changed.
453 * This could be as a result of Tx-Complete, admission change or association.
454 * The function modifies the internal queues Busy indication and calls the scheduler.
455 *
456 * \note
457 * \param  hTxMgmtQ   - The module's object
458 * \param  uTidBitMap - The busy TIDs bitmap
459 * \return void
460 * \sa     txMgmtQ_StopQueue
461 */
462void txMgmtQ_UpdateBusyMap (TI_HANDLE hTxMgmtQ, TI_UINT32 uTidBitMap)
463{
464	TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
465
466	/* Update the Queue(s) busy map. */
467	updateQueuesBusyMap (pTxMgmtQ, uTidBitMap);
468
469	/* If the queues are not empty, run the scheduler and if they become empty update the SM. */
470	runSchedulerNotFromSm (pTxMgmtQ);
471}
472
473
474/**
475 * \fn     txMgmtQ_StopAll
476 * \brief  Stop all queues transmission
477 *
478 * This function is called by the Tx-Port when the whole Mgmt-queue is stopped.
479 * It clears the common queues enable indication.
480 *
481 * \note
482 * \param  hTxMgmtQ   - The module's object
483 * \return void
484 * \sa     txMgmtQ_WakeAll
485 */
486void txMgmtQ_StopAll (TI_HANDLE hTxMgmtQ)
487{
488    TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
489
490	/* Disable the Mgmt Tx port */
491	pTxMgmtQ->bMgmtPortEnable = TI_FALSE;
492}
493
494
495/**
496 * \fn     txMgmtQ_WakeAll
497 * \brief  Enable all queues transmission
498 *
499 * This function is called by the Tx-Port when the whole Mgmt-queue is enabled.
500 * It sets the common queues enable indication and calls the scheduler.
501 *
502 * \note
503 * \param  hTxMgmtQ   - The module's object
504 * \return void
505 * \sa     txMgmtQ_StopAll
506 */
507void txMgmtQ_WakeAll (TI_HANDLE hTxMgmtQ)
508{
509    TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
510
511	/* Enable the Mgmt Tx port */
512	pTxMgmtQ->bMgmtPortEnable = TI_TRUE;
513
514	/* If the queues are not empty, run the scheduler and if they become empty update the SM. */
515	runSchedulerNotFromSm (pTxMgmtQ);
516}
517
518
519/**
520 * \fn     txMgmtQ_SetConnState
521 * \brief  Enable all queues transmission
522 *
523 * Called by the connection SM and updates the connection state from Tx perspective
524 *   (i.e. which packet types are permitted).
525*               Calls the local SM to handle this state change.
526*
527 * \note
528 * \param  hTxMgmtQ     - The module's object
529 * \param  eTxConnState - The new Tx connection state
530 * \return void
531 * \sa     mgmtQueuesSM
532 */
533void txMgmtQ_SetConnState (TI_HANDLE hTxMgmtQ, ETxConnState eTxConnState)
534{
535    TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
536
537	pTxMgmtQ->eTxConnState = eTxConnState;
538
539	/* Call the SM with the current event. */
540	switch (eTxConnState)
541	{
542		case TX_CONN_STATE_CLOSE:	mgmtQueuesSM(pTxMgmtQ, SM_EVENT_CLOSE);		break;
543		case TX_CONN_STATE_MGMT:	mgmtQueuesSM(pTxMgmtQ, SM_EVENT_MGMT);		break;
544		case TX_CONN_STATE_EAPOL:	mgmtQueuesSM(pTxMgmtQ, SM_EVENT_EAPOL);		break;
545		case TX_CONN_STATE_OPEN:	mgmtQueuesSM(pTxMgmtQ, SM_EVENT_OPEN);		break;
546
547		default:
548TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, ": Unknown eTxConnState = %d\n", eTxConnState);
549	}
550}
551
552
553
554/*******************************************************************************
555*                       INTERNAL  FUNCTIONS  IMPLEMENTATION					   *
556********************************************************************************/
557
558
559/**
560 * \fn     mgmtQueuesSM
561 * \brief  The module state-machine (static function)
562 *
563 * The SM follows the system management states (see ETxConnState) and the Mgmt queues
564 *   status (empty or not), and contorls the Tx queues flow accordingly (mgmt and data queues).
565 * For detailed explanation, see the Tx-Path LLD document!
566 *
567 * \note   To avoid recursion issues, all SM actions are done at the end of the function,
568 *            since some of them may invoke the SM again.
569 * \param  pTxMgmtQ - The module's object
570 * \param  eSmEvent - The event to act upon
571 * \return void
572 * \sa     txMgmtQ_SetConnState
573 */
574static void mgmtQueuesSM (TTxMgmtQ *pTxMgmtQ, ESmEvent eSmEvent)
575{
576	ESmState  ePrevState = pTxMgmtQ->eSmState;
577	ESmAction eSmAction  = SM_ACTION_NULL;
578
579	switch(eSmEvent)
580	{
581		case SM_EVENT_CLOSE:
582			/*
583			 * Tx link is closed (expected in any state), so disable both mgmt queues
584			 *   and if data-queues are active disable them via txPort module.
585			 */
586			pTxMgmtQ->eSmState = SM_STATE_CLOSE;
587			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_FALSE;
588			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE;
589			if (ePrevState == SM_STATE_OPEN_DATA)
590				eSmAction = SM_ACTION_ENABLE_MGMT;
591			break;
592
593		case SM_EVENT_MGMT:
594			/*
595			 * Only Mgmt packets are permitted (expected from any state):
596			 *   - Enable the mgmt queue and disable the Eapol queue.
597			 *   - If data-queues are active disable them via txPort (this will run the scheduler).
598			 *   - Else run the scheduler (to send mgmt packets if waiting).
599			 */
600			pTxMgmtQ->eSmState = SM_STATE_MGMT;
601			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_TRUE;
602			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE;
603			if (ePrevState == SM_STATE_OPEN_DATA)
604				eSmAction = SM_ACTION_ENABLE_MGMT;
605			else
606				eSmAction = SM_ACTION_RUN_SCHEDULER;
607			break;
608
609		case SM_EVENT_EAPOL:
610			/*
611			 * EAPOL packets are also permitted (expected in MGMT or CLOSE state), so enable the
612			 *   EAPOL queue and run the scheduler (to send packets from EAPOL queue if waiting).
613			 */
614			if ( (ePrevState != SM_STATE_CLOSE) && (ePrevState != SM_STATE_MGMT) )
615			{
616TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Got SmEvent=EAPOL when eSmState=%d\n", ePrevState);
617			}
618			pTxMgmtQ->eSmState = SM_STATE_EAPOL;
619			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_TRUE;
620			pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_TRUE;
621			eSmAction = SM_ACTION_RUN_SCHEDULER;
622			break;
623
624		case SM_EVENT_OPEN:
625			/*
626			 * All packets are now permitted (expected in EAPOL state), so if the mgmt-queues
627			 *   are empty disable them and enable the data queues via txPort module.
628			 */
629			if (ePrevState != SM_STATE_EAPOL)
630			{
631TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Got SmEvent=OPEN when eSmState=%d\n", ePrevState);
632			}
633			if ( ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) )
634			{
635				pTxMgmtQ->eSmState = SM_STATE_OPEN_DATA;
636				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_FALSE;
637				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE;
638				eSmAction = SM_ACTION_ENABLE_DATA;
639			}
640			else
641			{
642				pTxMgmtQ->eSmState = SM_STATE_OPEN_MGMT;
643			}
644			break;
645
646		case SM_EVENT_QUEUES_EMPTY:
647			/*
648			 * The mgmt-queues are empty, so if in OPEN_MGMT state disable the
649			 *   mgmt-queues and enable the data-queues via txPort module.
650			 */
651			if (ePrevState == SM_STATE_OPEN_MGMT)
652			{
653				pTxMgmtQ->eSmState = SM_STATE_OPEN_DATA;
654				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_FALSE;
655				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_FALSE;
656				eSmAction = SM_ACTION_ENABLE_DATA;
657			}
658			else
659			{
660				/* This may happen so it's just a warning and not an error. */
661TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_WARNING, "mgmtQueuesSM: Got SmEvent=QUEUES_EMPTY when eSmState=%d\n", ePrevState);
662			}
663			break;
664
665		case SM_EVENT_QUEUES_NOT_EMPTY:
666
667			/* A packet was inserted to the mgmt-queues */
668
669			/*
670			 * If in OPEN_DATA state, enable mgmt-queues and disable data-queues via txPort module.
671			 *
672			 * Note: The scheduler is not run here because the txPort will call
673			 *   txMgmtQueue_wakeAll() which will run the scheduler.
674			 */
675			if (ePrevState == SM_STATE_OPEN_DATA)
676			{
677				pTxMgmtQ->eSmState = SM_STATE_OPEN_MGMT;
678				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_MGMT]  = TI_TRUE;
679				pTxMgmtQ->aQueueEnabledBySM[QUEUE_TYPE_EAPOL] = TI_TRUE;
680				eSmAction = SM_ACTION_ENABLE_MGMT;
681			}
682
683			/*
684			 * If in MGMT or EAPOL state, run the scheduler to transmit the packet.
685			 */
686			else if ( (ePrevState == SM_STATE_MGMT) || (ePrevState == SM_STATE_EAPOL) )
687			{
688				eSmAction = SM_ACTION_RUN_SCHEDULER;
689			}
690
691			else
692			{
693				/* This may happen so it's just a warning and not an error. */
694TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_WARNING, "mgmtQueuesSM: Got SmEvent=QUEUES_NOT_EMPTY when eSmState=%d\n", ePrevState);
695			}
696			break;
697
698		default:
699TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Unknown SmEvent = %d\n", eSmEvent);
700			break;
701	}
702
703TRACE6( 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]);
704
705	/*
706	 * Execute the required action.
707	 * Note: This is done at the end of the SM because it may start a sequence that will call the SM again!
708	 */
709	switch (eSmAction)
710	{
711		case SM_ACTION_NULL:
712			break;
713
714		case SM_ACTION_ENABLE_DATA:
715			txPort_enableData(pTxMgmtQ->hTxPort);
716			break;
717
718		case SM_ACTION_ENABLE_MGMT:
719			txPort_enableMgmt(pTxMgmtQ->hTxPort);
720			break;
721
722		case SM_ACTION_RUN_SCHEDULER:
723			runScheduler(pTxMgmtQ);
724			break;
725
726		default:
727TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, ": Unknown SmAction = %d\n", eSmAction);
728			break;
729	}
730}
731
732
733/**
734 * \fn     runSchedulerNotFromSm
735 * \brief  Run scheduler due to other events then from SM (static function)
736 *
737 * To comply with the SM behavior, this function is used for any case where the
738 *    Mgmt-Queues scheduler may have work to do due to events external to the SM.
739 * If the queues are not empty, this function runs the scheduler.
740*				If the scheduler emptied the queues, update the SM.
741 *
742 * \note
743 * \param  pTxMgmtQ - The module's object
744 * \return void
745 * \sa
746 */
747static void runSchedulerNotFromSm (TTxMgmtQ *pTxMgmtQ)
748{
749	/* If the queues are not empty, run the scheduler. */
750	if ( !ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) )
751	{
752		runScheduler (pTxMgmtQ);
753
754		/* If the queues are now both empty, call the SM with QUEUES_EMPTY event. */
755		if ( ARE_ALL_MGMT_QUEUES_EMPTY(pTxMgmtQ->aQueues) )
756        {
757			mgmtQueuesSM (pTxMgmtQ, SM_EVENT_QUEUES_EMPTY);
758        }
759	}
760}
761
762
763/**
764 * \fn     runScheduler
765 * \brief  The scheduler processing (static function)
766 *
767 * Loops over the mgmt-queues (high priority first) and if queue enabled and
768 *   has packets, dequeue a packet and send it to the TxCtrl.
769*				Exit if the port level is disabled or if couldn't send anything from both queues.
770 *
771 * \note   Protect the queues access against preemption from external context (EAPOL).
772 * \param  pTxMgmtQ - The module's object
773 * \return void
774 * \sa
775 */
776static void runScheduler (TTxMgmtQ *pTxMgmtQ)
777{
778	TI_STATUS  eStatus;
779    TTxCtrlBlk *pPktCtrlBlk;
780	TI_UINT32  uQueId = 0; /* start from highest priority queue */
781
782	while(1)
783	{
784		/* If the Mgmt port is closed exit. */
785		if ( !pTxMgmtQ->bMgmtPortEnable )
786		{
787			return;
788		}
789
790		/* Check that the current queue is not busy and is enabled by the state-machine. */
791		if ( !pTxMgmtQ->aQueueBusy[uQueId]  &&  pTxMgmtQ->aQueueEnabledBySM[uQueId])
792		{
793            /* Dequeue a packet in a critical section */
794            context_EnterCriticalSection (pTxMgmtQ->hContext);
795            pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pTxMgmtQ->aQueues[uQueId]);
796            context_LeaveCriticalSection (pTxMgmtQ->hContext);
797
798			if (pPktCtrlBlk)
799			{
800				pTxMgmtQ->tDbgCounters.aDequeuePackets[uQueId]++;
801
802				/* Send the packet */
803				eStatus = txCtrl_XmitMgmt (pTxMgmtQ->hTxCtrl, pPktCtrlBlk);
804
805				/* In case the return status is busy it means that the packet wasn't handled
806					 so we need to requeue the packet for future try. */
807				if(eStatus == STATUS_XMIT_BUSY)
808				{
809                    /* Requeue the packet in a critical section */
810                    context_EnterCriticalSection (pTxMgmtQ->hContext);
811                    que_Requeue (pTxMgmtQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk);
812                    context_LeaveCriticalSection (pTxMgmtQ->hContext);
813
814                    pTxMgmtQ->tDbgCounters.aRequeuePackets[uQueId]++;
815				}
816
817				/* The packet was handled by the lower Tx layers. */
818				else
819				{
820					pTxMgmtQ->tDbgCounters.aXmittedPackets[uQueId]++;
821
822					/* Successful delivery so start next tx from the high priority queue (mgmt),
823					 *	 giving it strict priority over the lower queue.
824					 */
825					uQueId = 0;
826					continue;
827				}
828			}
829		}
830
831
832		/* If we got here we couldn't deliver a packet from current queue, so progress to lower
833		 *	 priority queue and if already in lowest queue exit.
834		 */
835		uQueId++;
836		if (uQueId < NUM_OF_MGMT_QUEUES)
837			continue;	/* Try sending from next queue (i.e. the EAPOL queue). */
838		else
839			return;		/* We couldn't send from both queues so exit. */
840
841	} /* End of while */
842
843	/* Unreachable code */
844}
845
846
847/**
848 * \fn     updateQueuesBusyMap
849 * \brief  Update queues busy map (static function)
850 *
851 * Set the queues busy indication on or off according to the highest TID bit
852 *    in the tidBitMap (1 = busy).
853*				Note that both Mgmt and Eapol queues are mapped to TID 7.
854*
855 * \note
856 * \param  pTxMgmtQ   - The module's object
857 * \param  uTidBitMap - The TIDs bitmap of the queue(s) to update
858 * \return void
859 * \sa
860 */
861static void updateQueuesBusyMap (TTxMgmtQ *pTxMgmtQ, TI_UINT32 uTidBitMap)
862{
863	/* Set the queues busy indication on or off according to the highest TID bit (1 = busy). */
864	if(uTidBitMap & (1 << MGMT_QUEUES_TID) )
865	{
866		pTxMgmtQ->aQueueBusy[QUEUE_TYPE_MGMT ] = TI_TRUE;
867		pTxMgmtQ->aQueueBusy[QUEUE_TYPE_EAPOL] = TI_TRUE;
868	}
869	else
870	{
871		pTxMgmtQ->aQueueBusy[QUEUE_TYPE_MGMT ] = TI_FALSE;
872		pTxMgmtQ->aQueueBusy[QUEUE_TYPE_EAPOL] = TI_FALSE;
873	}
874}
875
876
877
878/*******************************************************************************
879*                       DEBUG  FUNCTIONS  IMPLEMENTATION					   *
880********************************************************************************/
881
882#ifdef TI_DBG
883
884/**
885 * \fn     txMgmtQ_PrintModuleParams
886 * \brief  Print module's parameters (debug)
887 *
888 * This function prints the module's parameters.
889 *
890 * \note
891 * \param  hTxMgmtQ - The module's object
892 * \return void
893 * \sa
894 */
895void txMgmtQ_PrintModuleParams (TI_HANDLE hTxMgmtQ)
896{
897	TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
898	TI_UINT32 uQueId;
899
900	WLAN_OS_REPORT(("-------------- txMgmtQueue Module Params -----------------\n"));
901	WLAN_OS_REPORT(("==========================================================\n"));
902
903	WLAN_OS_REPORT(("eSmState        = %d\n", pTxMgmtQ->eSmState));
904	WLAN_OS_REPORT(("bMgmtPortEnable = %d\n", pTxMgmtQ->bMgmtPortEnable));
905	WLAN_OS_REPORT(("eTxConnState    = %d\n", pTxMgmtQ->eTxConnState));
906	WLAN_OS_REPORT(("uContextId      = %d\n", pTxMgmtQ->uContextId));
907
908	WLAN_OS_REPORT(("-------------- Queues Busy (in HW) -----------------------\n"));
909	for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
910    {
911        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->aQueueBusy[uQueId]));
912    }
913
914	WLAN_OS_REPORT(("-------------- Queues Enabled By SM ----------------------\n"));
915	for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
916    {
917        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->aQueueBusy[uQueId]));
918    }
919
920	WLAN_OS_REPORT(("-------------- Queues Info -------------------------------\n"));
921	for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
922    {
923        WLAN_OS_REPORT(("Que %d:\n", uQueId));
924        que_Print (pTxMgmtQ->aQueues[uQueId]);
925    }
926
927	WLAN_OS_REPORT(("==========================================================\n\n"));
928}
929
930
931/**
932 * \fn     txMgmtQ_PrintQueueStatistics
933 * \brief  Print queues statistics (debug)
934 *
935 * This function prints the module's Tx statistics per Queue.
936 *
937 * \note
938 * \param  hTxMgmtQ - The module's object
939 * \return void
940 * \sa
941 */
942void txMgmtQ_PrintQueueStatistics (TI_HANDLE hTxMgmtQ)
943{
944#ifdef REPORT_LOG
945    TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
946    TI_UINT32 uQueId;
947
948    WLAN_OS_REPORT(("-------------- Mgmt Queues Statistics  -------------------\n"));
949    WLAN_OS_REPORT(("==========================================================\n"));
950
951    WLAN_OS_REPORT(("-------------- Enqueue Packets ---------------------------\n"));
952    for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
953        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->tDbgCounters.aEnqueuePackets[uQueId]));
954
955    WLAN_OS_REPORT(("-------------- Dequeue Packets ---------------------------\n"));
956    for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
957        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->tDbgCounters.aDequeuePackets[uQueId]));
958
959    WLAN_OS_REPORT(("-------------- Requeue Packets ---------------------------\n"));
960    for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
961        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->tDbgCounters.aRequeuePackets[uQueId]));
962
963    WLAN_OS_REPORT(("-------------- Xmitted Packets ---------------------------\n"));
964    for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
965        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->tDbgCounters.aXmittedPackets[uQueId]));
966
967    WLAN_OS_REPORT(("-------------- Dropped Packets (queue full) --------------\n"));
968    for(uQueId = 0; uQueId < NUM_OF_MGMT_QUEUES; uQueId++)
969        WLAN_OS_REPORT(("Que[%d]:  %d\n", uQueId, pTxMgmtQ->tDbgCounters.aDroppedPackets[uQueId]));
970
971    WLAN_OS_REPORT(("==========================================================\n\n"));
972#endif
973}
974
975
976/**
977 * \fn     txMgmtQ_ResetQueueStatistics
978 * \brief  Reset queues statistics (debug)
979 *
980 * This function Resets the module's Tx statistics per Queue.
981 *
982 * \note
983 * \param  hTxMgmtQ - The module's object
984 * \return void
985 * \sa
986 */
987void txMgmtQ_ResetQueueStatistics (TI_HANDLE hTxMgmtQ)
988{
989	TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ;
990
991    os_memoryZero(pTxMgmtQ->hOs, (void *)&(pTxMgmtQ->tDbgCounters), sizeof(TDbgCount));
992}
993
994#endif /* TI_DBG */
995
996