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 QUE_UNLIMITED_SIZE 76 #define TXN_DONE_QUE_SIZE QUE_UNLIMITED_SIZE 77 78 79 /************************************************************************ 80 * Types 81 ************************************************************************/ 82 83 /* Functional driver's SM States */ 84 typedef 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 */ 93 typedef 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 */ 105 typedef 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 #ifdef TI_DBG 126 TI_HANDLE pAggregQueue; /* While Tx aggregation in progress, saves its queue pointer to ensure continuity */ 127 #endif 128 129 } TTxnQObj; 130 131 132 /************************************************************************ 133 * Internal functions prototypes 134 ************************************************************************/ 135 static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn); 136 static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn); 137 static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn); 138 static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ); 139 static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn); 140 141 142 143 /************************************************************************ 144 * 145 * Module functions implementation 146 * 147 ************************************************************************/ 148 149 TI_HANDLE txnQ_Create (TI_HANDLE hOs) 150 { 151 TI_HANDLE hTxnQ; 152 TTxnQObj *pTxnQ; 153 TI_UINT32 i; 154 155 hTxnQ = os_memoryAlloc(hOs, sizeof(TTxnQObj)); 156 if (hTxnQ == NULL) 157 return NULL; 158 159 pTxnQ = (TTxnQObj *)hTxnQ; 160 161 os_memoryZero(hOs, hTxnQ, sizeof(TTxnQObj)); 162 163 pTxnQ->hOs = hOs; 164 pTxnQ->pCurrTxn = NULL; 165 pTxnQ->uMinFuncId = MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */ 166 pTxnQ->uMaxFuncId = 0; /* Start at minimum and save maximal value in txnQ_Open */ 167 #ifdef TI_DBG 168 pTxnQ->pAggregQueue = NULL; 169 #endif 170 171 for (i = 0; i < MAX_FUNCTIONS; i++) 172 { 173 pTxnQ->aFuncInfo[i].eState = FUNC_STATE_NONE; 174 pTxnQ->aFuncInfo[i].uNumPrios = 0; 175 pTxnQ->aFuncInfo[i].pSingleStep = NULL; 176 pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL; 177 pTxnQ->aFuncInfo[i].hCbHandle = NULL; 178 } 179 180 /* Create the Bus-Driver module */ 181 pTxnQ->hBusDrv = busDrv_Create (hOs); 182 if (pTxnQ->hBusDrv == NULL) 183 { 184 WLAN_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__)); 185 txnQ_Destroy (hTxnQ); 186 return NULL; 187 } 188 189 return pTxnQ; 190 } 191 192 TI_STATUS txnQ_Destroy (TI_HANDLE hTxnQ) 193 { 194 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 195 196 if (pTxnQ) 197 { 198 if (pTxnQ->hBusDrv) 199 { 200 busDrv_Destroy (pTxnQ->hBusDrv); 201 } 202 if (pTxnQ->hTxnDoneQueue) 203 { 204 que_Destroy (pTxnQ->hTxnDoneQueue); 205 } 206 os_memoryFree (pTxnQ->hOs, pTxnQ, sizeof(TTxnQObj)); 207 } 208 return TI_OK; 209 } 210 211 void txnQ_Init (TI_HANDLE hTxnQ, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext) 212 { 213 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 214 TI_UINT32 uNodeHeaderOffset; 215 216 pTxnQ->hOs = hOs; 217 pTxnQ->hReport = hReport; 218 pTxnQ->hContext = hContext; 219 220 /* Create the TxnDone queue. */ 221 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 222 pTxnQ->hTxnDoneQueue = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset); 223 if (pTxnQ->hTxnDoneQueue == NULL) 224 { 225 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": TxnDone queue creation failed!\n"); 226 } 227 228 busDrv_Init (pTxnQ->hBusDrv, hReport); 229 } 230 231 TI_STATUS txnQ_ConnectBus (TI_HANDLE hTxnQ, 232 TBusDrvCfg *pBusDrvCfg, 233 TTxnDoneCb fConnectCb, 234 TI_HANDLE hConnectCb, 235 TI_UINT32 *pRxDmaBufLen, 236 TI_UINT32 *pTxDmaBufLen) 237 { 238 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 239 240 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_ConnectBus()\n"); 241 242 pTxnQ->fConnectCb = fConnectCb; 243 pTxnQ->hConnectCb = hConnectCb; 244 245 return busDrv_ConnectBus (pTxnQ->hBusDrv, pBusDrvCfg, txnQ_TxnDoneCb, hTxnQ, txnQ_ConnectCB, pRxDmaBufLen, pTxDmaBufLen); 246 } 247 248 TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ) 249 { 250 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 251 252 return busDrv_DisconnectBus (pTxnQ->hBusDrv); 253 } 254 255 TI_STATUS txnQ_Open (TI_HANDLE hTxnQ, 256 TI_UINT32 uFuncId, 257 TI_UINT32 uNumPrios, 258 TTxnQueueDoneCb fTxnQueueDoneCb, 259 TI_HANDLE hCbHandle) 260 { 261 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 262 TI_UINT32 uNodeHeaderOffset; 263 TI_UINT32 i; 264 265 if (uFuncId >= MAX_FUNCTIONS || uNumPrios > MAX_PRIORITY) 266 { 267 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Invalid Params! uFuncId = %d, uNumPrios = %d\n", uFuncId, uNumPrios); 268 return TI_NOK; 269 } 270 271 context_EnterCriticalSection (pTxnQ->hContext); 272 273 /* Save functional driver info */ 274 pTxnQ->aFuncInfo[uFuncId].uNumPrios = uNumPrios; 275 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = fTxnQueueDoneCb; 276 pTxnQ->aFuncInfo[uFuncId].hCbHandle = hCbHandle; 277 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED; 278 279 /* Create the functional driver's queues. */ 280 uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 281 for (i = 0; i < uNumPrios; i++) 282 { 283 pTxnQ->aTxnQueues[uFuncId][i] = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_QUE_SIZE, uNodeHeaderOffset); 284 if (pTxnQ->aTxnQueues[uFuncId][i] == NULL) 285 { 286 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Queues creation failed!\n"); 287 context_LeaveCriticalSection (pTxnQ->hContext); 288 return TI_NOK; 289 } 290 } 291 292 /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */ 293 if (uFuncId < pTxnQ->uMinFuncId) 294 { 295 pTxnQ->uMinFuncId = uFuncId; 296 } 297 if (uFuncId > pTxnQ->uMaxFuncId) 298 { 299 pTxnQ->uMaxFuncId = uFuncId; 300 } 301 302 context_LeaveCriticalSection (pTxnQ->hContext); 303 304 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d registered successfully, uNumPrios = %d\n", uFuncId, uNumPrios); 305 306 return TI_OK; 307 } 308 309 void txnQ_Close (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 310 { 311 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 312 TI_UINT32 i; 313 314 context_EnterCriticalSection (pTxnQ->hContext); 315 316 /* Destroy the functional driver's queues */ 317 for (i = 0; i < pTxnQ->aFuncInfo[uFuncId].uNumPrios; i++) 318 { 319 que_Destroy (pTxnQ->aTxnQueues[uFuncId][i]); 320 } 321 322 /* Clear functional driver info */ 323 pTxnQ->aFuncInfo[uFuncId].uNumPrios = 0; 324 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = NULL; 325 pTxnQ->aFuncInfo[uFuncId].hCbHandle = NULL; 326 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_NONE; 327 328 /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */ 329 pTxnQ->uMinFuncId = MAX_FUNCTIONS; 330 pTxnQ->uMaxFuncId = 0; 331 for (i = 0; i < MAX_FUNCTIONS; i++) 332 { 333 if (pTxnQ->aFuncInfo[i].eState != FUNC_STATE_NONE) 334 { 335 if (i < pTxnQ->uMinFuncId) 336 { 337 pTxnQ->uMinFuncId = i; 338 } 339 if (i > pTxnQ->uMaxFuncId) 340 { 341 pTxnQ->uMaxFuncId = i; 342 } 343 } 344 } 345 346 context_LeaveCriticalSection (pTxnQ->hContext); 347 348 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d Unregistered\n", uFuncId); 349 } 350 351 ETxnStatus txnQ_Restart (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 352 { 353 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 354 355 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart()\n"); 356 357 context_EnterCriticalSection (pTxnQ->hContext); 358 359 /* If a Txn from the calling function is in progress, set state to RESTART return PENDING */ 360 if (pTxnQ->pCurrTxn) 361 { 362 if (TXN_PARAM_GET_FUNC_ID(pTxnQ->pCurrTxn) == uFuncId) 363 { 364 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RESTART; 365 366 context_LeaveCriticalSection (pTxnQ->hContext); 367 368 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart(): pCurrTxn pending\n"); 369 370 /* Return PENDING to indicate that the restart will be completed later (in TxnDone) */ 371 return TXN_STATUS_PENDING; 372 } 373 } 374 375 context_LeaveCriticalSection (pTxnQ->hContext); 376 377 /* Clear the calling function's queues (call function CB with status=RECOVERY) */ 378 txnQ_ClearQueues (hTxnQ, uFuncId); 379 380 /* Return COMPLETE to indicate that the restart was completed */ 381 return TXN_STATUS_COMPLETE; 382 } 383 384 void txnQ_Run (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 385 { 386 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 387 388 #ifdef TI_DBG 389 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Run()\n"); 390 if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_STOPPED) 391 { 392 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_WARNING, "txnQ_Run(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState); 393 } 394 #endif 395 396 /* Enable function's queues */ 397 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING; 398 399 /* Send queued transactions as possible */ 400 txnQ_RunScheduler (pTxnQ, NULL); 401 } 402 403 void txnQ_Stop (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 404 { 405 TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ; 406 407 #ifdef TI_DBG 408 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Stop()\n"); 409 if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_RUNNING) 410 { 411 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Stop(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState); 412 } 413 #endif 414 415 /* Enable function's queues */ 416 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED; 417 } 418 419 ETxnStatus txnQ_Transact (TI_HANDLE hTxnQ, TTxnStruct *pTxn) 420 { 421 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 422 TI_UINT32 uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn); 423 ETxnStatus rc; 424 425 if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) 426 { 427 pTxnQ->aFuncInfo[uFuncId].pSingleStep = pTxn; 428 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Single step Txn\n"); 429 } 430 else 431 { 432 TI_STATUS eStatus; 433 TI_HANDLE hQueue = pTxnQ->aTxnQueues[uFuncId][TXN_PARAM_GET_PRIORITY(pTxn)]; 434 context_EnterCriticalSection (pTxnQ->hContext); 435 eStatus = que_Enqueue (hQueue, (TI_HANDLE)pTxn); 436 context_LeaveCriticalSection (pTxnQ->hContext); 437 if (eStatus != TI_OK) 438 { 439 TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Transact(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pTxn, pTxn->uHwAddr, pTxn->aLen[0]); 440 return TXN_STATUS_ERROR; 441 } 442 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Regular Txn\n"); 443 } 444 445 /* Send queued transactions as possible */ 446 rc = txnQ_RunScheduler (pTxnQ, pTxn); 447 448 return rc; 449 } 450 451 452 /** 453 * \fn txnQ_ConnectCB 454 * \brief Pending Connection completion CB 455 * 456 * txnQ_ConnectBus CB 457 * 458 * \note 459 * \param hTxnQ - The module's object 460 * \param pTxn - The completed transaction object 461 * \return void 462 * \sa 463 */ 464 static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn) 465 { 466 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 467 468 /* Call the Client Connect CB */ 469 pTxnQ->fConnectCb (pTxnQ->hConnectCb, NULL); 470 } 471 472 473 /** 474 * \fn txnQ_TxnDoneCb 475 * \brief Pending Transaction completion CB 476 * 477 * Called back by bus-driver upon pending transaction completion in TxnDone context (external!). 478 * Enqueue completed transaction in TxnDone queue and call scheduler to send queued transactions. 479 * 480 * \note 481 * \param hTxnQ - The module's object 482 * \param pTxn - The completed transaction object 483 * \return void 484 * \sa 485 */ 486 static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn) 487 { 488 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 489 TTxnStruct *pTxn = (TTxnStruct *)hTxn; 490 TI_UINT32 uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn); 491 492 #ifdef TI_DBG 493 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb()\n"); 494 if (pTxn != pTxnQ->pCurrTxn) 495 { 496 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): CB returned pTxn 0x%x while pCurrTxn is 0x%x !!\n", pTxn, pTxnQ->pCurrTxn); 497 } 498 #endif 499 500 /* If the function of the completed Txn is waiting for restart */ 501 if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART) 502 { 503 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb(): Handling restart\n"); 504 505 /* First, Clear the restarted function queues */ 506 txnQ_ClearQueues (hTxnQ, uFuncId); 507 508 /* Call function CB for current Txn with restart indication */ 509 TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_RECOVERY); 510 pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn); 511 } 512 513 /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */ 514 else 515 { 516 TI_STATUS eStatus; 517 518 context_EnterCriticalSection (pTxnQ->hContext); 519 eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pTxn); 520 if (eStatus != TI_OK) 521 { 522 TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pTxn, pTxn->uHwAddr, pTxn->aLen[0]); 523 } 524 context_LeaveCriticalSection (pTxnQ->hContext); 525 } 526 527 /* Indicate that no transaction is currently processed in the bus-driver */ 528 pTxnQ->pCurrTxn = NULL; 529 530 /* Send queued transactions as possible (TRUE indicates we are in external context) */ 531 txnQ_RunScheduler (pTxnQ, NULL); 532 } 533 534 535 /** 536 * \fn txnQ_RunScheduler 537 * \brief Send queued transactions 538 * 539 * Run the scheduler, which issues transactions as long as possible. 540 * Since this function is called from either internal or external (TxnDone) context, 541 * it handles reentry by setting a bSchedulerPend flag, and running the scheduler again 542 * when its current iteration is finished. 543 * 544 * \note 545 * \param pTxnQ - The module's object 546 * \param pInputTxn - The transaction inserted in the current context (NULL if none) 547 * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed 548 * \sa 549 */ 550 static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn) 551 { 552 TI_BOOL bFirstIteration; 553 ETxnStatus eStatus = TXN_STATUS_NONE; 554 555 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler()\n"); 556 557 context_EnterCriticalSection (pTxnQ->hContext); 558 559 /* If the scheduler is currently busy, set bSchedulerPend flag and exit */ 560 if (pTxnQ->bSchedulerBusy) 561 { 562 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Scheduler is busy\n"); 563 pTxnQ->bSchedulerPend = TI_TRUE; 564 context_LeaveCriticalSection (pTxnQ->hContext); 565 return TXN_STATUS_PENDING; 566 } 567 568 /* Indicate that the scheduler is currently busy */ 569 pTxnQ->bSchedulerBusy = TI_TRUE; 570 571 context_LeaveCriticalSection (pTxnQ->hContext); 572 573 bFirstIteration = TI_TRUE; 574 575 /* 576 * Run the scheduler while it has work to do 577 */ 578 while (1) 579 { 580 /* If first scheduler iteration, save its return code to return the original Txn result */ 581 if (bFirstIteration) 582 { 583 eStatus = txnQ_Scheduler (pTxnQ, pInputTxn); 584 bFirstIteration = TI_FALSE; 585 } 586 /* This is for handling pending calls when the scheduler was busy (see above) */ 587 else 588 { 589 TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Handle pending scheduler call\n"); 590 txnQ_Scheduler (pTxnQ, NULL); 591 } 592 593 context_EnterCriticalSection (pTxnQ->hContext); 594 595 /* If no pending calls, clear the busy flag and return the original caller Txn status */ 596 if (!pTxnQ->bSchedulerPend) 597 { 598 pTxnQ->bSchedulerBusy = TI_FALSE; 599 context_LeaveCriticalSection (pTxnQ->hContext); 600 return eStatus; 601 } 602 pTxnQ->bSchedulerPend = TI_FALSE; 603 604 context_LeaveCriticalSection (pTxnQ->hContext); 605 } 606 } 607 608 609 /** 610 * \fn txnQ_Scheduler 611 * \brief Send queued transactions 612 * 613 * Issue transactions as long as they are available and the bus is not occupied. 614 * Call CBs of completed transactions, except completion of pInputTxn (covered by the return value). 615 * Note that this function is called from either internal or external (TxnDone) context. 616 * However, the txnQ_RunScheduler which calls it, prevents scheduler reentry. 617 * 618 * \note 619 * \param pTxnQ - The module's object 620 * \param pInputTxn - The transaction inserted in the current context (NULL if none) 621 * \return COMPLETE if pInputTxn completed in this context, PENDING if not, ERROR if failed 622 * \sa txnQ_RunScheduler 623 */ 624 static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn) 625 { 626 ETxnStatus eInputTxnStatus; 627 628 /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */ 629 eInputTxnStatus = TXN_STATUS_PENDING; 630 631 /* if a previous transaction is in progress, return PENDING */ 632 if (pTxnQ->pCurrTxn) 633 { 634 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): pCurrTxn isn't null (0x%x) so exit\n", pTxnQ->pCurrTxn); 635 return TXN_STATUS_PENDING; 636 } 637 638 /* Loop while transactions are available and can be sent to bus driver */ 639 while (1) 640 { 641 TTxnStruct *pSelectedTxn; 642 ETxnStatus eStatus; 643 644 /* Get next enabled transaction by priority. If none, exit loop. */ 645 context_EnterCriticalSection (pTxnQ->hContext); 646 pSelectedTxn = txnQ_SelectTxn (pTxnQ); 647 context_LeaveCriticalSection (pTxnQ->hContext); 648 if (pSelectedTxn == NULL) 649 { 650 break; 651 } 652 653 /* Save transaction in case it will be async (to indicate that the bus driver is busy) */ 654 pTxnQ->pCurrTxn = pSelectedTxn; 655 656 /* Send selected transaction to bus driver */ 657 eStatus = busDrv_Transact (pTxnQ->hBusDrv, pSelectedTxn); 658 659 /* If we've just sent the input transaction, use the status as the return value */ 660 if (pSelectedTxn == pInputTxn) 661 { 662 eInputTxnStatus = eStatus; 663 } 664 665 TRACE3(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Txn 0x%x sent, status = %d, eInputTxnStatus = %d\n", pSelectedTxn, eStatus, eInputTxnStatus); 666 667 /* If transaction completed */ 668 if (eStatus != TXN_STATUS_PENDING) 669 { 670 pTxnQ->pCurrTxn = NULL; 671 672 /* If it's not the input transaction, enqueue it in TxnDone queue */ 673 if (pSelectedTxn != pInputTxn) 674 { 675 TI_STATUS eStatus; 676 677 context_EnterCriticalSection (pTxnQ->hContext); 678 eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pSelectedTxn); 679 if (eStatus != TI_OK) 680 { 681 TRACE3(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Scheduler(): Enqueue failed, pTxn=0x%x, HwAddr=0x%x, Len0=%d\n", pSelectedTxn, pSelectedTxn->uHwAddr, pSelectedTxn->aLen[0]); 682 } 683 context_LeaveCriticalSection (pTxnQ->hContext); 684 } 685 } 686 687 /* If pending Exit loop! */ 688 else 689 { 690 break; 691 } 692 } 693 694 /* Dequeue completed transactions and call their functional driver CB */ 695 /* Note that it's the functional driver CB and not the specific CB in the Txn! */ 696 while (1) 697 { 698 TTxnStruct *pCompletedTxn; 699 TI_UINT32 uFuncId; 700 TTxnQueueDoneCb fTxnQueueDoneCb; 701 TI_HANDLE hCbHandle; 702 703 context_EnterCriticalSection (pTxnQ->hContext); 704 pCompletedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue); 705 context_LeaveCriticalSection (pTxnQ->hContext); 706 if (pCompletedTxn == NULL) 707 { 708 /* Return the status of the input transaction (PENDING unless sent and completed here) */ 709 return eInputTxnStatus; 710 } 711 712 TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Calling TxnDone for Txn 0x%x\n", pCompletedTxn); 713 714 uFuncId = TXN_PARAM_GET_FUNC_ID(pCompletedTxn); 715 fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb; 716 hCbHandle = pTxnQ->aFuncInfo[uFuncId].hCbHandle; 717 718 fTxnQueueDoneCb (hCbHandle, pCompletedTxn); 719 } 720 } 721 722 723 /** 724 * \fn txnQ_SelectTxn 725 * \brief Select transaction to send 726 * 727 * Called from txnQ_RunScheduler() which is protected in critical section. 728 * Select the next enabled transaction by priority. 729 * 730 * \note 731 * \param pTxnQ - The module's object 732 * \return The selected transaction to send (NULL if none available) 733 * \sa 734 */ 735 static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ) 736 { 737 TTxnStruct *pSelectedTxn; 738 TI_UINT32 uFunc; 739 TI_UINT32 uPrio; 740 741 #ifdef TI_DBG 742 /* If within Tx aggregation, dequeue Txn from same queue, and if not NULL return it */ 743 if (pTxnQ->pAggregQueue) 744 { 745 pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->pAggregQueue); 746 if (pSelectedTxn != NULL) 747 { 748 /* If aggregation ended, reset the aggregation-queue pointer */ 749 if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_OFF) 750 { 751 if ((TXN_PARAM_GET_FIXED_ADDR(pSelectedTxn) != TXN_FIXED_ADDR) || 752 (TXN_PARAM_GET_DIRECTION(pSelectedTxn) != TXN_DIRECTION_WRITE)) 753 { 754 TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_SelectTxn: Mixed transaction during aggregation, HwAddr=0x%x, TxnParams=0x%x\n", pSelectedTxn->uHwAddr, pSelectedTxn->uTxnParams); 755 } 756 pTxnQ->pAggregQueue = NULL; 757 } 758 return pSelectedTxn; 759 } 760 return NULL; 761 } 762 #endif 763 764 /* For all functions, if single-step Txn waiting, return it (sent even if function is stopped) */ 765 for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++) 766 { 767 pSelectedTxn = pTxnQ->aFuncInfo[uFunc].pSingleStep; 768 if (pSelectedTxn != NULL) 769 { 770 pTxnQ->aFuncInfo[uFunc].pSingleStep = NULL; 771 return pSelectedTxn; 772 } 773 } 774 775 /* For all priorities from high to low */ 776 for (uPrio = 0; uPrio < MAX_PRIORITY; uPrio++) 777 { 778 /* For all functions */ 779 for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++) 780 { 781 /* If function running and uses this priority */ 782 if (pTxnQ->aFuncInfo[uFunc].eState == FUNC_STATE_RUNNING && 783 pTxnQ->aFuncInfo[uFunc].uNumPrios > uPrio) 784 { 785 /* Dequeue Txn from current func and priority queue, and if not NULL return it */ 786 pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFunc][uPrio]); 787 if (pSelectedTxn != NULL) 788 { 789 #ifdef TI_DBG 790 /* If aggregation begins, save the aggregation-queue pointer to ensure continuity */ 791 if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_ON) 792 { 793 pTxnQ->pAggregQueue = pTxnQ->aTxnQueues[uFunc][uPrio]; 794 } 795 #endif 796 return pSelectedTxn; 797 } 798 } 799 } 800 } 801 802 /* If no transaction was selected, return NULL */ 803 return NULL; 804 } 805 806 807 /** 808 * \fn txnQ_ClearQueues 809 * \brief Clear the function queues 810 * 811 * Clear the specified function queues and call its CB for each Txn with status=RECOVERY. 812 * 813 * \note Called in critical section. 814 * \param hTxnQ - The module's object 815 * \param uFuncId - The calling functional driver 816 * \return void 817 * \sa 818 */ 819 void txnQ_ClearQueues (TI_HANDLE hTxnQ, TI_UINT32 uFuncId) 820 { 821 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 822 TTxnStruct *pTxn; 823 TI_UINT32 uPrio; 824 825 context_EnterCriticalSection (pTxnQ->hContext); 826 827 pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL; 828 829 /* For all function priorities */ 830 for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++) 831 { 832 do 833 { 834 /* Dequeue Txn from current priority queue */ 835 pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]); 836 837 /* 838 * Drop on Restart 839 * do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback 840 */ 841 } while (pTxn != NULL); 842 } 843 844 /* Clear state - for restart (doesn't call txnQ_Open) */ 845 pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING; 846 847 context_LeaveCriticalSection (pTxnQ->hContext); 848 } 849 850 #ifdef TI_DBG 851 void txnQ_PrintQueues (TI_HANDLE hTxnQ) 852 { 853 TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; 854 855 WLAN_OS_REPORT(("Print TXN queues\n")); 856 WLAN_OS_REPORT(("================\n")); 857 que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_LOW_PRIORITY]); 858 que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_HIGH_PRIORITY]); 859 } 860 #endif /* TI_DBG */ 861