/** * \fn busDrv_Transact * \brief Process transaction * * Called by the TxnQ module to initiate a new transaction. * Prepare the transaction parts (lower layer single transactions), * and send them one by one to the lower layer. * * \note It's assumed that this function is called only when idle (i.e. previous Txn is done). * \param hBusDrv - The module's object * \param pTxn - The transaction object * \return COMPLETE if Txn completed in this context, PENDING if not, ERROR if failed * \sa busDrv_PrepareTxnParts, busDrv_SendTxnParts */ ETxnStatus busDrv_Transact (TI_HANDLE hBusDrv, TTxnStruct *pTxn) { TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; TI_BOOL bWithinAggregation; CL_TRACE_START_L4(); pBusDrv->pCurrTxn = pTxn; pBusDrv->uCurrTxnPartsCount = 0; pBusDrv->uCurrTxnPartsCountSync = 0; /* Set status OK in Txn struct (changed later to error if transaction fails) */ TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_OK); /* Prepare the transaction parts in a table. */ bWithinAggregation = busDrv_PrepareTxnParts (pBusDrv, pTxn); /* If in the middle of Tx aggregation, return Complete (current Txn was coppied to buffer but not sent) */ if (bWithinAggregation) { TRACE1(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_Transact: In aggregation so exit, uTxnLength=%d\n", pBusDrv->uTxnLength); CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TXN", ".Transact"); return TXN_STATUS_COMPLETE; } /* Send the prepared transaction parts. */ busDrv_SendTxnParts (pBusDrv); TRACE1(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_Transact: Status = %d\n", pBusDrv->eCurrTxnStatus); CL_TRACE_END_L4("tiwlan_drv.ko", "INHERIT", "TXN", ".Transact"); /* return transaction status - COMPLETE, PENDING or ERROR */ /* The status is updated in busDrv_SendTxnParts(). It is Async (pending) if not completed in this context */ return pBusDrv->eCurrTxnStatus; }
/** * \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); /* If the function of the completed Txn is waiting for restart */ if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART) { /* 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); 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); }
/**************************************************************************** * ConnectDone_CB() **************************************************************************** * DESCRIPTION: * Called back by the WSPI driver from Async transaction end interrupt (ISR context). * Calls the upper layers callback. * * INPUTS: status - * * OUTPUT: None * * RETURNS: None ****************************************************************************/ static void ConnectDone_CB(TI_HANDLE hBusDrv, int status) { TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; /* If the last transaction failed, call failure CB and exit. */ if (status != 0) { TXN_PARAM_SET_STATUS(pBusDrv->pCurrTxn, TXN_PARAM_STATUS_ERROR); } /* Call the upper layer CB */ pBusDrv->fTxnConnectDoneCb(pBusDrv->hCbHandle,pBusDrv->pCurrTxn); }
/** * \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 (handle_t hTxnQ, void *hTxn) { TTxnQObj *pTxnQ = (TTxnQObj*)hTxnQ; TTxnStruct *pTxn = (TTxnStruct *)hTxn; McpU32 uFuncId = TXN_PARAM_GET_FUNC_ID(pTxn); #ifdef TRAN_DBG if (pTxn != pTxnQ->pCurrTxn) { MCPF_REPORT_ERROR(pTxnQ->hMcpf, QUEUE_MODULE_LOG, ("%s: CB returned pTxn 0x%p while pCurrTxn is 0x%p !!\n", __FUNCTION__, pTxn, pTxnQ->pCurrTxn)); } #endif /* Enter critical section if configured to do so */ if (pTxnQ->bProtectTxnDone) { MCPF_ENTER_CRIT_SEC (pTxnQ->hMcpf); } /* If the function of the completed Txn is waiting for restart */ if (pTxnQ->aFuncInfo[uFuncId].eState == FUNC_STATE_RESTART) { /* Call function CB for current Txn with recovery indication */ TXN_PARAM_SET_STATUS(pTxn, TXN_STATUS_RECOVERY); pTxnQ->aFuncInfo[uFuncId].fTxnQueueDoneCb (pTxnQ->aFuncInfo[uFuncId].hCbHandle, pTxn, MCP_TRUE); /* Clear the restarted function queues (call function CB with status=RECOVERY) */ txnQ_ClearQueues (pTxnQ, uFuncId, MCP_TRUE); } /* In the normal case (no restart), enqueue completed transaction in TxnDone queue */ else { que_Enqueue (pTxnQ->hTxnDoneQueue, (handle_t)pTxn); } /* 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, MCP_TRUE); /* Leave critical section if configured to do so */ if (pTxnQ->bProtectTxnDone) { MCPF_EXIT_CRIT_SEC (pTxnQ->hMcpf); } }
/** * \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_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); }
/**************************************************************************** * ConnectDone_CB() **************************************************************************** * DESCRIPTION: * Called back by the WSPI driver from Async transaction end interrupt (ISR context). * Calls the upper layers callback. * * INPUTS: status - * * OUTPUT: None * * RETURNS: None ****************************************************************************/ static void ConnectDone_CB(TI_HANDLE hBusDrv, int status) { TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; /* If the last transaction failed, call failure CB and exit. */ if (status != 0) { TRACE2(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "ConnectDone_CB : Status = %d, fTxnConnectDoneCb = 0x%x\n", status,pBusDrv->fTxnConnectDoneCb); TXN_PARAM_SET_STATUS(pBusDrv->pCurrTxn, TXN_PARAM_STATUS_ERROR); } else { TRACE1 (pBusDrv->hReport, REPORT_SEVERITY_INIT, "ConnectDone_CB: Successful Connect Async cb done \n"); } /* Call the upper layer CB */ pBusDrv->fTxnConnectDoneCb(pBusDrv->hCbHandle,pBusDrv->pCurrTxn); }
/** * \fn busDrv_TxnDoneCb * \brief Continue async transaction processing (CB) * * Called back by the lower (BSP) bus-driver upon Async transaction completion (TxnDone ISR). * Call busDrv_SendTxnParts to continue sending the remained transaction parts. * * \note * \param hBusDrv - The module's object * \param status - The last transaction result - 0 = OK, else Error * \return void * \sa busDrv_SendTxnParts */ static void busDrv_TxnDoneCb (TI_HANDLE hBusDrv, int iStatus) { TBusDrvObj *pBusDrv = (TBusDrvObj*)hBusDrv; CL_TRACE_START_L1(); /* If last transaction part failed, set error in Txn struct, call TxnDone CB and exit. */ if (iStatus != 0) { TRACE1(pBusDrv->hReport, REPORT_SEVERITY_ERROR, "busDrv_TxnDoneCb: Status = 0x%x\n", iStatus); TXN_PARAM_SET_STATUS(pBusDrv->pCurrTxn, TXN_PARAM_STATUS_ERROR); pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pBusDrv->pCurrTxn); CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", ""); return; } TRACE0(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_TxnDoneCb()\n"); /* Continue sending the remained transaction parts. */ busDrv_SendTxnParts (pBusDrv); CL_TRACE_END_L1("tiwlan_drv.ko", "TXN_DONE", "BusDrvCB", ""); }
/** * \fn busDrv_SendTxnParts * \brief Send prepared transaction parts * * Called first by busDrv_Transact(), and also from TxnDone CB after Async completion. * Sends the prepared transaction parts in a loop. * If a transaction part is Async, the loop continues later in the TxnDone ISR context. * When all parts are done, the upper layer TxnDone CB is called. * * \note * \param pBusDrv - The module's object * \return void * \sa busDrv_Transact, busDrv_PrepareTxnParts */ static void busDrv_SendTxnParts (TBusDrvObj *pBusDrv) { ETxnStatus eStatus; TTxnPart *pTxnPart; TTxnStruct *pTxn = pBusDrv->pCurrTxn; /* While there are transaction parts to send */ while (pBusDrv->uCurrTxnPartsCount < pBusDrv->uCurrTxnPartsNum) { pTxnPart = &(pBusDrv->aTxnParts[pBusDrv->uCurrTxnPartsCount]); pBusDrv->uCurrTxnPartsCount++; /* Assume pending to be ready in case we are preempted by the TxnDon CB !! */ pBusDrv->eCurrTxnStatus = TXN_STATUS_PENDING; /* If single step, send ELP byte */ if (TXN_PARAM_GET_SINGLE_STEP(pTxn)) { /* Overwrite the function id with function 0 - for ELP register !!!! */ eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL, pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->bMore); /* If first write failed try once again (may happen once upon chip wakeup) */ if (eStatus == TXN_STATUS_ERROR) { /* Overwrite the function id with function 0 - for ELP register !!!! */ eStatus = sdioAdapt_TransactBytes (TXN_FUNC_ID_CTRL, pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->bMore); TRACE0(pBusDrv->hReport, REPORT_SEVERITY_WARNING, "busDrv_SendTxnParts: SDIO Single-Step transaction failed once so try again"); } } else { eStatus = sdioAdapt_Transact (TXN_PARAM_GET_FUNC_ID(pTxn), pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->bBlkMode, ((TXN_PARAM_GET_FIXED_ADDR(pTxn) == 1) ? 0 : 1), pTxnPart->bMore); } TRACE7(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: PartNum = %d, SingleStep = %d, Direction = %d, HwAddr = 0x%x, HostAddr = 0x%x, Length = %d, BlkMode = %d\n", pBusDrv->uCurrTxnPartsCount-1, TXN_PARAM_GET_SINGLE_STEP(pTxn), TXN_PARAM_GET_DIRECTION(pTxn), pTxnPart->uHwAddr, pTxnPart->pHostAddr, pTxnPart->uLength, pTxnPart->bBlkMode); /* If pending TxnDone (Async), continue this loop in the next TxnDone interrupt */ if (eStatus == TXN_STATUS_PENDING) { return; } /* Update current transaction status to deduce if it is all finished in the original context (Sync) or not. */ pBusDrv->eCurrTxnStatus = eStatus; pBusDrv->uCurrTxnPartsCountSync++; /* If error, set error in Txn struct, call TxnDone CB if not fully sync, and exit */ if (eStatus == TXN_STATUS_ERROR) { TXN_PARAM_SET_STATUS(pTxn, TXN_PARAM_STATUS_ERROR); if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount) { pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn); } return; } } /* If we got here we sent all buffers and we don't pend transaction end */ TRACE3(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_SendTxnParts: Txn finished successfully, Status = %d, PartsCount = %d, SyncCount = %d\n", pBusDrv->eCurrTxnStatus, pBusDrv->uCurrTxnPartsCount, pBusDrv->uCurrTxnPartsCountSync); /* For read transaction, copy the data from the DMA-able buffer to the host buffer(s) */ if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_READ) { TI_UINT32 uBufNum; TI_UINT32 uBufLen; TI_UINT8 *pDmaBuf = pBusDrv->pRxDmaBuf; /* After the read transaction the data is in the Rx DMA buffer */ for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) { uBufLen = pTxn->aLen[uBufNum]; /* If no more buffers, exit the loop */ if (uBufLen == 0) { break; } os_memoryCopy (pBusDrv->hOs, pTxn->aBuf[uBufNum], pDmaBuf, uBufLen); pDmaBuf += uBufLen; } } /* If not fully sync, call TxnDone CB */ if (pBusDrv->uCurrTxnPartsCountSync != pBusDrv->uCurrTxnPartsCount) { pBusDrv->fTxnDoneCb (pBusDrv->hCbHandle, pTxn); } }