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