/*
* TxnQueue.c
*
* Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved.
* All rights reserved.
*
* 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));
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, pRxDmaBufLen, pTxDmaBufLen);
}
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_EnterCriticalSection (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_LeaveCriticalSection (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_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (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_LeaveCriticalSection (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_EnterCriticalSection (pTxnQ->hContext);
eStatus = que_Enqueue (hQueue, (TI_HANDLE)pTxn);
context_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (pTxnQ->hContext);
return TXN_STATUS_PENDING;
}
/* Indicate that the scheduler is currently busy */
pTxnQ->bSchedulerBusy = TI_TRUE;
context_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (pTxnQ->hContext);
return eStatus;
}
pTxnQ->bSchedulerPend = TI_FALSE;
context_LeaveCriticalSection (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_EnterCriticalSection (pTxnQ->hContext);
pSelectedTxn = txnQ_SelectTxn (pTxnQ);
context_LeaveCriticalSection (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_EnterCriticalSection (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_LeaveCriticalSection (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_EnterCriticalSection (pTxnQ->hContext);
pCompletedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue);
context_LeaveCriticalSection (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;
}
/**
* \fn txnQ_ClearQueues
* \brief Clear the function queues
*
* Clear the specified function queues and call its CB for each Txn with status=RECOVERY.
*
* \note Called in critical section.
* \param hTxnQ - The module's object
* \param uFuncId - The calling functional driver
* \return void
* \sa
*/
void txnQ_ClearQueues (TI_HANDLE hTxnQ, TI_UINT32 uFuncId)
{
TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ;
TTxnStruct *pTxn;
TI_UINT32 uPrio;
context_EnterCriticalSection (pTxnQ->hContext);
pTxnQ->aFuncInfo[uFuncId].pSingleStep = NULL;
/* For all function priorities */
for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++)
{
do
{
/* Dequeue Txn from current priority queue */
pTxn = (TTxnStruct *) que_Dequeue (pTxnQ->aTxnQueues[uFuncId][uPrio]);
/*
* Drop on Restart
* do not call fTxnQueueDoneCb (hCbHandle, pTxn) callback
*/
} while (pTxn != NULL);
}
/* Clear state - for restart (doesn't call txnQ_Open) */
pTxnQ->aFuncInfo[uFuncId].eState = FUNC_STATE_RUNNING;
context_LeaveCriticalSection (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 */