1/* 2 * TxnQueue.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/** \file TxnQueue.c 36 * \brief The transaction-queue module. 37 * 38 * The Transaction Queue encapsulates the bus access from a functional driver (WLAN, BT). 39 * This TI proprietary module presents the same interface and same behavior for different 40 * bus configuration: SDIO (multi or single function) or SPI and for different modes 41 * of operation: Synchronous, a-synchronous or combination of both. 42 * It will also be used over the RS232 interface (using wUART protocol) which is applicable 43 * for RS applications (on PC). 44 * 45 * The TxnQ module provides the following requirements: 46 * Inter process protection on queue's internal database and synchronization between 47 * functional drivers that share the bus. 48 * Support multiple queues per function, handled by priority. 49 * Support the TTxnStruct API (as the Bus Driver) with the ability to manage commands 50 * queuing of multiple functions on top of the Bus Driver. 51 * The TxnQ (as well as the layers above it) is agnostic to the bus driver used beneath it 52 * (SDIO, WSPI or wUART), since all bus drivers introduce the same API and hide bus details. 53 * The TxnQ has no OS dependencies. It supports access from multiple OS threads. 54 * Note: It is assumed that any transaction forwarded to the TxnQ has enough resources in HW. 55 * 56 * \see TxnQueue.h 57 */ 58 59#define __FILE_ID__ FILE_ID_123 60#include "tidef.h" 61#include "report.h" 62#include "context.h" 63#include "osApi.h" 64#include "TxnDefs.h" 65#include "BusDrv.h" 66#include "TxnQueue.h" 67 68 69 70/************************************************************************ 71 * Defines 72 ************************************************************************/ 73#define MAX_FUNCTIONS 4 /* Maximum 4 functional drivers (including Func 0 which is for bus control) */ 74#define MAX_PRIORITY 2 /* Maximum 2 prioritys per functional driver */ 75#define TXN_QUE_SIZE 64 /* Txn-queue size */ 76#define TXN_DONE_QUE_SIZE 64 /* TxnDone-queue size */ 77 78 79/************************************************************************ 80 * Types 81 ************************************************************************/ 82 83/* Functional driver's SM States */ 84typedef enum 85{ 86 FUNC_STATE_NONE, /* Function not registered */ 87 FUNC_STATE_STOPPED, /* Queues are stopped */ 88 FUNC_STATE_RUNNING, /* Queues are running */ 89 FUNC_STATE_RESTART /* Wait for current Txn to finish before restarting queues */ 90} EFuncState; 91 92/* The functional drivers registered to TxnQ */ 93typedef struct 94{ 95 EFuncState eState; /* Function crrent state */ 96 TI_UINT32 uNumPrios; /* Number of queues (priorities) for this function */ 97 TTxnQueueDoneCb fTxnQueueDoneCb; /* The CB called by the TxnQueue upon full transaction completion. */ 98 TI_HANDLE hCbHandle; /* The callback handle */ 99 TTxnStruct * pSingleStep; /* A single step transaction waiting to be sent */ 100 101} TFuncInfo; 102 103 104/* The TxnQueue module Object */ 105typedef struct _TTxnQObj 106{ 107 TI_HANDLE hOs; 108 TI_HANDLE hReport; 109 TI_HANDLE hContext; 110 TI_HANDLE hBusDrv; 111 112 TFuncInfo aFuncInfo[MAX_FUNCTIONS]; /* Registered functional drivers - see above */ 113 TI_HANDLE aTxnQueues[MAX_FUNCTIONS][MAX_PRIORITY]; /* Handle of the Transactions-Queue */ 114 TI_HANDLE hTxnDoneQueue; /* Queue for completed transactions not reported to yet to the upper layer */ 115 TTxnStruct * pCurrTxn; /* The transaction currently processed in the bus driver (NULL if none) */ 116 TI_UINT32 uMinFuncId; /* The minimal function ID actually registered (through txnQ_Open) */ 117 TI_UINT32 uMaxFuncId; /* The maximal function ID actually registered (through txnQ_Open) */ 118 TI_BOOL bSchedulerBusy; /* If set, the scheduler is currently running so it shouldn't be reentered */ 119 TI_BOOL bSchedulerPend; /* If set, a call to the scheduler was postponed because it was busy */ 120 121 /* Environment dependent: TRUE if needed and allowed to protect TxnDone in critical section */ 122 TTxnDoneCb fConnectCb; 123 TI_HANDLE hConnectCb; 124 125} TTxnQObj; 126 127 128/************************************************************************ 129 * Internal functions prototypes 130 ************************************************************************/ 131static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn); 132static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn); 133static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn); 134static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ); 135static void txnQ_ClearQueues (TTxnQObj *pTxnQ, TI_UINT32 uFuncId); 136static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn); 137 138 139 140/************************************************************************ 141 * 142 * Module functions implementation 143 * 144 ************************************************************************/ 145 146TI_HANDLE txnQ_Create (TI_HANDLE hOs) 147{ 148 TI_HANDLE hTxnQ; 149 TTxnQObj *pTxnQ; 150 TI_UINT32 i; 151 152 hTxnQ = os_memoryAlloc(hOs, sizeof(TTxnQObj)); 153 if (hTxnQ == NULL) 154 return NULL; 155 156 pTxnQ = (TTxnQObj *)hTxnQ; 157 158 os_memoryZero(hOs, hTxnQ, sizeof(TTxnQObj)); 159 160 pTxnQ->hOs = hOs; 161 pTxnQ->pCurrTxn = NULL; 162 pTxnQ->uMinFuncId = MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */ 163 pTxnQ->uMaxFuncId = 0; /* Start at minimum and save maximal value in txnQ_Open */ 164 165 for (i = 0; i < MAX_FUNCTIONS; i++) 166 { 167 pTxnQ->aFuncInfo[i].eState = FUNC_STATE_NONE; 168 pTxnQ->aFuncInfo[i].uNumPrios = 0; 169 pTxnQ->aFuncInfo[i].pSingleStep = NULL; 170 pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL; 171 pTxnQ->aFuncInfo[i].hCbHandle = NULL; 172 } 173 174 /* Create the Bus-Driver module */ 175 pTxnQ->hBusDrv = busDrv_Create (hOs); 176 if (pTxnQ->hBusDrv == NULL) 177 { 178 WLAN_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__)); 179 txnQ_Destroy (hTxnQ); 180 return NULL; 181 } 182 183 return pTxnQ; 184} 185 186TI_STATUS txnQ_Destroy (TI_HANDLE hTxnQ) 187{ 188 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 189 190 if (pTxnQ) 191 { 192 if (pTxnQ->hBusDrv) 193 { 194 busDrv_Destroy (pTxnQ->hBusDrv); 195 } 196 if (pTxnQ->hTxnDoneQueue) 197 { 198 que_Destroy (pTxnQ->hTxnDoneQueue); 199 } 200 os_memoryFree (pTxnQ->hOs, pTxnQ, sizeof(TTxnQObj)); 201 } 202 return TI_OK; 203} 204 205void txnQ_Init (TI_HANDLE hTxnQ, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext) 206{ 207 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 208 TI_UINT32 uNodeHeaderOffset; 209 210 pTxnQ->hOs = hOs; 211 pTxnQ->hReport = hReport; 212 pTxnQ->hContext = hContext; 213 214 /* Create the TxnDone queue. */ 215 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 216 pTxnQ->hTxnDoneQueue = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset); 217 if (pTxnQ->hTxnDoneQueue == NULL) 218 { 219 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": TxnDone queue creation failed!\n"); 220 } 221 222 busDrv_Init (pTxnQ->hBusDrv, hReport); 223} 224 225TI_STATUS txnQ_ConnectBus (TI_HANDLE hTxnQ, TBusDrvCfg *pBusDrvCfg, TTxnDoneCb fConnectCb, TI_HANDLE hConnectCb) 226{ 227 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 228 229 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_ConnectBus()\n"); 230 231 pTxnQ->fConnectCb = fConnectCb; 232 pTxnQ->hConnectCb = hConnectCb; 233 234 return busDrv_ConnectBus (pTxnQ->hBusDrv, pBusDrvCfg, txnQ_TxnDoneCb, hTxnQ, txnQ_ConnectCB); 235} 236 237TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ) 238{ 239 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 240 241 return busDrv_DisconnectBus (pTxnQ->hBusDrv); 242} 243 244TI_STATUS txnQ_Open (TI_HANDLE hTxnQ, 245 TI_UINT32 uFuncId, 246 TI_UINT32 uNumPrios, 247 TTxnQueueDoneCb fTxnQueueDoneCb, 248 TI_HANDLE hCbHandle) 249{ 250 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 251 TI_UINT32 uNodeHeaderOffset; 252 TI_UINT32 i; 253 254 if (uFuncId >= MAX_FUNCTIONS || uNumPrios > MAX_PRIORITY) 255 { 256 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Invalid Params! uFuncId = %d, uNumPrios = %d\n", uFuncId, uNumPrios); 257 return TI_NOK; 258 } 259 260 context_EnterCriticalSection (pTxnQ->hContext); 261 262 /* Save functional driver info */ 263 pTxnQ->aFuncInfo[uFuncId].uNumPrios = uNumPrios; 264 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = fTxnQueueDoneCb; 265 pTxnQ->aFuncInfo[uFuncId].hCbHandle = hCbHandle; 266 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED; 267 268 /* Create the functional driver's queues. */ 269 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 270 for (i = 0; i < uNumPrios; i++) 271 { 272 pTxnQ->aTxnQueues[uFuncId][i] = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_QUE_SIZE, uNodeHeaderOffset); 273 if (pTxnQ->aTxnQueues[uFuncId][i] == NULL) 274 { 275 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Queues creation failed!\n"); 276 context_LeaveCriticalSection (pTxnQ->hContext); 277 return TI_NOK; 278 } 279 } 280 281 /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */ 282 if (uFuncId < pTxnQ->uMinFuncId) 283 { 284 pTxnQ->uMinFuncId = uFuncId; 285 } 286 if (uFuncId > pTxnQ->uMaxFuncId) 287 { 288 pTxnQ->uMaxFuncId = uFuncId; 289 } 290 291 context_LeaveCriticalSection (pTxnQ->hContext); 292 293 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d registered successfully, uNumPrios = %d\n", uFuncId, uNumPrios); 294 295 return TI_OK; 296} 297 298void txnQ_Close (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 299{ 300 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 301 TI_UINT32 i; 302 303 context_EnterCriticalSection (pTxnQ->hContext); 304 305 /* Destroy the functional driver's queues */ 306 for (i = 0; i < pTxnQ->aFuncInfo[uFuncId].uNumPrios; i++) 307 { 308 que_Destroy (pTxnQ->aTxnQueues[uFuncId][i]); 309 } 310 311 /* Clear functional driver info */ 312 pTxnQ->aFuncInfo[uFuncId].uNumPrios = 0; 313 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = NULL; 314 pTxnQ->aFuncInfo[uFuncId].hCbHandle = NULL; 315 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_NONE; 316 317 /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */ 318 pTxnQ->uMinFuncId = MAX_FUNCTIONS; 319 pTxnQ->uMaxFuncId = 0; 320 for (i = 0; i < MAX_FUNCTIONS; i++) 321 { 322 if (pTxnQ->aFuncInfo[i].eState != FUNC_STATE_NONE) 323 { 324 if (i < pTxnQ->uMinFuncId) 325 { 326 pTxnQ->uMinFuncId = i; 327 } 328 if (i > pTxnQ->uMaxFuncId) 329 { 330 pTxnQ->uMaxFuncId = i; 331 } 332 } 333 } 334 335 context_LeaveCriticalSection (pTxnQ->hContext); 336 337 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d Unregistered\n", uFuncId); 338} 339 340ETxnStatus txnQ_Restart (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 341{ 342 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 343 344 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart()\n"); 345 346 context_EnterCriticalSection (pTxnQ->hContext); 347 348 /* If a Txn from the calling function is in progress, set state to RESTART return PENDING */ 349 if (pTxnQ->pCurrTxn) 350 { 351 if (TXN_PARAM_GET_FUNC_ID(pTxnQ->pCurrTxn) == uFuncId) 352 { 353 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RESTART; 354 355 context_LeaveCriticalSection (pTxnQ->hContext); 356 357 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart(): pCurrTxn pending\n"); 358 359 /* Return PENDING to indicate that the restart will be completed later (in TxnDone) */ 360 return TXN_STATUS_PENDING; 361 } 362 } 363 364 context_LeaveCriticalSection (pTxnQ->hContext); 365 366 /* Clear the calling function's queues (call function CB with status=RECOVERY) */ 367 txnQ_ClearQueues (pTxnQ, uFuncId); 368 369 /* Return COMPLETE to indicate that the restart was completed */ 370 return TXN_STATUS_COMPLETE; 371} 372 373void txnQ_Run (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 374{ 375 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 376 377#ifdef TI_DBG 378 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Run()\n"); 379 if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_STOPPED) 380 { 381 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_WARNING, "txnQ_Run(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState); 382 } 383#endif 384 385 /* Enable function's queues */ 386 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING; 387 388 /* Send queued transactions as possible */ 389 txnQ_RunScheduler (pTxnQ, NULL); 390} 391 392void txnQ_Stop (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 393{ 394 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 395 396#ifdef TI_DBG 397 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Stop()\n"); 398 if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_RUNNING) 399 { 400 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Stop(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState); 401 } 402#endif 403 404 /* Enable function's queues */ 405 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED; 406} 407 408ETxnStatus txnQ_Transact (TI_HANDLE hTxnQ, TTxnStruct *pTxn) 409{ 410 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 411 TI_UINT32 uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn); 412 ETxnStatus rc; 413 414 if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) 415 { 416 pTxnQ->aFuncInfo[uFuncId].pSingleStep = pTxn; 417 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Single step Txn\n"); 418 } 419 else 420 { 421 TI_HANDLE hQueue = pTxnQ->aTxnQueues[uFuncId][TXN_PARAM_GET_PRIORITY(pTxn)]; 422 context_EnterCriticalSection (pTxnQ->hContext); 423 que_Enqueue (hQueue, (TI_HANDLE)pTxn); 424 context_LeaveCriticalSection (pTxnQ->hContext); 425 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Regular Txn\n"); 426 } 427 428 /* Send queued transactions as possible */ 429 rc = txnQ_RunScheduler (pTxnQ, pTxn); 430 431 return rc; 432} 433 434 435/** 436 * \fn txnQ_ConnectCB 437 * \brief Pending Connection completion CB 438 * 439 * txnQ_ConnectBus CB 440 * 441 * \note 442 * \param hTxnQ - The module's object 443 * \param pTxn - The completed transaction object 444 * \return void 445 * \sa 446 */ 447static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn) 448{ 449 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 450 451 /* Call the Client Connect CB */ 452 pTxnQ->fConnectCb (pTxnQ->hConnectCb, NULL); 453} 454 455 456/** 457 * \fn txnQ_TxnDoneCb 458 * \brief Pending Transaction completion CB 459 * 460 * Called back by bus-driver upon pending transaction completion in TxnDone context (external!). 461 * Enqueue completed transaction in TxnDone queue and call scheduler to send queued transactions. 462 * 463 * \note 464 * \param hTxnQ - The module's object 465 * \param pTxn - The completed transaction object 466 * \return void 467 * \sa 468 */ 469static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn) 470{ 471 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 472 TTxnStruct *pTxn = (TTxnStruct *)hTxn; 473 TI_UINT32 uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn); 474 475#ifdef TI_DBG 476 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb()\n"); 477 if (pTxn != pTxnQ->pCurrTxn) 478 { 479 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): CB returned pTxn 0x%x while pCurrTxn is 0x%x !!\n", pTxn, pTxnQ->pCurrTxn); 480 } 481#endif 482 483 /* If the function of the completed Txn is waiting for restart */ 484 if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART) 485 { 486 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb(): Handling restart\n"); 487 488 /* First, Clear the restarted function queues */ 489 txnQ_ClearQueues (pTxnQ, uFuncId); 490 491 /* Call function CB for current Txn with restart indication */ 492 TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_RECOVERY); 493 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn); 494 } 495 496 /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */ 497 else 498 { 499 context_EnterCriticalSection (pTxnQ->hContext); 500 que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pTxn); 501 context_LeaveCriticalSection (pTxnQ->hContext); 502 } 503 504 /* Indicate that no transaction is currently processed in the bus-driver */ 505 pTxnQ->pCurrTxn = NULL; 506 507 /* Send queued transactions as possible (TRUE indicates we are in external context) */ 508 txnQ_RunScheduler (pTxnQ, NULL); 509} 510 511 512/** 513 * \fn txnQ_RunScheduler 514 * \brief Send queued transactions 515 * 516 * Run the scheduler, which issues transactions as long as possible. 517 * Since this function is called from either internal or external (TxnDone) context, 518 * it handles reentry by setting a bSchedulerPend flag, and running the scheduler again 519 * when its current iteration is finished. 520 * 521 * \note 522 * \param pTxnQ - The module's object 523 * \param pInputTxn - The transaction inserted in the current context (NULL if none) 524 * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed 525 * \sa 526 */ 527static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn) 528{ 529 TI_BOOL bFirstIteration; 530 ETxnStatus eStatus = TXN_STATUS_NONE; 531 532 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler()\n"); 533 534 context_EnterCriticalSection (pTxnQ->hContext); 535 536 /* If the scheduler is currently busy, set bSchedulerPend flag and exit */ 537 if (pTxnQ->bSchedulerBusy) 538 { 539 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Scheduler is busy\n"); 540 pTxnQ->bSchedulerPend = TI_TRUE; 541 context_LeaveCriticalSection (pTxnQ->hContext); 542 return TXN_STATUS_PENDING; 543 } 544 545 /* Indicate that the scheduler is currently busy */ 546 pTxnQ->bSchedulerBusy = TI_TRUE; 547 548 context_LeaveCriticalSection (pTxnQ->hContext); 549 550 bFirstIteration = TI_TRUE; 551 552 /* 553 * Run the scheduler while it has work to do 554 */ 555 while (1) 556 { 557 /* If first scheduler iteration, save its return code to return the original Txn result */ 558 if (bFirstIteration) 559 { 560 eStatus = txnQ_Scheduler (pTxnQ, pInputTxn); 561 bFirstIteration = TI_FALSE; 562 } 563 /* This is for handling pending calls when the scheduler was busy (see above) */ 564 else 565 { 566 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Handle pending scheduler call\n"); 567 txnQ_Scheduler (pTxnQ, NULL); 568 } 569 570 context_EnterCriticalSection (pTxnQ->hContext); 571 572 /* If no pending calls, clear the busy flag and return the original caller Txn status */ 573 if (!pTxnQ->bSchedulerPend) 574 { 575 pTxnQ->bSchedulerBusy = TI_FALSE; 576 context_LeaveCriticalSection (pTxnQ->hContext); 577 return eStatus; 578 } 579 pTxnQ->bSchedulerPend = TI_FALSE; 580 581 context_LeaveCriticalSection (pTxnQ->hContext); 582 } 583} 584 585 586/** 587 * \fn txnQ_Scheduler 588 * \brief Send queued transactions 589 * 590 * Issue transactions as long as they are available and the bus is not occupied. 591 * Call CBs of completed transactions, except completion of pInputTxn (covered by the return value). 592 * Note that this function is called from either internal or external (TxnDone) context. 593 * However, the txnQ_RunScheduler which calls it, prevents scheduler reentry. 594 * 595 * \note 596 * \param pTxnQ - The module's object 597 * \param pInputTxn - The transaction inserted in the current context (NULL if none) 598 * \return COMPLETE if pInputTxn completed in this context, PENDING if not, ERROR if failed 599 * \sa txnQ_RunScheduler 600 */ 601static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn) 602{ 603 ETxnStatus eInputTxnStatus; 604 605 /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */ 606 eInputTxnStatus = TXN_STATUS_PENDING; 607 608 /* if a previous transaction is in progress, return PENDING */ 609 if (pTxnQ->pCurrTxn) 610 { 611 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): pCurrTxn isn't null (0x%x) so exit\n", pTxnQ->pCurrTxn); 612 return TXN_STATUS_PENDING; 613 } 614 615 /* Loop while transactions are available and can be sent to bus driver */ 616 while (1) 617 { 618 TTxnStruct *pSelectedTxn; 619 ETxnStatus eStatus; 620 621 /* Get next enabled transaction by priority. If none, exit loop. */ 622 context_EnterCriticalSection (pTxnQ->hContext); 623 pSelectedTxn = txnQ_SelectTxn (pTxnQ); 624 context_LeaveCriticalSection (pTxnQ->hContext); 625 if (pSelectedTxn == NULL) 626 { 627 break; 628 } 629 630 /* Save transaction in case it will be async (to indicate that the bus driver is busy) */ 631 pTxnQ->pCurrTxn = pSelectedTxn; 632 633 /* Send selected transaction to bus driver */ 634 eStatus = busDrv_Transact (pTxnQ->hBusDrv, pSelectedTxn); 635 636 /* If we've just sent the input transaction, use the status as the return value */ 637 if (pSelectedTxn == pInputTxn) 638 { 639 eInputTxnStatus = eStatus; 640 } 641 642 TRACE3(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Txn 0x%x sent, status = %d, eInputTxnStatus = %d\n", pSelectedTxn, eStatus, eInputTxnStatus); 643 644 /* If transaction completed */ 645 if (eStatus != TXN_STATUS_PENDING) 646 { 647 pTxnQ->pCurrTxn = NULL; 648 649 /* If it's not the input transaction, enqueue it in TxnDone queue */ 650 if (pSelectedTxn != pInputTxn) 651 { 652 context_EnterCriticalSection (pTxnQ->hContext); 653 que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pSelectedTxn); 654 context_LeaveCriticalSection (pTxnQ->hContext); 655 } 656 } 657 658 /* If pending Exit loop! */ 659 else 660 { 661 break; 662 } 663 } 664 665 /* Dequeue completed transactions and call their functional driver CB */ 666 /* Note that it's the functional driver CB and not the specific CB in the Txn! */ 667 while (1) 668 { 669 TTxnStruct *pCompletedTxn; 670 TI_UINT32 uFuncId; 671 TTxnQueueDoneCb fTxnQueueDoneCb; 672 TI_HANDLE hCbHandle; 673 674 context_EnterCriticalSection (pTxnQ->hContext); 675 pCompletedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue); 676 context_LeaveCriticalSection (pTxnQ->hContext); 677 if (pCompletedTxn == NULL) 678 { 679 /* Return the status of the input transaction (PENDING unless sent and completed here) */ 680 return eInputTxnStatus; 681 } 682 683 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Calling TxnDone for Txn 0x%x\n", pCompletedTxn); 684 685 uFuncId = TXN_PARAM_GET_FUNC_ID(pCompletedTxn); 686 fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb; 687 hCbHandle = pTxnQ->aFuncInfo[uFuncId].hCbHandle; 688 689 fTxnQueueDoneCb (hCbHandle, pCompletedTxn); 690 } 691} 692 693 694/** 695 * \fn txnQ_SelectTxn 696 * \brief Select transaction to send 697 * 698 * Called from txnQ_RunScheduler() which is protected in critical section. 699 * Select the next enabled transaction by priority. 700 * 701 * \note 702 * \param pTxnQ - The module's object 703 * \return The selected transaction to send (NULL if none available) 704 * \sa 705 */ 706static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ) 707{ 708 TTxnStruct *pSelectedTxn; 709 TI_UINT32 uFunc; 710 TI_UINT32 uPrio; 711 712 /* For all functions, if single-step Txn waiting, return it (sent even if function is stopped) */ 713 for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++) 714 { 715 pSelectedTxn = pTxnQ->aFuncInfo[uFunc].pSingleStep; 716 if (pSelectedTxn != NULL) 717 { 718 pTxnQ->aFuncInfo[uFunc].pSingleStep = NULL; 719 return pSelectedTxn; 720 } 721 } 722 723 /* For all priorities from high to low */ 724 for (uPrio = 0; uPrio < MAX_PRIORITY; uPrio++) 725 { 726 /* For all functions */ 727 for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++) 728 { 729 /* If function running and uses this priority */ 730 if (pTxnQ->aFuncInfo[uFunc].eState == FUNC_STATE_RUNNING && 731 pTxnQ->aFuncInfo[uFunc].uNumPrios > uPrio) 732 { 733 /* Dequeue Txn from current func and priority queue, and if not NULL return it */ 734 pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFunc][uPrio]); 735 if (pSelectedTxn != NULL) 736 { 737 return pSelectedTxn; 738 } 739 } 740 } 741 } 742 743 /* If no transaction was selected, return NULL */ 744 return NULL; 745} 746 747 748/** 749 * \fn txnQ_ClearQueues 750 * \brief Clear the function queues 751 * 752 * Clear the specified function queues and call its CB for each Txn with status=RECOVERY. 753 * 754 * \note Called in critical section. 755 * \param pTxnQ - The module's object 756 * \param uFuncId - The calling functional driver 757 * \return void 758 * \sa 759 */ 760static void txnQ_ClearQueues (TTxnQObj *pTxnQ, TI_UINT32 uFuncId) 761{ 762 TTxnStruct *pTxn; 763 TI_UINT32 uPrio; 764 765 pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL; 766 767 /* For all function priorities */ 768 for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++) 769 { 770 do 771 { 772 context_EnterCriticalSection (pTxnQ->hContext); 773 /* Dequeue Txn from current priority queue */ 774 pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]); 775 context_LeaveCriticalSection (pTxnQ->hContext); 776 777 /* 778 * Drop on Restart 779 * do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback 780 */ 781 } while (pTxn != NULL); 782 } 783 784 /* Clear state - for restart (doesn't call txnQ_Open) */ 785 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING; 786} 787 788#ifdef TI_DBG 789void txnQ_PrintQueues (TI_HANDLE hTxnQ) 790{ 791 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 792 793 WLAN_OS_REPORT(("Print TXN queues\n")); 794 WLAN_OS_REPORT(("================\n")); 795 que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_LOW_PRIORITY]); 796 que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_HIGH_PRIORITY]); 797} 798#endif /* TI_DBG */ 799