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
74 typedef 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 */
82 typedef 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 */
93 typedef 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 */
103 typedef 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
115 typedef 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. */
125 typedef 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 */
150 static void mgmtQueuesSM (TTxMgmtQ *pTxMgmtQ, ESmEvent smEvent);
151 static void runSchedulerNotFromSm (TTxMgmtQ *pTxMgmtQ);
152 static void runScheduler (TTxMgmtQ *pTxMgmtQ);
153 static 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 */
txMgmtQ_Create(TI_HANDLE hOs)171 TI_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 */
txMgmtQ_Init(TStadHandlesList * pStadHandles)204 void 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
250 TRACE0(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 */
txMgmtQ_Destroy(TI_HANDLE hTxMgmtQ)265 TI_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 */
txMgmtQ_ClearQueues(TI_HANDLE hTxMgmtQ)302 void 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 */
txMgmtQ_Xmit(TI_HANDLE hTxMgmtQ,TTxCtrlBlk * pPktCtrlBlk,TI_BOOL bExternalContext)346 TI_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 */
txMgmtQ_QueuesNotEmpty(TI_HANDLE hTxMgmtQ)417 void 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 */
txMgmtQ_StopQueue(TI_HANDLE hTxMgmtQ,TI_UINT32 uTidBitMap)439 void 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 */
txMgmtQ_UpdateBusyMap(TI_HANDLE hTxMgmtQ,TI_UINT32 uTidBitMap)462 void 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 */
txMgmtQ_StopAll(TI_HANDLE hTxMgmtQ)486 void 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 */
txMgmtQ_WakeAll(TI_HANDLE hTxMgmtQ)507 void 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 */
txMgmtQ_SetConnState(TI_HANDLE hTxMgmtQ,ETxConnState eTxConnState)533 void 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:
548 TRACE1(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 */
mgmtQueuesSM(TTxMgmtQ * pTxMgmtQ,ESmEvent eSmEvent)574 static 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 {
616 TRACE1(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 {
631 TRACE1(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. */
661 TRACE1(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. */
694 TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_WARNING, "mgmtQueuesSM: Got SmEvent=QUEUES_NOT_EMPTY when eSmState=%d\n", ePrevState);
695 }
696 break;
697
698 default:
699 TRACE1(pTxMgmtQ->hReport, REPORT_SEVERITY_ERROR, "mgmtQueuesSM: Unknown SmEvent = %d\n", eSmEvent);
700 break;
701 }
702
703 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]);
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:
727 TRACE1(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 */
runSchedulerNotFromSm(TTxMgmtQ * pTxMgmtQ)747 static 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 */
runScheduler(TTxMgmtQ * pTxMgmtQ)776 static 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 */
updateQueuesBusyMap(TTxMgmtQ * pTxMgmtQ,TI_UINT32 uTidBitMap)861 static 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 */
txMgmtQ_PrintModuleParams(TI_HANDLE hTxMgmtQ)895 void 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 */
txMgmtQ_PrintQueueStatistics(TI_HANDLE hTxMgmtQ)942 void 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 */
txMgmtQ_ResetQueueStatistics(TI_HANDLE hTxMgmtQ)987 void 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