Home | History | Annotate | Download | only in Txn
      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 */
     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 } TTxnQObj;
    126 
    127 
    128 /************************************************************************
    129  * Internal functions prototypes
    130  ************************************************************************/
    131 static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
    132 static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
    133 static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
    134 static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
    135 static void         txnQ_ClearQueues  (TTxnQObj *pTxnQ, TI_UINT32 uFuncId);
    136 static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
    137 
    138 
    139 
    140 /************************************************************************
    141  *
    142  *   Module functions implementation
    143  *
    144  ************************************************************************/
    145 
    146 TI_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 
    186 TI_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 
    205 void 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 
    225 TI_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 
    237 TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
    238 {
    239     TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
    240 
    241     return busDrv_DisconnectBus (pTxnQ->hBusDrv);
    242 }
    243 
    244 TI_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 
    298 void 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 
    340 ETxnStatus 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 
    373 void 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 
    392 void 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 
    408 ETxnStatus 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  */
    447 static 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  */
    469 static 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  */
    527 static 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  */
    601 static 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  */
    706 static 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  */
    760 static 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
    789 void 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