/** * \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) { 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 tmr_HandleExpiry * \brief Handles queued expiry events in driver context * * This is the Timer module's callback that is registered to the ContextEngine module to be invoked * from the driver task (after requested by tmr_GetExpiry through context_RequestSchedule ()). * It dequeues all expiry events from the queue that correlates to the current driver state, * and calls their users callbacks. * * \note * \param hTimerModule - The module object * \return void * \sa tmr_GetExpiry */ void tmr_HandleExpiry (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; /* The timer module handle */ TTimerInfo *pTimerInfo; /* The timer handle */ TI_BOOL bTwdInitOccured; /* Indicates if TWD init occured since timer start */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_HandleExpiry(): ERROR - NULL timer!\n")); return; } while (1) { /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); /* If current driver state is Operational, dequeue timer object from Operational-queue */ if (pTimerModule->bOperState) { pTimerInfo = (TTimerInfo *) que_Dequeue (pTimerModule->hOperQueue); } /* Else (driver state is NOT-Operational), dequeue timer object from Init-queue */ else { pTimerInfo = (TTimerInfo *) que_Dequeue (pTimerModule->hInitQueue); } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* If no more objects in queue, exit */ if (!pTimerInfo) { return; /** EXIT Point **/ } /* If current TWD-Init-Count is different than when the timer was started, Init occured. */ bTwdInitOccured = (pTimerModule->uTwdInitCount != pTimerInfo->uTwdInitCountWhenStarted); /* Call specific timer callback function */ pTimerInfo->fExpiryCbFunc (pTimerInfo->hExpiryCbHndl, bTwdInitOccured); /* If the expired timer is periodic, start it again. */ if (pTimerInfo->bPeriodic) { tmr_StartTimer ((TI_HANDLE)pTimerInfo, pTimerInfo->fExpiryCbFunc, pTimerInfo->hExpiryCbHndl, pTimerInfo->uIntervalMsec, pTimerInfo->bPeriodic); } } }
/** * \fn TxMgmtQ_FlushLinkQueues * \brief Flush management queues the specific link * * * \note * \param hTxMgmtQ - The module's object * \param uHlid - link id * \return void * \sa */ void TxMgmtQ_FlushLinkQueues(TI_HANDLE hTxMgmtQ, TI_UINT32 uHlid) { TTxMgmtQ *pTxMgmtQ = (TTxMgmtQ *)hTxMgmtQ; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId; TMgmtLinkQ *pLinkQ; pLinkQ = &pTxMgmtQ->aMgmtLinkQ[uHlid]; /* Link queues */ /* Dequeue and free all queued packets */ for (uQueId = 0 ; uQueId < NUM_OF_MGMT_QUEUES ; uQueId++) { while (1) { context_EnterCriticalSection (pTxMgmtQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pLinkQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxMgmtQ->hContext); if (pPktCtrlBlk == NULL) { break; } txCtrl_FreePacket (pTxMgmtQ->hTxCtrl, pPktCtrlBlk, TI_NOK); } } }
/** * \fn tmr_UpdateDriverState * \brief Update driver state * * Under critical section, update driver state (operational or not), * and if opertional, clear init queue. * Leave critical section and if operational state, request schedule for handling * timer events in driver context (if any). * * \note * \param hTimerModule - The timer module object * \param bOperState - TRUE if driver state is now operational, FALSE if not. * \return void * \sa */ void tmr_UpdateDriverState (TI_HANDLE hTimerModule, TI_BOOL bOperState) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; if (bOperState == pTimerModule->bOperState) { return; } /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); /* Save new state (TRUE means operational). */ pTimerModule->bOperState = bOperState; /* If new state is operational */ if (bOperState) { /* Increment the TWD initializations counter (for detecting recovery events). */ pTimerModule->uTwdInitCount++; /* Empty the init queue (obsolete). */ while (que_Dequeue (pTimerModule->hInitQueue) != NULL) {} } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* If new state is operational, request switch to driver context for handling timer events */ if (bOperState) { context_RequestSchedule (pTimerModule->hContext, pTimerModule->uContextId); } }
/** * \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); }
/** * \fn twIf_ClearTxnDoneQueue * \brief Clean the DoneQueue * * Clear the specified done queue - don't call the callbacks. * * \note * \param hTwIf - The module's object * \return void * \sa */ static void twIf_ClearTxnDoneQueue(TI_HANDLE hTwIf) { TTwIfObj *pTwIf = (TTwIfObj *) hTwIf; TTxnStruct *pTxn; /* Loop while there are completed transactions to handle */ while (1) { /* In critical section, dequeue completed transaction from the TxnDoneQ. */ context_EnterCriticalSection(pTwIf->hContext); pTxn = (TTxnStruct *) que_Dequeue(pTwIf->hTxnDoneQueue); context_LeaveCriticalSection(pTwIf->hContext); /* If no more transactions to handle, exit */ if (pTxn != NULL) { /* Decrement pending Txn counter */ if (pTwIf->uPendingTxnCount > 0) { /* in case of callback on recovery after restart */ pTwIf->uPendingTxnCount--; } /* * Drop on Recovery * do not call pTxn->fTxnDoneCb (pTxn->hCbHandle, pTxn) callback */ } if (pTxn == NULL) { return; } } }
/** * \fn txDataQ_FlushLinkQueues * \brief Flush all queues of the specific link * * Free all pending packets in link queue * * \note * \param hTxDataQ - The object * \param uHlid - Link ID * \return void * \sa */ void txDataQ_FlushLinkQueues (TI_HANDLE hTxDataQ, TI_UINT32 uHlid) { TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId; TDataLinkQ *pLinkQ; pLinkQ = &pTxDataQ->aDataLinkQ[uHlid]; /* Link queues */ /* Dequeue and free all queued packets */ for (uQueId = 0 ; uQueId < pTxDataQ->uNumQueues ; uQueId++) { while (1) { context_EnterCriticalSection (pTxDataQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pLinkQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxDataQ->hContext); if (pPktCtrlBlk == NULL) { break; } txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); } } }
void tmr_ClearOperQueue (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; context_EnterCriticalSection (pTimerModule->hContext); while (que_Dequeue (pTimerModule->hOperQueue) != NULL) {} context_LeaveCriticalSection (pTimerModule->hContext); }
/** * \fn cmdHndlr_HandleCommands * \brief Handle queued commands * * While there are queued commands, dequeue a command and call the * commands interpreter (OID or WEXT selected at compile time). * If the command processing is not completed in this context (pending), we exit and * this function is called again upon commnad completion, so it can continue processing * further queued commands (if any). * * \note * \param hCmdHndlr - The module object * \return void * \sa cmdHndlr_InsertCommand, cmdHndlr_Complete */ void cmdHndlr_HandleCommands(TI_HANDLE hCmdHndlr) { TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *) hCmdHndlr; while (1) { /* Enter critical section to protect queue access */ context_EnterCriticalSection(pCmdHndlr->hContext); /* Dequeue a command */ pCmdHndlr->pCurrCmd = (TConfigCommand *) que_Dequeue(pCmdHndlr->hCmdQueue); /* If we have got a command */ if (pCmdHndlr->pCurrCmd) { /* Leave critical section */ context_LeaveCriticalSection(pCmdHndlr->hContext); /* Convert to driver structure and execute command */ pCmdHndlr->pCurrCmd->eCmdStatus = cmdInterpret_convertAndExecute(pCmdHndlr-> hCmdInterpret, pCmdHndlr->pCurrCmd); /* * If command not completed in this context (Async), return. * (we'll be called back upon command completion) */ if (COMMAND_PENDING == pCmdHndlr->pCurrCmd->eCmdStatus) { return; } /* Command was completed so free the wait signal and continue to next command */ wlanDrvIf_CommandDone(pCmdHndlr->hOs, pCmdHndlr->pCurrCmd-> pSignalObject, pCmdHndlr->pCurrCmd-> CmdRespBuffer); pCmdHndlr->pCurrCmd = NULL; } /* Else, we don't have commands to handle */ else { /* Indicate that we are not handling commands (before leaving critical section!) */ pCmdHndlr->bProcessingCmds = TI_FALSE; /* Leave critical section */ context_LeaveCriticalSection(pCmdHndlr->hContext); /* Exit (no more work) */ return; } } }
/** * \fn cmdHndlr_ClearQueue * \brief Clear commands queue * * Dequeue and free all queued commands. * * \note * \param hCmdHndlr - The object * \return void * \sa */ void cmdHndlr_ClearQueue (TI_HANDLE hCmdHndlr) { TCmdHndlrObj *pCmdHndlr = (TCmdHndlrObj *)hCmdHndlr; TConfigCommand *pCurrCmd; /* Dequeue and free all queued commands */ do { context_EnterCriticalSection (pCmdHndlr->hContext); pCurrCmd = (TConfigCommand *)que_Dequeue(pCmdHndlr->hCmdQueue); context_LeaveCriticalSection (pCmdHndlr->hContext); if (pCurrCmd != NULL) { /* Just release the semaphore. The command is freed subsequently. */ os_SignalObjectSet (pCmdHndlr->hOs, pCurrCmd->pSignalObject); } } while (pCurrCmd != NULL); }
/** * \fn txDataQ_ClearQueues * \brief Clear all queues * * Dequeue and free all queued packets. * * \note * \param hTxDataQ - The object * \return void * \sa */ void txDataQ_ClearQueues (TI_HANDLE hTxDataQ) { TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId; /* Dequeue and free all queued packets */ for (uQueId = 0 ; uQueId < pTxDataQ->uNumQueues ; uQueId++) { do { context_EnterCriticalSection (pTxDataQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue(pTxDataQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxDataQ->hContext); if (pPktCtrlBlk != NULL) { txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); } } while (pPktCtrlBlk != NULL); } }
/** * \fn tmr_UpdateDriverState * \brief Update driver state * * Under critical section, update driver state (operational or not), * and if opertional, clear init queue. * Leave critical section and if operational state, request schedule for handling * timer events in driver context (if any). * * \note * \param hTimerModule - The timer module object * \param bOperState - TRUE if driver state is now operational, FALSE if not. * \return void * \sa */ void tmr_UpdateDriverState (TI_HANDLE hTimerModule, TI_BOOL bOperState) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_UpdateDriverState(): ERROR - NULL timer!\n")); return; } /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); if (bOperState == pTimerModule->bOperState) { context_LeaveCriticalSection (pTimerModule->hContext); TRACE1(pTimerModule->hReport, REPORT_SEVERITY_ERROR, "tmr_UpdateDriverState(): New bOperState (%d) is as current!\n", bOperState); return; } /* Save new state (TRUE means operational). */ pTimerModule->bOperState = bOperState; /* If new state is operational */ if (bOperState) { /* Increment the TWD initializations counter (for detecting recovery events). */ pTimerModule->uTwdInitCount++; /* Empty the init queue (obsolete). */ while (que_Dequeue (pTimerModule->hInitQueue) != NULL) {} } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* If new state is operational, request switch to driver context for handling timer events */ if (bOperState) { context_RequestSchedule (pTimerModule->hContext, pTimerModule->uContextId); } }
/** * \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; McpU32 uFunc; McpU32 uPrio; /* 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 < TXN_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) { 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 pTxnQ - The module's object * \param uFuncId - The calling functional driver * \param bExternalContext - TRUE if called in external context (TxnDone) * \return void * \sa */ static void txnQ_ClearQueues (TTxnQObj *pTxnQ, McpU32 uFuncId, McpBool bExternalContext) { TTxnStruct *pTxn; McpU32 uPrio; TTxnQueueDoneCb fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb; /* function TxnDone CB */ handle_t hCbHandle = pTxnQ->aFuncInfo[uFuncId].hCbHandle; McpBool loopCond = MCP_TRUE; /* If single step waiting in this function, call function CB with recovery indication */ pTxn = pTxnQ->aFuncInfo[uFuncId].pSingleStep; if (pTxn != NULL) { TXN_PARAM_SET_STATUS(pTxn, TXN_STATUS_RECOVERY); fTxnQueueDoneCb (hCbHandle, pTxn, bExternalContext); } /* For all function priorities */ for (uPrio = 0; uPrio < pTxnQ->aFuncInfo[uFuncId].uNumPrios; uPrio++) { while (loopCond) { /* 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; } /* Call function CB with recovery indication in the Txn */ TXN_PARAM_SET_STATUS(pTxn, TXN_STATUS_RECOVERY); fTxnQueueDoneCb (hCbHandle, pTxn, bExternalContext); } } }
/** * \fn txnQ_RunScheduler * \brief Send queued transactions * * Issue transactions as long as they are available and the bus is not occupied. * Call CBs of completed transactions, except conpletion of pCurrTxn (covered by the return value). * Note that this function can't be preempted, since it is always called in critical section * See txnQ_Transact(), txnQ_SendCtrlByte(), txnQ_Run() and txnQ_TxnDoneCb(). * * \note * \param pTxnQ - The module's object * \param pCurrTxn - The transaction inserted in the current context (NULL if none) * \param bExternalContext - TRUE if called in external context (TxnDone) * \return COMPLETE if pCurrTxn completed in this context, PENDING if not, ERROR if failed * \sa */ static ETxnStatus txnQ_RunScheduler (TTxnQObj *pTxnQ, TTxnStruct *pInputTxn, McpBool bExternalContext) { /* Use as return value the status of the input transaction (PENDING unless sent and completed here) */ ETxnStatus eInputTxnStatus = TXN_STATUS_PENDING; McpBool loopCond = MCP_TRUE; /* if a previous transaction is in progress, return PENDING */ if (pTxnQ->pCurrTxn) { return TXN_STATUS_PENDING; } /* Loop while transactions are available and can be sent to bus driver */ while (loopCond) { TTxnStruct *pSelectedTxn; ETxnStatus eStatus; #ifdef TRAN_DBG #define TXN_MAX_LOOP_ITERATES 100 int loopIterates = 0; if (loopIterates++ >= TXN_MAX_LOOP_ITERATES) { MCPF_REPORT_WARNING (pTxnQ->hMcpf, QUEUE_MODULE_LOG, ("txnQ_RunScheduler loop reached max iterates=%d\n", loopIterates)); } #endif /* Get next enabled transaction by priority. If none, exit loop. */ pSelectedTxn = txnQ_SelectTxn (pTxnQ); if (pSelectedTxn == NULL) { break; } /* 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; } /* If transaction completed */ if (eStatus == TXN_STATUS_COMPLETE) { /* If it's not the input transaction, enqueue it in TxnDone queue */ if (pSelectedTxn != pInputTxn) { que_Enqueue (pTxnQ->hTxnDoneQueue, (handle_t)pSelectedTxn); } } /* If pending or error */ else { /* If transaction pending, save it to indicate that the bus driver is busy */ if (eStatus == TXN_STATUS_PENDING) { pTxnQ->pCurrTxn = pSelectedTxn; } /* Exit loop! */ 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 (loopCond) { TTxnStruct *pCompletedTxn; McpU32 uFuncId; TTxnQueueDoneCb fTxnQueueDoneCb; handle_t hCbHandle; pCompletedTxn = (TTxnStruct *) que_Dequeue (pTxnQ->hTxnDoneQueue); if (pCompletedTxn == NULL) { /* Return the status of the input transaction (PENDING unless sent and completed here) */ return eInputTxnStatus; } uFuncId = TXN_PARAM_GET_FUNC_ID(pCompletedTxn); fTxnQueueDoneCb = pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb; hCbHandle = pTxnQ->aFuncInfo[uFuncId].hCbHandle; fTxnQueueDoneCb (hCbHandle, pCompletedTxn, bExternalContext); } return TXN_STATUS_PENDING; }
/** * \fn txDataQ_RunScheduler * \brief The module's Tx scheduler * * This function is the Data-Queue scheduler. * It selects a packet to transmit from the tx queues and sends it to the TxCtrl. * The queues are selected in a round-robin order. * The function is called by one of: * txDataQ_Run() * txDataQ_UpdateBusyMap() * txDataQ_WakeAll() * * \note * \param hTxDataQ - The object * \return void * \sa */ static void txDataQ_RunScheduler (TI_HANDLE hTxDataQ) { TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; TI_UINT32 uIdleIterationsCount = 0; /* Count iterations without packet transmission (for exit criteria) */ TI_UINT32 uQueId = pTxDataQ->uLastQueId; /* The last iteration queue */ EStatusXmit eStatus; /* The return status of the txCtrl_xmitData function */ TTxCtrlBlk *pPktCtrlBlk; /* Pointer to the packet to be dequeued and sent */ while (1) { /* If the Data port is closed or the scheduler couldn't send packets from all queues, indicate end of current packets burst and exit */ if ( !pTxDataQ->bDataPortEnable || (uIdleIterationsCount >= pTxDataQ->uNumQueues) ) { TWD_txXfer_EndOfBurst (pTxDataQ->hTWD); return; } /* Selecting the next queue */ uQueId++; if (uQueId == pTxDataQ->uNumQueues) { uQueId = 0; } pTxDataQ->uLastQueId = uQueId; /* Increment the idle iterations counter */ uIdleIterationsCount++; /* If the queue is busy (AC is full), continue to next queue. */ if (pTxDataQ->aQueueBusy[uQueId]) { continue; } /* Dequeue a packet in a critical section */ context_EnterCriticalSection (pTxDataQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pTxDataQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxDataQ->hContext); /* If the queue was empty, continue to the next queue */ if (pPktCtrlBlk == NULL) { if ((pTxDataQ->bStopNetStackTx) && pTxDataQ->aNetStackQueueStopped[uQueId]) { pTxDataQ->aNetStackQueueStopped[uQueId] = TI_FALSE; /*Resume the TX process as our date queues are empty*/ wlanDrvIf_ResumeTx (pTxDataQ->hOs); } continue; } #ifdef TI_DBG pTxDataQ->aQueueCounters[uQueId].uDequeuePacket++; #endif /* TI_DBG */ /* Send the packet */ eStatus = txCtrl_XmitData (pTxDataQ->hTxCtrl, pPktCtrlBlk); /* * If the return status is busy it means that the packet was not sent * so we need to requeue it for future try. */ if (eStatus == STATUS_XMIT_BUSY) { TI_STATUS eQueStatus; /* Requeue the packet in a critical section */ context_EnterCriticalSection (pTxDataQ->hContext); eQueStatus = que_Requeue (pTxDataQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); if (eQueStatus != TI_OK) { /* If the packet can't be queued drop it */ /* Note: may happen only if this thread was preempted between the dequeue and requeue and new packets were inserted into this quque */ txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); #ifdef TI_DBG pTxDataQ->aQueueCounters[uQueId].uDroppedPacket++; #endif /* TI_DBG */ } context_LeaveCriticalSection (pTxDataQ->hContext); #ifdef TI_DBG pTxDataQ->aQueueCounters[uQueId].uRequeuePacket++; #endif /* TI_DBG */ continue; } /* If we reach this point, a packet was sent successfully so reset the idle iterations counter. */ uIdleIterationsCount = 0; #ifdef TI_DBG pTxDataQ->aQueueCounters[uQueId].uXmittedPacket++; #endif /* TI_DBG */ } /* End of while */ /* Unreachable code */ }
/** * \fn twIf_HandleTxnDone * \brief Completed transactions handler * * The completed transactions handler, called upon TxnDone event, either from the context engine * or directly from twIf_TxnDoneCb() if we are already in the WLAN driver's context. * Dequeue all completed transactions in critical section, and call their callbacks if available. * If awake is not required and no pending transactions in TxnQ, issue Sleep event to SM. * * \note * \param hTwIf - The module's object * \return void * \sa */ static void twIf_HandleTxnDone(TI_HANDLE hTwIf) { TTwIfObj *pTwIf = (TTwIfObj *) hTwIf; TTxnStruct *pTxn; /* In case of recovery, call the recovery callback and exit */ if (pTwIf->bTxnDoneInRecovery) { TRACE0(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HandleTxnDone: call RecoveryCb\n"); pTwIf->bTxnDoneInRecovery = TI_FALSE; if (pTwIf->bPendRestartTimerRunning) { tmr_StopTimer(pTwIf->hPendRestartTimer); pTwIf->bPendRestartTimerRunning = TI_FALSE; } pTwIf->fRecoveryCb(pTwIf->hRecoveryCb); return; } /* Loop while there are completed transactions to handle */ while (1) { /* In critical section, dequeue completed transaction from the TxnDoneQ. */ context_EnterCriticalSection(pTwIf->hContext); pTxn = (TTxnStruct *) que_Dequeue(pTwIf->hTxnDoneQueue); context_LeaveCriticalSection(pTwIf->hContext); /* If no more transactions to handle, exit */ if (pTxn != NULL) { context_EnterCriticalSection(pTwIf->hContext); /* Decrement pending Txn counter */ if (pTwIf->uPendingTxnCount > 0) { /* in case of callback on recovery after restart */ pTwIf->uPendingTxnCount--; } context_LeaveCriticalSection(pTwIf->hContext); TRACE4(pTwIf->hReport, REPORT_SEVERITY_INFORMATION, "twIf_HandleTxnDone: Completed-Txn: Params=0x%x, HwAddr=0x%x, Len0=%d, fTxnDoneCb=0x%x\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->fTxnDoneCb); /* If Txn failed and error CB available, call it to initiate recovery */ if (TXN_PARAM_GET_STATUS(pTxn) == TXN_PARAM_STATUS_ERROR) { TRACE6(pTwIf->hReport, REPORT_SEVERITY_ERROR, "twIf_HandleTxnDone: Txn failed!! Params=0x%x, HwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", pTxn->uTxnParams, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]); if (pTwIf->fErrCb) { pTwIf->fErrCb(pTwIf->hErrCb, BUS_FAILURE); } /* in error do not continue */ return; } /* If Txn specific CB available, call it (may free Txn resources and issue new Txns) */ if (pTxn->fTxnDoneCb != NULL) { ((TTxnDoneCb) (pTxn->fTxnDoneCb)) (pTxn-> hCbHandle, pTxn); } } /*If uPendingTxnCount == 0 and awake not required, issue Sleep event to SM */ if ((pTwIf->uAwakeReqCount == 0) && (pTwIf->uPendingTxnCount == 0)) { twIf_HandleSmEvent(pTwIf, SM_EVENT_SLEEP); } if (pTxn == NULL) { return; } } }
/** * \fn runScheduler * \brief The scheduler processing (static function) * * Loops over the mgmt-queues (high priority first) and if queue enabled and * has packets, dequeue a packet and send it to the TxCtrl. * Exit if the port level is disabled or if couldn't send anything from both queues. * * \note Protect the queues access against preemption from external context (EAPOL). * \param pTxMgmtQ - The module's object * \return void * \sa */ static void runScheduler (TTxMgmtQ *pTxMgmtQ) { TI_STATUS eStatus; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId = 0; /* start from highest priority queue */ while(1) { /* If the Mgmt port is closed exit. */ if ( !pTxMgmtQ->bMgmtPortEnable ) { return; } /* Check that the current queue is not busy and is enabled by the state-machine. */ if ( !pTxMgmtQ->aQueueBusy[uQueId] && pTxMgmtQ->aQueueEnabledBySM[uQueId]) { /* Dequeue a packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pTxMgmtQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxMgmtQ->hContext); if (pPktCtrlBlk) { pTxMgmtQ->tDbgCounters.aDequeuePackets[uQueId]++; /* Send the packet */ eStatus = txCtrl_XmitMgmt (pTxMgmtQ->hTxCtrl, pPktCtrlBlk); /* In case the return status is busy it means that the packet wasn't handled so we need to requeue the packet for future try. */ if(eStatus == STATUS_XMIT_BUSY) { /* Requeue the packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); que_Requeue (pTxMgmtQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); context_LeaveCriticalSection (pTxMgmtQ->hContext); pTxMgmtQ->tDbgCounters.aRequeuePackets[uQueId]++; } /* The packet was handled by the lower Tx layers. */ else { pTxMgmtQ->tDbgCounters.aXmittedPackets[uQueId]++; /* Successful delivery so start next tx from the high priority queue (mgmt), * giving it strict priority over the lower queue. */ uQueId = 0; continue; } } } /* If we got here we couldn't deliver a packet from current queue, so progress to lower * priority queue and if already in lowest queue exit. */ uQueId++; if (uQueId < NUM_OF_MGMT_QUEUES) { continue; /* Try sending from next queue (i.e. the EAPOL queue). */ } else { /* We couldn't send from both queues so indicate end of packets burst and exit. */ TWD_txXfer_EndOfBurst (pTxMgmtQ->hTWD); return; } } /* End of while */ /* Unreachable code */ }
/** * \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 runScheduler * \brief The scheduler processing (static function) * * Loops over the mgmt-queues (high priority first) and if queue enabled and * has packets, dequeue a packet and send it to the TxCtrl. * Exit if the port level is disabled or if couldn't send anything from both queues. * * \note Protect the queues access against preemption from external context (EAPOL). * \param pTxMgmtQ - The module's object * \return void * \sa */ static void runScheduler (TTxMgmtQ *pTxMgmtQ) { TI_STATUS eStatus; TTxCtrlBlk *pPktCtrlBlk; TI_UINT32 uQueId = 0; /* start from highest priority queue */ TMgmtLinkQ *pLinkQ; TI_UINT32 uHlid = 0; TI_BOOL bQueueActive; TI_BOOL bLinkActive; while(1) { /* If the Mgmt port is closed or AC for mgmt is busy, exit. */ if ( (!pTxMgmtQ->bMgmtPortEnable) || (pTxMgmtQ->aMgmtAcBusy) ) { return; } pLinkQ = &pTxMgmtQ->aMgmtLinkQ[uHlid]; /* Link queues */ bLinkActive = ( (!pLinkQ->bBusy) && pLinkQ->bEnabled ); bQueueActive = pLinkQ->aQenabled[uQueId]; /* Check that the current link and current queue are not busy and are enabled */ if ( bLinkActive && bQueueActive) { /* Dequeue a packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pLinkQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxMgmtQ->hContext); if (pPktCtrlBlk) { pLinkQ->tDbgCounters.aDequeuePackets[uQueId]++; /* Send the packet */ eStatus = txCtrl_XmitMgmt (pTxMgmtQ->hTxCtrl, pPktCtrlBlk); /* In case the return status is busy it means that the packet wasn't handled so we need to requeue the packet for future try. */ if(eStatus == STATUS_XMIT_BUSY) { /* Requeue the packet in a critical section */ context_EnterCriticalSection (pTxMgmtQ->hContext); que_Requeue (pLinkQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); context_LeaveCriticalSection (pTxMgmtQ->hContext); pLinkQ->tDbgCounters.aRequeuePackets[uQueId]++; } /* The packet was handled by the lower Tx layers. */ else { pLinkQ->tDbgCounters.aXmittedPackets[uQueId]++; /* Successful delivery so start next tx from the high priority queue (mgmt), * giving it strict priority over the lower queue. */ uQueId = 0; continue; } } } /* If we got here we couldn't deliver a packet from current queue, so progress to lower * priority queue and if already in lowest queue exit. */ if (bLinkActive) /* Continue to next queue only if the link is active */ { uQueId++; if (uQueId < NUM_OF_MGMT_QUEUES) { continue; /* Try sending from next queue (i.e. the EAPOL queue). */ } } /* * continue to next link */ uHlid++; if (uHlid < WLANLINKS_MAX_LINKS) { uQueId = 0; continue; } else { /* We couldn't send from both queues so indicate end of packets burst and exit. */ TWD_txXfer_EndOfBurst (pTxMgmtQ->hTWD); return; } } /* End of while */ /* Unreachable code */ }
/** * \fn txDataQ_RunScheduler * \brief The module's Tx scheduler * * This function is the Data-Queue scheduler. * It selects a packet to transmit from the tx queues and sends it to the TxCtrl. * The queues are selected in a round-robin order. * The function is called by one of: * txDataQ_Run() * txDataQ_UpdateBusyMap() * txDataQ_WakeAll() * * \note * \param hTxDataQ - The object * \return void * \sa */ static void txDataQ_RunScheduler (TI_HANDLE hTxDataQ) { TTxDataQ *pTxDataQ = (TTxDataQ *)hTxDataQ; TI_UINT32 uIdleAcCount = 0; /* Count AC queues iterations without packet transmission (for exit criteria) */ TI_UINT32 uIdleLinkCount = 0; /* Count Links iterations without packet transmission (for exit criteria) */ TI_UINT32 uQueId = pTxDataQ->uNextQueId; /* The last iteration queue */ EStatusXmit eStatus; /* The return status of the txCtrl_xmitData function */ TTxCtrlBlk *pPktCtrlBlk; /* Pointer to the packet to be dequeued and sent */ TI_UINT32 uHlid = pTxDataQ->uNextHlid; /* The last iteration link */ TDataLinkQ *pLinkQ; TI_BOOL bStopScheduler = TI_FALSE; while(!bStopScheduler) { bStopScheduler = TI_TRUE; for(uIdleLinkCount = 0; uIdleLinkCount < WLANLINKS_MAX_LINKS; uIdleLinkCount++) { /* If the Data port is closed, indicate end of current packets burst and exit */ if ( !pTxDataQ->bDataPortEnable ) { TWD_txXfer_EndOfBurst (pTxDataQ->hTWD); return; } if (uHlid == WLANLINKS_MAX_LINKS) { uHlid = 0; } pLinkQ = &pTxDataQ->aDataLinkQ[uHlid]; /* Link queues */ /* If the link is busy (AC is full), continue to next queue. */ if ( (!pLinkQ->bEnabled) || (pLinkQ->bBusy)) { uQueId = 0; uHlid++; continue; } for(uIdleAcCount = 0; uIdleAcCount < pTxDataQ->uNumQueues; uIdleAcCount++) { if ( !pTxDataQ->bDataPortEnable ) { TWD_txXfer_EndOfBurst (pTxDataQ->hTWD); return; } if (uQueId == pTxDataQ->uNumQueues) { uQueId = 0; } if (pTxDataQ->aQueueBusy[uQueId]) { uQueId++; continue; } /* Dequeue a packet in a critical section */ context_EnterCriticalSection (pTxDataQ->hContext); pPktCtrlBlk = (TTxCtrlBlk *) que_Dequeue (pLinkQ->aQueues[uQueId]); context_LeaveCriticalSection (pTxDataQ->hContext); /* If the queue was empty, continue to the next queue */ if (pPktCtrlBlk == NULL) { if ((pTxDataQ->bStopNetStackTx) && pLinkQ->aNetStackQueueStopped[uQueId]) { pLinkQ->aNetStackQueueStopped[uQueId] = TI_FALSE; /*Resume the TX process as our date queues are empty*/ wlanDrvIf_ResumeTx (pTxDataQ->hOs); } uQueId++; continue; } #ifdef TI_DBG pLinkQ->aQueueCounters[uQueId].uDequeuePacket++; #endif /* TI_DBG */ /* Send the packet */ eStatus = txCtrl_XmitData (pTxDataQ->hTxCtrl, pPktCtrlBlk); /* * If the return status is busy it means that the packet was not sent * so we need to requeue it for future try. */ if(eStatus == STATUS_XMIT_BUSY) { TI_STATUS eQueStatus; /* Requeue the packet in a critical section */ context_EnterCriticalSection (pTxDataQ->hContext); eQueStatus = que_Requeue (pLinkQ->aQueues[uQueId], (TI_HANDLE)pPktCtrlBlk); if (eQueStatus != TI_OK) { /* If the packet can't be queued drop it */ /* Note: may happen only if this thread was preempted between the dequeue and requeue and new packets were inserted into this quque */ txCtrl_FreePacket (pTxDataQ->hTxCtrl, pPktCtrlBlk, TI_NOK); #ifdef TI_DBG pLinkQ->aQueueCounters[uQueId].uDroppedPacket++; #endif /* TI_DBG */ } context_LeaveCriticalSection (pTxDataQ->hContext); #ifdef TI_DBG pLinkQ->aQueueCounters[uQueId].uRequeuePacket++; #endif /* TI_DBG */ uQueId++; continue; } else if( eStatus == STATUS_XMIT_SUCCESS ) { bStopScheduler = TI_FALSE; /* Save the next que should be proceed by scheduler */ pTxDataQ->uNextQueId = uQueId + 1; pTxDataQ->uNextHlid = uHlid + 1; } #ifdef TI_DBG pLinkQ->aQueueCounters[uQueId].uXmittedPacket++; #endif /* TI_DBG */ uQueId++; } /* for(uIdleAcCount = 0; uIdleAcCount < pTxDataQ->uNumQueues; uIdleAcCount++)*/ uHlid++; } /*for(uIdleLinkCount = 0; uIdleLinkCount < WLANLINKS_MAX_LINKS; uIdleLinkCount++)*/ } /*while(!bStopScheduler)*/ TWD_txXfer_EndOfBurst (pTxDataQ->hTWD); }