/** * \fn busDrv_Transact * \brief Process transaction * * Called by the TxnQ module to initiate a new transaction. * Call either write or read functions according to Txn direction field. * * \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; WSPI_CB_T cb; TI_UINT8 * tempReadBuff; TI_UINT8 * tempWriteBuff; pBusDrv->pCurrTxn = pTxn; pBusDrv->eCurrTxnStatus = TXN_STATUS_COMPLETE; /* The Txn is Sync as long as it continues in this context */ cb.CBFunc = asyncEnded_CB; /* The BusTxn callback called upon Async transaction end. */ cb.CBArg = hBusDrv; /* The handle for the BusTxnCB. */ /* If write command */ if (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_WRITE) { //WLAN_REPORT_INIT(pBusDrv->hReport, TNETW_DRV_MODULE_LOG, // ("busDrv_Transact: Write to pTxn->uHwAddr %x\n", pTxn->uHwAddr )); /*WLAN_REPORT_ERROR(pBusDrv->hReport, BUS_DRV_MODULE_LOG, ("busDrv_Transact: Buff= %x, Len=%d\n", pTxn->aBuf[0], pTxn->aLen[0]));*/ /*1.write memory*/ /* Decrease the data pointer to the beginning of the WSPI padding */ tempWriteBuff = pTxn->aBuf[0]; tempWriteBuff -= WSPI_PAD_LEN_WRITE; /* Write the data to the WSPI in Aync mode (not completed in the current context). */ pBusDrv->eCurrTxnStatus = WSPI_WriteAsync(pBusDrv->hWspi, pTxn->uHwAddr,tempWriteBuff,pTxn->aLen[0], &cb, TI_TRUE, TI_TRUE,TXN_PARAM_GET_FIXED_ADDR(pTxn)); } /* If read command */ else { //WLAN_REPORT_INIT(pBusDrv->hReport, TNETW_DRV_MODULE_LOG, // ("busDrv_Transact: Read from pTxn->uHwAddr %x pTxn %x \n", pTxn->uHwAddr,pTxn)); /*1. Read mem */ /* Decrease the tempReadBuff pointer to the beginning of the WSPI padding */ tempReadBuff = pTxn->aBuf[0]; tempReadBuff -= WSPI_PAD_LEN_READ; /* Read the required data from the WSPI in Aync mode (not completed in the current context). */ pBusDrv->eCurrTxnStatus = WSPI_ReadAsync(pBusDrv->hWspi, pTxn->uHwAddr,tempReadBuff,pTxn->aLen[0], &cb, TI_TRUE, TI_TRUE,TXN_PARAM_GET_FIXED_ADDR(pTxn)); } /* return transaction status - COMPLETE, PENDING or ERROR */ return (pBusDrv->eCurrTxnStatus == WSPI_TXN_COMPLETE ? TXN_STATUS_COMPLETE : (pBusDrv->eCurrTxnStatus == WSPI_TXN_PENDING ? TXN_STATUS_PENDING : TXN_STATUS_ERROR)); }
/** * \fn busDrv_PrepareTxnParts * \brief Prepare write or read transaction parts * * Called by busDrv_Transact(). * Prepares the actual sequence of SDIO bus transactions in a table. * Use a DMA-able buffer for the bus transaction, so all data is copied * to it from the host buffer(s) before write transactions, * or copied from it to the host buffers after read transactions. * * \note * \param pBusDrv - The module's object * \param pTxn - The transaction object * \return TRUE if we are in the middle of a Tx aggregation * \sa busDrv_Transact, busDrv_SendTxnParts, */ static TI_BOOL busDrv_PrepareTxnParts (TBusDrvObj *pBusDrv, TTxnStruct *pTxn) { TI_UINT32 uPartNum = 0; TI_UINT32 uCurrHwAddr = pTxn->uHwAddr; TI_BOOL bFixedHwAddr = TXN_PARAM_GET_FIXED_ADDR(pTxn); TI_BOOL bWrite = (TXN_PARAM_GET_DIRECTION(pTxn) == TXN_DIRECTION_WRITE) ? TI_TRUE : TI_FALSE; TI_UINT8 *pHostBuf = bWrite ? pBusDrv->pTxDmaBuf : pBusDrv->pRxDmaBuf; /* Use DMA buffer (Rx or Tx) for actual transaction */ TI_UINT32 uBufNum; TI_UINT32 uBufLen; TI_UINT32 uRemainderLen; /* Go over the transaction buffers */ for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) { uBufLen = pTxn->aLen[uBufNum]; /* If no more buffers, exit the loop */ if (uBufLen == 0) { break; } /* For write transaction, copy the data to the DMA buffer */ if (bWrite) { os_memoryCopy (pBusDrv->hOs, pHostBuf + pBusDrv->uTxnLength, pTxn->aBuf[uBufNum], uBufLen); } /* Add buffer length to total transaction length */ pBusDrv->uTxnLength += uBufLen; } /* If in a Tx aggregation, return TRUE (need to accumulate all parts before sending the transaction) */ if (TXN_PARAM_GET_AGGREGATE(pTxn) == TXN_AGGREGATE_ON) { TRACE6(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_PrepareTxnParts: In aggregation so exit, uTxnLength=%d, bWrite=%d, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", pBusDrv->uTxnLength, bWrite, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]); return TI_TRUE; } /* If current buffer has a remainder, prepare its transaction part */ uRemainderLen = pBusDrv->uTxnLength & pBusDrv->uBlkSizeMask; if (uRemainderLen > 0) { pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_FALSE; pBusDrv->aTxnParts[uPartNum].uLength = uRemainderLen; pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)pHostBuf; pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; /* If not fixed HW address, increment it by this part's size */ if (!bFixedHwAddr) { uCurrHwAddr += uRemainderLen; } uPartNum++; } #ifdef DISABLE_SDIO_MULTI_BLK_MODE /* SDIO multi-block mode is disabled so split to 512 bytes blocks */ { TI_UINT32 uLen; for (uLen = uRemainderLen; uLen < pBusDrv->uTxnLength; uLen += pBusDrv->uBlkSize) { pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_FALSE; pBusDrv->aTxnParts[uPartNum].uLength = pBusDrv->uBlkSize; pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uLen); pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; /* If not fixed HW address, increment it by this part's size */ if (!bFixedHwAddr) { uCurrHwAddr += pBusDrv->uBlkSize; } uPartNum++; } } #else /* Use SDIO block mode (this is the default behavior) */ /* If current buffer has full SDIO blocks, prepare a block-mode transaction part */ if (pBusDrv->uTxnLength >= pBusDrv->uBlkSize) { pBusDrv->aTxnParts[uPartNum].bBlkMode = TI_TRUE; pBusDrv->aTxnParts[uPartNum].uLength = pBusDrv->uTxnLength - uRemainderLen; pBusDrv->aTxnParts[uPartNum].uHwAddr = uCurrHwAddr; pBusDrv->aTxnParts[uPartNum].pHostAddr = (void *)(pHostBuf + uRemainderLen); pBusDrv->aTxnParts[uPartNum].bMore = TI_TRUE; uPartNum++; } #endif /* DISABLE_SDIO_MULTI_BLK_MODE */ /* Set last More flag as specified for the whole Txn */ pBusDrv->aTxnParts[uPartNum - 1].bMore = TXN_PARAM_GET_MORE(pTxn); pBusDrv->uCurrTxnPartsNum = uPartNum; TRACE9(pBusDrv->hReport, REPORT_SEVERITY_INFORMATION, "busDrv_PrepareTxnParts: Txn prepared, PartsNum=%d, bWrite=%d, uTxnLength=%d, uRemainderLen=%d, uHwAddr=0x%x, Len0=%d, Len1=%d, Len2=%d, Len3=%d\n", uPartNum, bWrite, pBusDrv->uTxnLength, uRemainderLen, pTxn->uHwAddr, pTxn->aLen[0], pTxn->aLen[1], pTxn->aLen[2], pTxn->aLen[3]); pBusDrv->uTxnLength = 0; /* Return FALSE to indicate that we are not in the middle of a Tx aggregation so the Txn is ready to send */ return TI_FALSE; }
/** * \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); } }
/** * \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; }