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