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