Txn/TxnQueue.c
changeset 0 10c42ec6c05f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Txn/TxnQueue.c	Tue Jun 29 12:34:26 2010 +0100
@@ -0,0 +1,861 @@
+/*
+ * TxnQueue.c
+ *
+ * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved.      
+ * All rights reserved.      
+ * 
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 or BSD License which accompanies
+ * this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the BSD License is as below.                                   
+ *                                                                       
+ * Redistribution and use in source and binary forms, with or without    
+ * modification, are permitted provided that the following conditions    
+ * are met:                                                              
+ *                                                                       
+ *  * Redistributions of source code must retain the above copyright     
+ *    notice, this list of conditions and the following disclaimer.      
+ *  * Redistributions in binary form must reproduce the above copyright  
+ *    notice, this list of conditions and the following disclaimer in    
+ *    the documentation and/or other materials provided with the         
+ *    distribution.                                                      
+ *  * Neither the name Texas Instruments nor the names of its            
+ *    contributors may be used to endorse or promote products derived    
+ *    from this software without specific prior written permission.      
+ *                                                                       
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ 
+/** \file   TxnQueue.c 
+ *  \brief  The transaction-queue module. 
+ *
+ * The Transaction Queue encapsulates the bus access from a functional driver (WLAN, BT).
+ * This TI proprietary module presents the same interface and same behavior for different 
+ *     bus configuration: SDIO (multi or single function) or SPI and for different modes 
+ *     of operation: Synchronous, a-synchronous or combination of both. 
+ * It will also be used over the RS232 interface (using wUART protocol) which is applicable 
+ *     for RS applications (on PC).
+ * 
+ * The TxnQ module provides the following requirements:
+ *     Inter process protection on queue's internal database and synchronization between 
+ *         functional drivers that share the bus.
+ *     Support multiple queues per function, handled by priority. 
+ *     Support the TTxnStruct API (as the Bus Driver) with the ability to manage commands 
+ *         queuing of multiple functions on top of the Bus Driver. 
+ *     The TxnQ (as well as the layers above it) is agnostic to the bus driver used beneath it 
+ *         (SDIO, WSPI or wUART), since all bus drivers introduce the same API and hide bus details. 
+ *     The TxnQ has no OS dependencies. It supports access from multiple OS threads.
+ * Note: It is assumed that any transaction forwarded to the TxnQ has enough resources in HW. 
+ * 
+ *  \see    TxnQueue.h
+ */
+
+#define __FILE_ID__  FILE_ID_123
+#include "tidef.h"
+#include "report.h"
+#include "context.h"
+#include "osApi.h"
+#include "TxnDefs.h"
+#include "BusDrv.h"
+#include "TxnQueue.h"
+
+
+
+/************************************************************************
+ * Defines
+ ************************************************************************/
+#define MAX_FUNCTIONS       4   /* Maximum 4 functional drivers (including Func 0 which is for bus control) */
+#define MAX_PRIORITY        2   /* Maximum 2 prioritys per functional driver */
+#define TXN_QUE_SIZE        QUE_UNLIMITED_SIZE
+#define TXN_DONE_QUE_SIZE   QUE_UNLIMITED_SIZE
+
+
+/************************************************************************
+ * Types
+ ************************************************************************/
+
+/* Functional driver's SM States */
+typedef enum
+{
+    FUNC_STATE_NONE,              /* Function not registered */
+	FUNC_STATE_STOPPED,           /* Queues are stopped */
+	FUNC_STATE_RUNNING,           /* Queues are running */
+	FUNC_STATE_RESTART            /* Wait for current Txn to finish before restarting queues */
+} EFuncState;
+
+/* The functional drivers registered to TxnQ */
+typedef struct 
+{
+    EFuncState      eState;             /* Function crrent state */
+    TI_UINT32       uNumPrios;          /* Number of queues (priorities) for this function */
+	TTxnQueueDoneCb fTxnQueueDoneCb;    /* The CB called by the TxnQueue upon full transaction completion. */
+	TI_HANDLE       hCbHandle;          /* The callback handle */
+    TTxnStruct *    pSingleStep;        /* A single step transaction waiting to be sent */
+
+} TFuncInfo;
+
+
+/* The TxnQueue module Object */
+typedef struct _TTxnQObj
+{
+    TI_HANDLE	    hOs;		   	 
+    TI_HANDLE	    hReport;
+    TI_HANDLE	    hContext;
+	TI_HANDLE	    hBusDrv;
+
+    TFuncInfo       aFuncInfo[MAX_FUNCTIONS];  /* Registered functional drivers - see above */
+    TI_HANDLE       aTxnQueues[MAX_FUNCTIONS][MAX_PRIORITY];  /* Handle of the Transactions-Queue */
+    TI_HANDLE       hTxnDoneQueue;      /* Queue for completed transactions not reported to yet to the upper layer */
+    TTxnStruct *    pCurrTxn;           /* The transaction currently processed in the bus driver (NULL if none) */
+    TI_UINT32       uMinFuncId;         /* The minimal function ID actually registered (through txnQ_Open) */
+    TI_UINT32       uMaxFuncId;         /* The maximal function ID actually registered (through txnQ_Open) */
+    TI_BOOL         bSchedulerBusy;     /* If set, the scheduler is currently running so it shouldn't be reentered */
+    TI_BOOL         bSchedulerPend;     /* If set, a call to the scheduler was postponed because it was busy */
+    
+    /* Environment dependent: TRUE if needed and allowed to protect TxnDone in critical section */
+    TTxnDoneCb      fConnectCb;
+    TI_HANDLE       hConnectCb;
+
+#ifdef TI_DBG
+    TI_HANDLE       pAggregQueue;       /* While Tx aggregation in progress, saves its queue pointer to ensure continuity */
+#endif
+
+} TTxnQObj;
+
+
+/************************************************************************
+ * Internal functions prototypes
+ ************************************************************************/
+static void         txnQ_TxnDoneCb    (TI_HANDLE hTxnQ, void *hTxn);
+static ETxnStatus   txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
+static ETxnStatus   txnQ_Scheduler    (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn);
+static TTxnStruct  *txnQ_SelectTxn    (TTxnQObj *pTxnQ);
+static void         txnQ_ConnectCB    (TI_HANDLE hTxnQ, void *hTxn);
+
+
+
+/************************************************************************
+ *
+ *   Module functions implementation
+ *
+ ************************************************************************/
+
+TI_HANDLE txnQ_Create (TI_HANDLE hOs)
+{
+    TI_HANDLE  hTxnQ;
+    TTxnQObj  *pTxnQ;
+    TI_UINT32  i;
+
+    hTxnQ = os_memoryAlloc(hOs, sizeof(TTxnQObj),MemoryNormal);
+    if (hTxnQ == NULL)
+        return NULL;
+    
+    pTxnQ = (TTxnQObj *)hTxnQ;
+
+    os_memoryZero(hOs, hTxnQ, sizeof(TTxnQObj));
+    
+    pTxnQ->hOs             = hOs;
+    pTxnQ->pCurrTxn        = NULL;
+    pTxnQ->uMinFuncId      = MAX_FUNCTIONS; /* Start at maximum and save minimal value in txnQ_Open */
+    pTxnQ->uMaxFuncId      = 0;             /* Start at minimum and save maximal value in txnQ_Open */
+#ifdef TI_DBG
+    pTxnQ->pAggregQueue    = NULL;
+#endif
+
+    for (i = 0; i < MAX_FUNCTIONS; i++)
+    {
+        pTxnQ->aFuncInfo[i].eState          = FUNC_STATE_NONE;
+        pTxnQ->aFuncInfo[i].uNumPrios       = 0;
+        pTxnQ->aFuncInfo[i].pSingleStep     = NULL;
+        pTxnQ->aFuncInfo[i].fTxnQueueDoneCb = NULL;
+        pTxnQ->aFuncInfo[i].hCbHandle       = NULL;
+    }
+    
+    /* Create the Bus-Driver module */
+    pTxnQ->hBusDrv = busDrv_Create (hOs);
+    if (pTxnQ->hBusDrv == NULL)
+    {
+        WLAN_OS_REPORT(("%s: Error - failed to create BusDrv\n", __FUNCTION__));
+        txnQ_Destroy (hTxnQ);
+        return NULL;
+    }
+
+    return pTxnQ;
+}
+
+TI_STATUS txnQ_Destroy (TI_HANDLE hTxnQ)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ;
+
+    if (pTxnQ)
+    {
+        if (pTxnQ->hBusDrv) 
+        {
+            busDrv_Destroy (pTxnQ->hBusDrv);
+        }
+        if (pTxnQ->hTxnDoneQueue) 
+        {
+            que_Destroy (pTxnQ->hTxnDoneQueue);
+        }
+        os_memoryFree (pTxnQ->hOs, pTxnQ, sizeof(TTxnQObj));     
+    }
+    return TI_OK;
+}
+
+void txnQ_Init (TI_HANDLE hTxnQ, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext)
+{
+    TTxnQObj  *pTxnQ = (TTxnQObj*)hTxnQ;
+    TI_UINT32  uNodeHeaderOffset;
+
+    pTxnQ->hOs             = hOs;
+    pTxnQ->hReport         = hReport;
+    pTxnQ->hContext        = hContext;
+
+    /* Create the TxnDone queue. */
+    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 
+    pTxnQ->hTxnDoneQueue = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_DONE_QUE_SIZE, uNodeHeaderOffset);
+    if (pTxnQ->hTxnDoneQueue == NULL)
+    {
+        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": TxnDone queue creation failed!\n");
+    }
+
+    busDrv_Init (pTxnQ->hBusDrv, hReport);
+}
+
+TI_STATUS txnQ_ConnectBus (TI_HANDLE  hTxnQ, 
+                           TBusDrvCfg *pBusDrvCfg, 
+                           TTxnDoneCb fConnectCb, 
+                           TI_HANDLE  hConnectCb, 
+                           TI_UINT32  *pRxDmaBufLen,
+                           TI_UINT32  *pTxDmaBufLen)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
+
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_ConnectBus()\n");
+
+    pTxnQ->fConnectCb = fConnectCb;
+    pTxnQ->hConnectCb = hConnectCb;
+
+    return busDrv_ConnectBus (pTxnQ->hBusDrv, pBusDrvCfg, txnQ_TxnDoneCb, hTxnQ, txnQ_ConnectCB);
+}
+
+TI_STATUS txnQ_DisconnectBus (TI_HANDLE hTxnQ)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
+
+    return busDrv_DisconnectBus (pTxnQ->hBusDrv);
+}
+
+TI_STATUS txnQ_Open (TI_HANDLE       hTxnQ, 
+                     TI_UINT32       uFuncId, 
+                     TI_UINT32       uNumPrios, 
+                     TTxnQueueDoneCb fTxnQueueDoneCb,
+                     TI_HANDLE       hCbHandle)
+{
+    TTxnQObj     *pTxnQ = (TTxnQObj*) hTxnQ;
+    TI_UINT32     uNodeHeaderOffset;
+    TI_UINT32     i;
+
+    if (uFuncId >= MAX_FUNCTIONS  ||  uNumPrios > MAX_PRIORITY) 
+    {
+        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Invalid Params!  uFuncId = %d, uNumPrios = %d\n", uFuncId, uNumPrios);
+        return TI_NOK;
+    }
+
+    CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+    /* Save functional driver info */
+    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = uNumPrios;
+    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = fTxnQueueDoneCb;
+    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = hCbHandle;
+    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_STOPPED;
+    
+    /* Create the functional driver's queues. */
+    uNodeHeaderOffset = TI_FIELD_OFFSET(TTxnStruct, tTxnQNode); 
+    for (i = 0; i < uNumPrios; i++)
+    {
+        pTxnQ->aTxnQueues[uFuncId][i] = que_Create (pTxnQ->hOs, pTxnQ->hReport, TXN_QUE_SIZE, uNodeHeaderOffset);
+        if (pTxnQ->aTxnQueues[uFuncId][i] == NULL)
+        {
+            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_ERROR, ": Queues creation failed!\n");
+            CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+            return TI_NOK;
+        }
+    }
+
+    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
+    if (uFuncId < pTxnQ->uMinFuncId) 
+    {
+        pTxnQ->uMinFuncId = uFuncId;
+    }
+    if (uFuncId > pTxnQ->uMaxFuncId) 
+    {
+        pTxnQ->uMaxFuncId = uFuncId;
+    }
+
+    CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+
+    TRACE2(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d registered successfully, uNumPrios = %d\n", uFuncId, uNumPrios);
+
+    return TI_OK;
+}
+
+void txnQ_Close (TI_HANDLE  hTxnQ, TI_UINT32 uFuncId)
+{
+    TTxnQObj     *pTxnQ = (TTxnQObj*)hTxnQ;
+    TI_UINT32     i;
+
+    CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+    /* Destroy the functional driver's queues */
+    for (i = 0; i < pTxnQ->aFuncInfo[uFuncId].uNumPrios; i++)
+    {
+        que_Destroy (pTxnQ->aTxnQueues[uFuncId][i]);
+    }
+
+    /* Clear functional driver info */
+    pTxnQ->aFuncInfo[uFuncId].uNumPrios       = 0;
+    pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb = NULL;
+    pTxnQ->aFuncInfo[uFuncId].hCbHandle       = NULL;
+    pTxnQ->aFuncInfo[uFuncId].eState          = FUNC_STATE_NONE;
+    
+    /* Update functions actual range (to optimize Txn selection loops - see txnQ_SelectTxn) */
+    pTxnQ->uMinFuncId      = MAX_FUNCTIONS; 
+    pTxnQ->uMaxFuncId      = 0;             
+    for (i = 0; i < MAX_FUNCTIONS; i++) 
+    {
+        if (pTxnQ->aFuncInfo[i].eState != FUNC_STATE_NONE) 
+        {
+            if (i < pTxnQ->uMinFuncId) 
+            {
+                pTxnQ->uMinFuncId = i;
+            }
+            if (i > pTxnQ->uMaxFuncId) 
+            {
+                pTxnQ->uMaxFuncId = i;
+            }
+        }
+    }
+
+    CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+
+    TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, ": Function %d Unregistered\n", uFuncId);
+}
+
+ETxnStatus txnQ_Restart (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
+
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart()\n");
+
+    CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+    /* If a Txn from the calling function is in progress, set state to RESTART return PENDING */
+    if (pTxnQ->pCurrTxn) 
+    {
+        if (TXN_PARAM_GET_FUNC_ID(pTxnQ->pCurrTxn) == uFuncId)
+        {
+            pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RESTART;
+
+            CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+
+            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Restart(): pCurrTxn pending\n");
+
+            /* Return PENDING to indicate that the restart will be completed later (in TxnDone) */
+            return TXN_STATUS_PENDING;
+        }
+    }
+
+    CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+
+    /* Clear the calling function's queues (call function CB with status=RECOVERY) */
+    txnQ_ClearQueues (hTxnQ, uFuncId);
+
+    /* Return COMPLETE to indicate that the restart was completed */
+    return TXN_STATUS_COMPLETE;
+}
+
+void txnQ_Run (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
+
+#ifdef TI_DBG
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Run()\n");
+    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_STOPPED) 
+    {
+        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_WARNING, "txnQ_Run(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
+    }
+#endif
+
+    /* Enable function's queues */
+    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
+
+    /* Send queued transactions as possible */
+    txnQ_RunScheduler (pTxnQ, NULL); 
+}
+
+void txnQ_Stop (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
+{
+    TTxnQObj *pTxnQ = (TTxnQObj*) hTxnQ;
+
+#ifdef TI_DBG
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Stop()\n");
+    if (pTxnQ->aFuncInfo[uFuncId].eState != FUNC_STATE_RUNNING) 
+    {
+        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_Stop(): Called while func %d state is %d!\n", uFuncId, pTxnQ->aFuncInfo[uFuncId].eState);
+    }
+#endif
+
+    /* Enable function's queues */
+    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_STOPPED;
+}
+
+ETxnStatus txnQ_Transact (TI_HANDLE hTxnQ, TTxnStruct *pTxn)
+{
+    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
+    TI_UINT32    uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
+    ETxnStatus   rc;
+
+    if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) 
+    {
+        pTxnQ->aFuncInfo[uFuncId].pSingleStep = pTxn;
+        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Single step Txn\n");
+    }
+    else 
+    {
+        TI_STATUS eStatus;
+        TI_HANDLE hQueue = pTxnQ->aTxnQueues[uFuncId][TXN_PARAM_GET_PRIORITY(pTxn)];
+        CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+        eStatus = que_Enqueue (hQueue, (TI_HANDLE)pTxn);
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+        if (eStatus != TI_OK)
+        {
+            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]);
+            return TXN_STATUS_ERROR;
+        }
+        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Transact(): Regular Txn\n");
+    }
+
+    /* Send queued transactions as possible */
+    rc = txnQ_RunScheduler (pTxnQ, pTxn); 
+
+    return rc;
+}
+
+
+/** 
+ * \fn     txnQ_ConnectCB
+ * \brief  Pending Connection completion CB
+ * 
+ *  txnQ_ConnectBus CB
+ * 
+ * \note   
+ * \param  hTxnQ - The module's object
+ * \param  pTxn  - The completed transaction object 
+ * \return void
+ * \sa     
+ */ 
+static void txnQ_ConnectCB (TI_HANDLE hTxnQ, void *hTxn)
+{
+    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
+
+    /* Call the Client Connect CB */
+    pTxnQ->fConnectCb (pTxnQ->hConnectCb, NULL);
+}
+
+
+/** 
+ * \fn     txnQ_TxnDoneCb
+ * \brief  Pending Transaction completion CB
+ * 
+ * Called back by bus-driver upon pending transaction completion in TxnDone context (external!).
+ * Enqueue completed transaction in TxnDone queue and call scheduler to send queued transactions.
+ * 
+ * \note   
+ * \param  hTxnQ - The module's object
+ * \param  pTxn  - The completed transaction object 
+ * \return void
+ * \sa     
+ */ 
+static void txnQ_TxnDoneCb (TI_HANDLE hTxnQ, void *hTxn)
+{
+    TTxnQObj   *pTxnQ   = (TTxnQObj*)hTxnQ;
+    TTxnStruct *pTxn    = (TTxnStruct *)hTxn;
+    TI_UINT32   uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn);
+
+#ifdef TI_DBG
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb()\n");
+    if (pTxn != pTxnQ->pCurrTxn) 
+    {
+        TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_TxnDoneCb(): CB returned pTxn 0x%x  while pCurrTxn is 0x%x !!\n", pTxn, pTxnQ->pCurrTxn);
+    }
+#endif
+
+    /* If the function of the completed Txn is waiting for restart */
+    if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART) 
+    {
+        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_TxnDoneCb(): Handling restart\n");
+
+        /* First, Clear the restarted function queues  */
+        txnQ_ClearQueues (hTxnQ, uFuncId);
+
+        /* Call function CB for current Txn with restart indication */
+        TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_RECOVERY);
+        pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn);
+    }
+
+    /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */
+    else 
+    {
+        TI_STATUS eStatus;
+
+        CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+        eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pTxn);
+        if (eStatus != TI_OK)
+        {
+            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]);
+        }
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+    }
+
+    /* Indicate that no transaction is currently processed in the bus-driver */
+    pTxnQ->pCurrTxn = NULL;
+
+    /* Send queued transactions as possible (TRUE indicates we are in external context) */
+    txnQ_RunScheduler (pTxnQ, NULL); 
+}
+
+
+/** 
+ * \fn     txnQ_RunScheduler
+ * \brief  Send queued transactions
+ * 
+ * Run the scheduler, which issues transactions as long as possible.
+ * Since this function is called from either internal or external (TxnDone) context,
+ *   it handles reentry by setting a bSchedulerPend flag, and running the scheduler again
+ *   when its current iteration is finished.
+ * 
+ * \note   
+ * \param  pTxnQ            - The module's object
+ * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
+ * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed
+ * \sa     
+ */ 
+static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
+{
+    TI_BOOL bFirstIteration;  
+	ETxnStatus eStatus = TXN_STATUS_NONE;
+
+    TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler()\n");
+
+    CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+    /* If the scheduler is currently busy, set bSchedulerPend flag and exit */
+    if (pTxnQ->bSchedulerBusy)
+    {
+        TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Scheduler is busy\n");
+        pTxnQ->bSchedulerPend = TI_TRUE;
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+        return TXN_STATUS_PENDING;
+    }
+
+    /* Indicate that the scheduler is currently busy */
+    pTxnQ->bSchedulerBusy = TI_TRUE;
+
+    CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+
+    bFirstIteration = TI_TRUE;  
+
+    /* 
+     * Run the scheduler while it has work to do 
+     */
+    while (1)
+    {
+        /* If first scheduler iteration, save its return code to return the original Txn result */
+        if (bFirstIteration) 
+        {
+            eStatus = txnQ_Scheduler (pTxnQ, pInputTxn);
+            bFirstIteration = TI_FALSE;
+        }
+        /* This is for handling pending calls when the scheduler was busy (see above) */
+        else 
+        {    
+            TRACE0(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_RunScheduler(): Handle pending scheduler call\n");
+            txnQ_Scheduler (pTxnQ, NULL);
+        }
+
+        CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+        /* If no pending calls, clear the busy flag and return the original caller Txn status */
+        if (!pTxnQ->bSchedulerPend) 
+        {
+            pTxnQ->bSchedulerBusy = TI_FALSE;
+            CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+            return eStatus;
+        }
+        pTxnQ->bSchedulerPend = TI_FALSE;
+
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+    }
+}
+
+
+/** 
+ * \fn     txnQ_Scheduler
+ * \brief  Send queued transactions
+ * 
+ * Issue transactions as long as they are available and the bus is not occupied.
+ * Call CBs of completed transactions, except completion of pInputTxn (covered by the return value).
+ * Note that this function is called from either internal or external (TxnDone) context.
+ * However, the txnQ_RunScheduler which calls it, prevents scheduler reentry.
+ * 
+ * \note   
+ * \param  pTxnQ     - The module's object
+ * \param  pInputTxn - The transaction inserted in the current context (NULL if none)
+ * \return COMPLETE if pInputTxn completed in this context, PENDING if not, ERROR if failed
+ * \sa     txnQ_RunScheduler
+ */ 
+static ETxnStatus txnQ_Scheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn)
+{
+    ETxnStatus eInputTxnStatus;  
+
+    /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */
+    eInputTxnStatus = TXN_STATUS_PENDING;  
+
+    /* if a previous transaction is in progress, return PENDING */
+    if (pTxnQ->pCurrTxn)
+    {
+        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): pCurrTxn isn't null (0x%x) so exit\n", pTxnQ->pCurrTxn);
+        return TXN_STATUS_PENDING;
+    }
+
+    /* Loop while transactions are available and can be sent to bus driver */
+    while (1)
+    {
+        TTxnStruct   *pSelectedTxn;
+        ETxnStatus    eStatus;
+
+        /* Get next enabled transaction by priority. If none, exit loop. */
+        CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+        pSelectedTxn = txnQ_SelectTxn (pTxnQ);
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+        if (pSelectedTxn == NULL)
+        {
+            break;
+        }
+
+        /* Save transaction in case it will be async (to indicate that the bus driver is busy) */
+        pTxnQ->pCurrTxn = pSelectedTxn;
+
+        /* Send selected transaction to bus driver */
+        eStatus = busDrv_Transact (pTxnQ->hBusDrv, pSelectedTxn);
+
+        /* If we've just sent the input transaction, use the status as the return value */
+        if (pSelectedTxn == pInputTxn)
+        {
+            eInputTxnStatus = eStatus;
+        }
+
+        TRACE3(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Txn 0x%x sent, status = %d, eInputTxnStatus = %d\n", pSelectedTxn, eStatus, eInputTxnStatus);
+
+        /* If transaction completed */
+        if (eStatus != TXN_STATUS_PENDING)
+        {
+            pTxnQ->pCurrTxn = NULL;
+
+            /* If it's not the input transaction, enqueue it in TxnDone queue */
+            if (pSelectedTxn != pInputTxn)
+            {
+                TI_STATUS eStatus;
+
+                CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+                eStatus = que_Enqueue (pTxnQ->hTxnDoneQueue, (TI_HANDLE)pSelectedTxn);
+                if (eStatus != TI_OK)
+                {
+                    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]);
+                }
+                CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+            }
+        }
+
+        /* If pending Exit loop! */
+        else 
+        {
+            break;
+        }
+    }
+
+    /* Dequeue completed transactions and call their functional driver CB */
+    /* Note that it's the functional driver CB and not the specific CB in the Txn! */
+    while (1)
+    {
+        TTxnStruct      *pCompletedTxn;
+        TI_UINT32        uFuncId;
+        TTxnQueueDoneCb  fTxnQueueDoneCb;
+        TI_HANDLE        hCbHandle;
+
+        CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+        pCompletedTxn   = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue);
+        CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+        if (pCompletedTxn == NULL)
+        {
+            /* Return the status of the input transaction (PENDING unless sent and completed here) */
+            return eInputTxnStatus;
+        }
+
+        TRACE1(pTxnQ->hReport, REPORT_SEVERITY_INFORMATION, "txnQ_Scheduler(): Calling TxnDone for Txn 0x%x\n", pCompletedTxn);
+
+        uFuncId         = TXN_PARAM_GET_FUNC_ID(pCompletedTxn);
+        fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb;
+        hCbHandle       = pTxnQ->aFuncInfo[uFuncId].hCbHandle;
+
+        fTxnQueueDoneCb (hCbHandle, pCompletedTxn);
+    }
+}
+
+
+/** 
+ * \fn     txnQ_SelectTxn
+ * \brief  Select transaction to send
+ * 
+ * Called from txnQ_RunScheduler() which is protected in critical section.
+ * Select the next enabled transaction by priority.
+ * 
+ * \note   
+ * \param  pTxnQ - The module's object
+ * \return The selected transaction to send (NULL if none available)
+ * \sa     
+ */ 
+static TTxnStruct *txnQ_SelectTxn (TTxnQObj *pTxnQ)
+{
+    TTxnStruct *pSelectedTxn;
+    TI_UINT32   uFunc;
+    TI_UINT32   uPrio;
+
+#ifdef TI_DBG
+    /* If within Tx aggregation, dequeue Txn from same queue, and if not NULL return it */
+    if (pTxnQ->pAggregQueue)
+    {
+        pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->pAggregQueue);
+        if (pSelectedTxn != NULL)
+        {
+            /* If aggregation ended, reset the aggregation-queue pointer */
+            if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_OFF) 
+            {
+                if ((TXN_PARAM_GET_FIXED_ADDR(pSelectedTxn) != TXN_FIXED_ADDR) ||
+                    (TXN_PARAM_GET_DIRECTION(pSelectedTxn)  != TXN_DIRECTION_WRITE))
+                {
+                    TRACE2(pTxnQ->hReport, REPORT_SEVERITY_ERROR, "txnQ_SelectTxn: Mixed transaction during aggregation, HwAddr=0x%x, TxnParams=0x%x\n", pSelectedTxn->uHwAddr, pSelectedTxn->uTxnParams);
+                }
+                pTxnQ->pAggregQueue = NULL;
+            }
+            return pSelectedTxn;
+        }
+        return NULL;
+    }
+#endif
+
+    /* For all functions, if single-step Txn waiting, return it (sent even if function is stopped) */
+    for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
+    {
+        pSelectedTxn = pTxnQ->aFuncInfo[uFunc].pSingleStep;
+        if (pSelectedTxn != NULL)
+        {
+            pTxnQ->aFuncInfo[uFunc].pSingleStep = NULL;
+            return pSelectedTxn;
+        }
+    }
+
+    /* For all priorities from high to low */
+    for (uPrio = 0; uPrio < MAX_PRIORITY; uPrio++)
+    {
+        /* For all functions */
+        for (uFunc = pTxnQ->uMinFuncId; uFunc <= pTxnQ->uMaxFuncId; uFunc++)
+        {
+            /* If function running and uses this priority */
+            if (pTxnQ->aFuncInfo[uFunc].eState == FUNC_STATE_RUNNING  &&
+                pTxnQ->aFuncInfo[uFunc].uNumPrios > uPrio)
+            {
+                /* Dequeue Txn from current func and priority queue, and if not NULL return it */
+                pSelectedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFunc][uPrio]);
+                if (pSelectedTxn != NULL)
+                {
+#ifdef TI_DBG
+                    /* If aggregation begins, save the aggregation-queue pointer to ensure continuity */
+                    if (TXN_PARAM_GET_AGGREGATE(pSelectedTxn) == TXN_AGGREGATE_ON) 
+                    {
+                        pTxnQ->pAggregQueue = pTxnQ->aTxnQueues[uFunc][uPrio];
+                    }
+#endif
+                    return pSelectedTxn;
+                }
+            }
+        }
+    }
+
+    /* If no transaction was selected, return NULL */
+    return NULL;
+}
+
+
+void txnQ_ClearQueues (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
+{
+    TTxnQObj        *pTxnQ = (TTxnQObj*)hTxnQ;
+    TTxnStruct      *pTxn;
+    TI_UINT32        uPrio;
+
+    CONTEXT_ENTER_CRITICAL_SECTION (pTxnQ->hContext);
+
+    pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL;
+
+    /* For all function priorities */
+    for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++)
+    {
+        while (1) 
+        {
+            /* Dequeue Txn from current priority queue */
+            pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]);
+
+            /* If NULL Txn (queue empty), exit while loop */
+            if (pTxn == NULL)
+            {
+                break;
+            }
+
+            /* 
+             * Drop on Restart 
+             * do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback 
+             */
+        }
+    }
+
+    /* Clear state - for restart (doesn't call txnQ_Open) */
+    pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
+
+    CONTEXT_LEAVE_CRITICAL_SECTION (pTxnQ->hContext);
+}
+
+#ifdef TI_DBG
+void txnQ_PrintQueues (TI_HANDLE hTxnQ)
+{
+    TTxnQObj    *pTxnQ   = (TTxnQObj*)hTxnQ;
+
+    WLAN_OS_REPORT(("Print TXN queues\n"));
+    WLAN_OS_REPORT(("================\n"));
+    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_LOW_PRIORITY]);
+    que_Print(pTxnQ->aTxnQueues[TXN_FUNC_ID_WLAN][TXN_HIGH_PRIORITY]);
+}
+#endif /* TI_DBG */
+
+