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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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