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