| /* |
| * RxXfer.c |
| * |
| * Copyright(c) 1998 - 2010 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. |
| */ |
| |
| |
| /**************************************************************************** |
| * |
| * MODULE: rxXfer.c |
| * |
| * PURPOSE: Rx Xfer module implementation.Responsible for reading Rx from the FW |
| * and forward it to the upper layers. |
| * |
| ****************************************************************************/ |
| |
| #define __FILE_ID__ FILE_ID_106 |
| #include "tidef.h" |
| #include "osApi.h" |
| #include "report.h" |
| #include "rxXfer_api.h" |
| #include "FwEvent_api.h" |
| #include "TWDriverInternal.h" |
| #include "RxQueue_api.h" |
| #include "TwIf.h" |
| #include "public_host_int.h" |
| #include "bmtrace_api.h" |
| |
| #define RX_DRIVER_COUNTER_ADDRESS 0x300538 |
| #define PLCP_HEADER_LENGTH 8 |
| #define WORD_SIZE 4 |
| #define UNALIGNED_PAYLOAD 0x1 |
| #define RX_DESCRIPTOR_SIZE (sizeof(RxIfDescriptor_t)) |
| #define MAX_PACKETS_NUMBER 8 |
| #define MAX_CONSECUTIVE_READ_TXN 16 |
| #define MAX_PACKET_SIZE 8192 /* Max Txn size */ |
| |
| #ifdef PLATFORM_SYMBIAN /* UMAC is using only one buffer and therefore we can't use consecutive reads */ |
| #define MAX_CONSECUTIVE_READS 1 |
| #else |
| #define MAX_CONSECUTIVE_READS 8 |
| #endif |
| |
| #define SLV_MEM_CP_VALUE(desc, offset) (((RX_DESC_GET_MEM_BLK(desc) << 8) + offset)) |
| #define ALIGNMENT_SIZE(desc) ((RX_DESC_GET_UNALIGNED(desc) & UNALIGNED_PAYLOAD) ? 2 : 0) |
| |
| #if (NUM_RX_PKT_DESC & (NUM_RX_PKT_DESC - 1)) |
| #error NUM_RX_PKT_DESC is not a power of 2 which may degrade performance when we calculate modulo!! |
| #endif |
| |
| |
| #ifdef TI_DBG |
| typedef struct |
| { |
| TI_UINT32 uCountFwEvents; |
| TI_UINT32 uCountPktsForward; |
| TI_UINT32 uCountBufPend; |
| TI_UINT32 uCountBufNoMem; |
| TI_UINT32 uCountPktAggreg[MAX_XFER_BUFS]; |
| |
| } TRxXferDbgStat; |
| #endif |
| |
| typedef struct |
| { |
| TTxnStruct tTxnStruct; |
| TI_UINT32 uRegData; |
| TI_UINT32 uRegAdata; |
| |
| } TRegTxn; |
| |
| typedef struct |
| { |
| TTxnStruct tTxnStruct; |
| TI_UINT32 uCounter; |
| |
| } TCounterTxn; |
| |
| typedef struct |
| { |
| TI_HANDLE hOs; |
| TI_HANDLE hReport; |
| TI_HANDLE hTwIf; |
| TI_HANDLE hFwEvent; |
| TI_HANDLE hRxQueue; |
| |
| TI_UINT32 aRxPktsDesc[NUM_RX_PKT_DESC]; /* Save Rx packets short descriptors from FwStatus */ |
| TI_UINT32 uFwRxCntr; /* Save last FW packets counter from FwStatus */ |
| TI_UINT32 uDrvRxCntr; /* The current driver processed packets counter */ |
| TI_UINT32 uPacketMemoryPoolStart; /* The FW mem-blocks area base address */ |
| TI_UINT32 uMaxAggregLen; /* The max length in bytes of aggregated packets transaction */ |
| TI_UINT32 uMaxAggregPkts; /* The max number of packets that may be aggregated in one transaction */ |
| TRequestForBufferCb RequestForBufferCB; /* Upper layer CB for allocating buffers for packets */ |
| TI_HANDLE RequestForBufferCB_handle; /* The upper later CB handle */ |
| TI_BOOL bPendingBuffer; /* If TRUE, we exited the Rx handler upon pending-buffer */ |
| |
| TI_UINT32 uCurrTxnIndex; /* The current Txn structures index to use */ |
| TI_UINT32 uAvailableTxn; /* Number of Txn structures currently available */ |
| TRegTxn aSlaveRegTxn[MAX_CONSECUTIVE_READ_TXN]; /* Txn structures for writing mem-block address reg */ |
| TTxnStruct aTxnStruct[MAX_CONSECUTIVE_READ_TXN]; /* Txn structures for reading the Rx packets */ |
| TCounterTxn aCounterTxn[MAX_CONSECUTIVE_READ_TXN]; /* Txn structures for writing the driver counter workaround */ |
| |
| TI_UINT8 aTempBuffer[MAX_PACKET_SIZE]; /* Dummy buffer to use if we couldn't get a buffer for the packet (so drop the packet) */ |
| TFailureEventCb fErrCb; /* The upper layer CB function for error handling */ |
| TI_HANDLE hErrCb; /* The CB function handle */ |
| |
| #ifdef TI_DBG |
| TRxXferDbgStat tDbgStat; |
| #endif |
| |
| } TRxXfer; |
| |
| |
| /************************ static function declaration *****************************/ |
| static TI_STATUS rxXfer_Handle(TI_HANDLE hRxXfer); |
| static void rxXfer_TxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct* pTxn); |
| static void rxXfer_PktDropTxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn); |
| static ETxnStatus rxXfer_IssueTxn (TI_HANDLE hRxXfer, TI_UINT32 uFirstMemBlkAddr); |
| static void rxXfer_ForwardPacket (TRxXfer* pRxXfer, TTxnStruct* pTxn); |
| |
| |
| /**************************************************************************** |
| * RxXfer_Create() |
| **************************************************************************** |
| * DESCRIPTION: Create the RxXfer module object |
| * |
| * INPUTS: None |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: The Created object |
| ****************************************************************************/ |
| TI_HANDLE rxXfer_Create (TI_HANDLE hOs) |
| { |
| TRxXfer *pRxXfer; |
| |
| pRxXfer = os_memoryAlloc (hOs, sizeof(TRxXfer)); |
| if (pRxXfer == NULL) |
| return NULL; |
| |
| /* For all the counters */ |
| os_memoryZero (hOs, pRxXfer, sizeof(TRxXfer)); |
| |
| pRxXfer->hOs = hOs; |
| |
| return (TI_HANDLE)pRxXfer; |
| } |
| |
| |
| /**************************************************************************** |
| * RxXfer_Destroy() |
| **************************************************************************** |
| * DESCRIPTION: Destroy the RxXfer module object |
| * |
| * INPUTS: hRxXfer - The object to free |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| void rxXfer_Destroy (TI_HANDLE hRxXfer) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| if (pRxXfer) |
| { |
| os_memoryFree (pRxXfer->hOs, pRxXfer, sizeof(TRxXfer)); |
| } |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_init() |
| **************************************************************************** |
| * DESCRIPTION: Init the module object |
| * |
| * INPUTS: hRxXfer - module handle; |
| * other modules handles. |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: None |
| ****************************************************************************/ |
| void rxXfer_Init(TI_HANDLE hRxXfer, |
| TI_HANDLE hFwEvent, |
| TI_HANDLE hReport, |
| TI_HANDLE hTwIf, |
| TI_HANDLE hRxQueue) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| pRxXfer->hFwEvent = hFwEvent; |
| pRxXfer->hReport = hReport; |
| pRxXfer->hTwIf = hTwIf; |
| pRxXfer->hRxQueue = hRxQueue; |
| |
| rxXfer_Restart (hRxXfer); |
| |
| #ifdef TI_DBG |
| rxXfer_ClearStats (pRxXfer); |
| #endif |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_SetDefaults() |
| **************************************************************************** |
| * DESCRIPTION: Set module parameters default setting |
| * |
| * INPUTS: hRxXfer - module handle; |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: None |
| ****************************************************************************/ |
| void rxXfer_SetDefaults (TI_HANDLE hRxXfer, TTwdInitParams *pInitParams) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| pRxXfer->uMaxAggregPkts = pInitParams->tGeneral.uRxAggregPktsLimit; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_SetBusParams() |
| **************************************************************************** |
| * DESCRIPTION: Configure bus driver DMA-able buffer length to be used as a limit to the aggragation length. |
| * |
| * INPUTS: hRxXfer - module handle |
| * uDmaBufLen - The bus driver DMA-able buffer length |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: None |
| ****************************************************************************/ |
| void rxXfer_SetBusParams (TI_HANDLE hRxXfer, TI_UINT32 uDmaBufLen) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| pRxXfer->uMaxAggregLen = uDmaBufLen; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_Register_CB() |
| **************************************************************************** |
| * DESCRIPTION: Register the function to be called for request for buffer. |
| * |
| * INPUTS: hRxXfer - RxXfer handle; |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: None |
| ****************************************************************************/ |
| void rxXfer_Register_CB (TI_HANDLE hRxXfer, TI_UINT32 CallBackID, void *CBFunc, TI_HANDLE CBObj) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| TRACE1(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_Register_CB (Value = 0x%x)\n", CallBackID); |
| |
| switch(CallBackID) |
| { |
| case TWD_INT_REQUEST_FOR_BUFFER: |
| pRxXfer->RequestForBufferCB = (TRequestForBufferCb)CBFunc; |
| pRxXfer->RequestForBufferCB_handle = CBObj; |
| break; |
| |
| default: |
| TRACE0(pRxXfer->hReport, REPORT_SEVERITY_ERROR, "rxXfer_Register_CB - Illegal value\n"); |
| return; |
| } |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_ForwardPacket() |
| **************************************************************************** |
| * DESCRIPTION: Forward received packet(s) to the upper layers. |
| * |
| * INPUTS: |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| static void rxXfer_ForwardPacket (TRxXfer *pRxXfer, TTxnStruct *pTxn) |
| { |
| TI_UINT32 uBufNum; |
| RxIfDescriptor_t *pRxInfo = (RxIfDescriptor_t*)(pTxn->aBuf[0]); |
| #ifdef TI_DBG /* for packet sanity check */ |
| TI_UINT16 uLenFromRxInfo; |
| #endif |
| |
| /* Go over all occupied Txn buffers and forward their Rx packets upward */ |
| for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) |
| { |
| /* If no more buffers, exit the loop */ |
| if (pTxn->aLen[uBufNum] == 0) |
| { |
| break; |
| } |
| |
| #ifdef TI_DBG /* Packet sanity check */ |
| /* Get length from RxInfo, handle endianess and convert to length in bytes */ |
| pRxInfo = (RxIfDescriptor_t*)(pTxn->aBuf[uBufNum]); |
| uLenFromRxInfo = ENDIAN_HANDLE_WORD(pRxInfo->length) << 2; |
| |
| /* If the length in the RxInfo is different than in the short descriptor, set error status */ |
| if (pTxn->aLen[uBufNum] != uLenFromRxInfo) |
| { |
| TRACE3(pRxXfer->hReport, REPORT_SEVERITY_ERROR , "rxXfer_ForwardPacket: Bad Length!! RxInfoLength=%d, ShortDescLen=%d, RxInfoStatus=0x%x\n", uLenFromRxInfo, pTxn->aLen[uBufNum], pRxInfo->status); |
| |
| pRxInfo->status &= ~RX_DESC_STATUS_MASK; |
| pRxInfo->status |= RX_DESC_STATUS_DRIVER_RX_Q_FAIL; |
| pRxInfo->length = ENDIAN_HANDLE_WORD(pTxn->aLen[uBufNum] >> 2); |
| |
| /* If error CB available, trigger recovery !! */ |
| if (pRxXfer->fErrCb) |
| { |
| pRxXfer->fErrCb (pRxXfer->hErrCb, RX_XFER_FAILURE); |
| } |
| } |
| else |
| { |
| TRACE2(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_ForwardPacket: RxInfoLength=%d, RxInfoStatus=0x%x\n", uLenFromRxInfo, pRxInfo->status); |
| } |
| pRxXfer->tDbgStat.uCountPktsForward++; |
| #endif |
| |
| /* This is the last packet in the Burst so mark its EndOfBurst flag */ |
| if (TXN_PARAM_GET_END_OF_BURST(pTxn) && (uBufNum == (MAX_XFER_BUFS - 1) || pTxn->aLen[uBufNum + 1] == 0)) |
| { |
| TXN_PARAM_SET_END_OF_BURST(pTxn, 0); |
| pRxInfo->driverFlags |= DRV_RX_FLAG_END_OF_BURST; |
| } |
| /* Forward received packet to the upper layers */ |
| RxQueue_ReceivePacket (pRxXfer->hRxQueue, (const void *)pTxn->aBuf[uBufNum]); |
| } |
| |
| /* reset the aBuf field for clean on recovery purpose */ |
| pTxn->aBuf[0] = 0; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_RxEvent() |
| **************************************************************************** |
| * DESCRIPTION: Called upon Rx event from the FW.calls the SM |
| * |
| * INPUTS: hRxXfer - RxXfer handle; |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: TWIF_OK in case of Synch mode, or TWIF_PENDING in case of Asynch mode |
| * (when returning TWIF_PENDING, FwEvent module expects the FwEvent_EventComplete() |
| * function call to finish the Rx Client handling |
| * |
| ****************************************************************************/ |
| ETxnStatus rxXfer_RxEvent (TI_HANDLE hRxXfer, FwStatus_t *pFwStatus) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| TI_UINT32 uTempCounters; |
| FwStatCntrs_t *pFwStatusCounters; |
| TI_UINT32 i; |
| TI_STATUS rc; |
| CL_TRACE_START_L2(); |
| |
| uTempCounters = ENDIAN_HANDLE_LONG (pFwStatus->counters); |
| pFwStatusCounters = (FwStatCntrs_t*)(&uTempCounters); |
| |
| TRACE2(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_RxEvent: NewFwCntr=%d, OldFwCntr=%d\n", pFwStatusCounters->fwRxCntr, pRxXfer->uFwRxCntr); |
| |
| /* If no new Rx packets - exit */ |
| if ((pFwStatusCounters->fwRxCntr % NUM_RX_PKT_DESC) == (pRxXfer->uFwRxCntr % NUM_RX_PKT_DESC)) |
| { |
| CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", ""); |
| return TXN_STATUS_COMPLETE; |
| } |
| |
| #ifdef TI_DBG |
| pRxXfer->tDbgStat.uCountFwEvents++; |
| #endif |
| |
| /* Save current FW counter and Rx packets short descriptors for processing */ |
| pRxXfer->uFwRxCntr = pFwStatusCounters->fwRxCntr; |
| for (i = 0; i < NUM_RX_PKT_DESC; i++) |
| { |
| pRxXfer->aRxPktsDesc[i] = ENDIAN_HANDLE_LONG (pFwStatus->rxPktsDesc[i]); |
| } |
| |
| /* Handle all new Rx packets */ |
| rc = rxXfer_Handle (pRxXfer); |
| |
| CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", ""); |
| return TXN_STATUS_COMPLETE; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_Handle() |
| **************************************************************************** |
| * DESCRIPTION: |
| * |
| * INPUTS: hRxXfer - RxXfer handle; |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| static TI_STATUS rxXfer_Handle(TI_HANDLE hRxXfer) |
| { |
| #ifndef _VLCT_ |
| TRxXfer * pRxXfer = (TRxXfer *)hRxXfer; |
| TI_BOOL bIssueTxn = TI_FALSE; /* If TRUE transact current aggregated packets */ |
| TI_BOOL bDropLastPkt = TI_FALSE; /* If TRUE, need to drop last packet (RX_BUF_ALLOC_OUT_OF_MEM) */ |
| TI_BOOL bExit = TI_FALSE; /* If TRUE, can't process further packets so exit (after serving the other flags) */ |
| TI_UINT32 uAggregPktsNum = 0; /* Number of aggregated packets */ |
| TI_UINT32 uFirstMemBlkAddr = 0; |
| TI_UINT32 uRxDesc = 0; |
| TI_UINT32 uBuffSize = 0; |
| TI_UINT32 uTotalAggregLen = 0; |
| TI_UINT32 uDrvIndex; |
| TI_UINT32 uFwIndex; |
| TI_UINT8 * pHostBuf; |
| TTxnStruct * pTxn = NULL; |
| ETxnStatus eTxnStatus; |
| ERxBufferStatus eBufStatus; |
| PacketClassTag_e eRxPacketType; |
| CL_TRACE_START_L2(); |
| |
| |
| /* If no Txn structures available exit!! (fatal error - not expected to happen) */ |
| if (pRxXfer->uAvailableTxn == 0 ) |
| { |
| TRACE0(pRxXfer->hReport, REPORT_SEVERITY_ERROR, "rxXfer_Handle: No available Txn structures left!\n"); |
| CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", ""); |
| return TI_NOK; |
| } |
| |
| uFwIndex = pRxXfer->uFwRxCntr % NUM_RX_PKT_DESC; |
| |
| /* Loop while Rx packets can be transfered from the FW */ |
| while (1) |
| { |
| uDrvIndex = pRxXfer->uDrvRxCntr % NUM_RX_PKT_DESC; |
| |
| /* If there are unprocessed Rx packets */ |
| if (uDrvIndex != uFwIndex) |
| { |
| /* Get next packte info */ |
| uRxDesc = pRxXfer->aRxPktsDesc[uDrvIndex]; |
| uBuffSize = RX_DESC_GET_LENGTH(uRxDesc) << 2; |
| eRxPacketType = (PacketClassTag_e)RX_DESC_GET_PACKET_CLASS_TAG (uRxDesc); |
| |
| /* If new packet exceeds max aggregation length, set flag to send previous packets (postpone it to next loop) */ |
| if ((uTotalAggregLen + uBuffSize) > pRxXfer->uMaxAggregLen) |
| { |
| bIssueTxn = TI_TRUE; |
| } |
| |
| /* No length limit so try to aggregate new packet */ |
| else |
| { |
| /* Allocate host read buffer */ |
| /* The RxBufAlloc() add an extra word for MAC header alignment in case of QoS MSDU */ |
| eBufStatus = pRxXfer->RequestForBufferCB(pRxXfer->RequestForBufferCB_handle, |
| (void**)&pHostBuf, |
| uBuffSize, |
| (TI_UINT32)NULL, |
| eRxPacketType); |
| |
| TRACE6(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_Handle: Index=%d, RxDesc=0x%x, DrvCntr=%d, FwCntr=%d, BufStatus=%d, BuffSize=%d\n", uDrvIndex, uRxDesc, pRxXfer->uDrvRxCntr, pRxXfer->uFwRxCntr, eBufStatus, uBuffSize); |
| |
| /* If buffer allocated, add it to current Txn (up to 4 packets aggregation) */ |
| if (eBufStatus == RX_BUF_ALLOC_COMPLETE) |
| { |
| /* If first aggregated packet prepare the next Txn struct */ |
| if (uAggregPktsNum == 0) |
| { |
| pTxn = (TTxnStruct*)&(pRxXfer->aTxnStruct[pRxXfer->uCurrTxnIndex]); |
| pTxn->uHwAddr = SLV_MEM_DATA; |
| |
| /* Save first mem-block of first aggregated packet! */ |
| uFirstMemBlkAddr = SLV_MEM_CP_VALUE(uRxDesc, pRxXfer->uPacketMemoryPoolStart); |
| } |
| pTxn->aBuf[uAggregPktsNum] = pHostBuf + ALIGNMENT_SIZE(uRxDesc); |
| pTxn->aLen[uAggregPktsNum] = uBuffSize; |
| uAggregPktsNum++; |
| uTotalAggregLen += uBuffSize; |
| if (uAggregPktsNum >= pRxXfer->uMaxAggregPkts) |
| { |
| bIssueTxn = TI_TRUE; |
| } |
| pRxXfer->uDrvRxCntr++; |
| } |
| |
| /* If buffer pending until freeing previous buffer, set Exit flag and if needed set IssueTxn flag. */ |
| else if (eBufStatus == RX_BUF_ALLOC_PENDING) |
| { |
| bExit = TI_TRUE; |
| pRxXfer->bPendingBuffer = TI_TRUE; |
| if (uAggregPktsNum > 0) |
| { |
| bIssueTxn = TI_TRUE; |
| } |
| #ifdef TI_DBG |
| pRxXfer->tDbgStat.uCountBufPend++; |
| #endif |
| } |
| |
| /* If no buffer due to out-of-memory, set DropLastPkt flag and if needed set IssueTxn flag. */ |
| else |
| { |
| bDropLastPkt = TI_TRUE; |
| if (uAggregPktsNum > 0) |
| { |
| bIssueTxn = TI_TRUE; |
| } |
| #ifdef TI_DBG |
| pRxXfer->tDbgStat.uCountBufNoMem++; |
| #endif |
| } |
| } |
| } |
| |
| /* If no more packets, set Exit flag and if needed set IssueTxn flag. */ |
| else |
| { |
| bExit = TI_TRUE; |
| if (uAggregPktsNum > 0) |
| { |
| bIssueTxn = TI_TRUE; |
| } |
| } |
| |
| |
| /* If required to send Rx packet(s) transaction */ |
| if (bIssueTxn) |
| { |
| if (bExit) |
| { |
| TXN_PARAM_SET_END_OF_BURST(pTxn, 1); |
| } |
| /* If not all 4 Txn buffers are used, reset first unused buffer length for indication */ |
| if (uAggregPktsNum < MAX_XFER_BUFS) |
| { |
| pTxn->aLen[uAggregPktsNum] = 0; |
| } |
| |
| eTxnStatus = rxXfer_IssueTxn (pRxXfer, uFirstMemBlkAddr); |
| |
| if (eTxnStatus == TXN_STATUS_COMPLETE) |
| { |
| /* Forward received packet to the upper layers */ |
| rxXfer_ForwardPacket (pRxXfer, pTxn); |
| } |
| else if (eTxnStatus == TXN_STATUS_PENDING) |
| { |
| /* Decrease the number of available txn structures */ |
| pRxXfer->uAvailableTxn--; |
| } |
| else |
| { |
| TRACE3(pRxXfer->hReport, REPORT_SEVERITY_ERROR , "rxXfer_Handle: Status=%d, DrvCntr=%d, RxDesc=0x%x\n", eTxnStatus, pRxXfer->uDrvRxCntr, uRxDesc); |
| } |
| |
| #ifdef TI_DBG |
| pRxXfer->tDbgStat.uCountPktAggreg[uAggregPktsNum - 1]++; |
| #endif |
| |
| uAggregPktsNum = 0; |
| uTotalAggregLen = 0; |
| bIssueTxn = TI_FALSE; |
| pRxXfer->uCurrTxnIndex = (pRxXfer->uCurrTxnIndex + 1) % MAX_CONSECUTIVE_READ_TXN; |
| } |
| |
| /* If last packet should be dropped (no memory for host buffer) */ |
| if (bDropLastPkt) |
| { |
| /* Increment driver packets counter before calling rxXfer_IssueTxn() */ |
| pRxXfer->uDrvRxCntr++; |
| |
| /* Read packet to dummy buffer and ignore it (no callback needed) */ |
| uFirstMemBlkAddr = SLV_MEM_CP_VALUE(uRxDesc, pRxXfer->uPacketMemoryPoolStart); |
| pTxn = (TTxnStruct*)&pRxXfer->aTxnStruct[pRxXfer->uCurrTxnIndex]; |
| BUILD_TTxnStruct(pTxn, SLV_MEM_DATA, pRxXfer->aTempBuffer, uBuffSize, (TTxnDoneCb)rxXfer_PktDropTxnDoneCb, hRxXfer) |
| eTxnStatus = rxXfer_IssueTxn (pRxXfer, uFirstMemBlkAddr); |
| if (eTxnStatus == TXN_STATUS_PENDING) |
| { |
| pRxXfer->uAvailableTxn--; |
| } |
| pRxXfer->uCurrTxnIndex = (pRxXfer->uCurrTxnIndex + 1) % MAX_CONSECUTIVE_READ_TXN; |
| bDropLastPkt = TI_FALSE; |
| } |
| |
| /* Can't process more packets so exit */ |
| if (bExit) |
| { |
| CL_TRACE_END_L2("tiwlan_drv.ko", "CONTEXT", "RX", ""); |
| return TI_OK; |
| } |
| |
| } /* End of while(1) */ |
| |
| /* Unreachable code */ |
| |
| #endif |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_IssueTxn() |
| **************************************************************************** |
| * DESCRIPTION: |
| * |
| * INPUTS: |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| static ETxnStatus rxXfer_IssueTxn (TI_HANDLE hRxXfer, TI_UINT32 uFirstMemBlkAddr) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| TI_UINT32 uIndex = pRxXfer->uCurrTxnIndex; |
| TTxnStruct *pTxn; |
| ETxnStatus eStatus; |
| |
| /* Write the next mem block that we want to read */ |
| pTxn = &pRxXfer->aSlaveRegTxn[uIndex].tTxnStruct; |
| pTxn->uHwAddr = SLV_REG_DATA; |
| pRxXfer->aSlaveRegTxn[uIndex].uRegData = ENDIAN_HANDLE_LONG(uFirstMemBlkAddr); |
| pRxXfer->aSlaveRegTxn[uIndex].uRegAdata = ENDIAN_HANDLE_LONG(uFirstMemBlkAddr + 4); |
| twIf_Transact(pRxXfer->hTwIf, pTxn); |
| |
| /* Issue the packet(s) read transaction (prepared in rxXfer_Handle) */ |
| pTxn = &pRxXfer->aTxnStruct[uIndex]; |
| eStatus = twIf_Transact(pRxXfer->hTwIf, pTxn); |
| |
| /* Write driver packets counter to FW. This write automatically generates interrupt to FW */ |
| /* Note: Workaround for WL6-PG1.0 is still needed for PG2.0 ==> if (pRxXfer->bChipIs1273Pg10) */ |
| pTxn = &pRxXfer->aCounterTxn[uIndex].tTxnStruct; |
| pTxn->uHwAddr = RX_DRIVER_COUNTER_ADDRESS; |
| pRxXfer->aCounterTxn[uIndex].uCounter = ENDIAN_HANDLE_LONG(pRxXfer->uDrvRxCntr); |
| twIf_Transact(pRxXfer->hTwIf, pTxn); |
| |
| TRACE5(pRxXfer->hReport, REPORT_SEVERITY_INFORMATION , "rxXfer_IssueTxn: Counter-Txn: HwAddr=0x%x, Len0=%d, Data0=%d, DrvCount=%d, TxnParams=0x%x\n", pTxn->uHwAddr, pTxn->aLen[0], *(TI_UINT32 *)(pTxn->aBuf[0]), pRxXfer->uDrvRxCntr, pTxn->uTxnParams); |
| |
| /* Return the status of the packet(s) transaction - COMPLETE, PENDING or ERROR */ |
| return eStatus; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_SetRxDirectAccessParams() |
| **************************************************************************** |
| * DESCRIPTION: |
| * |
| * INPUTS: |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| void rxXfer_SetRxDirectAccessParams (TI_HANDLE hRxXfer, TDmaParams *pDmaParams) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| pRxXfer->uPacketMemoryPoolStart = pDmaParams->PacketMemoryPoolStart; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_TxnDoneCb() |
| **************************************************************************** |
| * DESCRIPTION: Forward the packet to the registered CB |
| * |
| * INPUTS: |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| static void rxXfer_TxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| CL_TRACE_START_L2(); |
| |
| /* Increase the number of available txn structures */ |
| pRxXfer->uAvailableTxn++; |
| |
| /* Forward received packet to the upper layers */ |
| rxXfer_ForwardPacket (pRxXfer, pTxn); |
| |
| /* If we exited the handler upon pending-buffer, call it again to handle further packets if any */ |
| if (pRxXfer->bPendingBuffer) |
| { |
| pRxXfer->bPendingBuffer = TI_FALSE; |
| rxXfer_Handle (hRxXfer); |
| } |
| |
| CL_TRACE_END_L2("tiwlan_drv.ko", "INHERIT", "RX", ""); |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_PktDropTxnDoneCb() |
| **************************************************************************** |
| * DESCRIPTION: Dummy CB for case of dropping a packet due to out-of-memory. |
| * |
| * INPUTS: |
| * |
| * OUTPUT: |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| static void rxXfer_PktDropTxnDoneCb (TI_HANDLE hRxXfer, TTxnStruct *pTxn) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| /* Increase the number of available txn structures */ |
| pRxXfer->uAvailableTxn++; |
| |
| /* Restore the regular TxnDone callback to the used structure */ |
| pTxn->fTxnDoneCb = (TTxnDoneCb)rxXfer_TxnDoneCb; |
| pTxn->hCbHandle = hRxXfer; |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_Restart() |
| **************************************************************************** |
| * DESCRIPTION: rxXfer_Restart the RxXfer module object (called by the recovery) |
| * |
| * INPUTS: hRxXfer - The object to free |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: NONE |
| ****************************************************************************/ |
| void rxXfer_Restart(TI_HANDLE hRxXfer) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| TTxnStruct* pTxn; |
| TI_UINT8 i; |
| |
| pRxXfer->uFwRxCntr = 0; |
| pRxXfer->uDrvRxCntr = 0; |
| pRxXfer->uCurrTxnIndex = 0; |
| pRxXfer->uAvailableTxn = MAX_CONSECUTIVE_READ_TXN - 1; |
| |
| /* Scan all transaction array and release only pending transaction */ |
| for (i = 0; i < MAX_CONSECUTIVE_READ_TXN; i++) |
| { |
| pTxn = &(pRxXfer->aTxnStruct[i]); |
| |
| /* Check if buffer allocated and not the dummy one (has a different callback) */ |
| if ((pTxn->aBuf[0] != 0) && (pTxn->fTxnDoneCb == (TTxnDoneCb)rxXfer_TxnDoneCb)) |
| { |
| TI_UINT32 uBufNum; |
| RxIfDescriptor_t *pRxParams; |
| |
| /* Go over the Txn occupied buffers and mark them as TAG_CLASS_UNKNOWN to be freed */ |
| for (uBufNum = 0; uBufNum < MAX_XFER_BUFS; uBufNum++) |
| { |
| /* If no more buffers, exit the loop */ |
| if (pTxn->aLen[uBufNum] == 0) |
| { |
| break; |
| } |
| |
| pRxParams = (RxIfDescriptor_t *)(pTxn->aBuf[uBufNum]); |
| pRxParams->packet_class_tag = TAG_CLASS_UNKNOWN; |
| } |
| |
| /* Call upper layer only to release the allocated buffer */ |
| rxXfer_ForwardPacket (pRxXfer, pTxn); |
| } |
| } |
| |
| /* Fill the transaction structures fields that have constant values */ |
| for (i = 0; i < MAX_CONSECUTIVE_READ_TXN; i++) |
| { |
| /* First mem-block address (two consecutive registers) */ |
| pTxn = &(pRxXfer->aSlaveRegTxn[i].tTxnStruct); |
| TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR) |
| BUILD_TTxnStruct(pTxn, SLV_REG_DATA, &pRxXfer->aSlaveRegTxn[i].uRegData, REGISTER_SIZE*2, NULL, NULL) |
| |
| /* The packet(s) read transaction */ |
| pTxn = &(pRxXfer->aTxnStruct[i]); |
| TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_READ, TXN_FIXED_ADDR) |
| pTxn->fTxnDoneCb = (TTxnDoneCb)rxXfer_TxnDoneCb; |
| pTxn->hCbHandle = hRxXfer; |
| |
| /* The driver packets counter */ |
| pTxn = &(pRxXfer->aCounterTxn[i].tTxnStruct); |
| TXN_PARAM_SET(pTxn, TXN_LOW_PRIORITY, TXN_FUNC_ID_WLAN, TXN_DIRECTION_WRITE, TXN_INC_ADDR) |
| BUILD_TTxnStruct(pTxn, RX_DRIVER_COUNTER_ADDRESS, &pRxXfer->aCounterTxn[i].uCounter, REGISTER_SIZE, NULL, NULL) |
| } |
| |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_RegisterErrCb() |
| **************************************************************************** |
| * DESCRIPTION: Register Error CB |
| * |
| * INPUTS: |
| * hRxXfer - The object |
| * ErrCb - The upper layer CB function for error handling |
| * hErrCb - The CB function handle |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: void |
| ****************************************************************************/ |
| void rxXfer_RegisterErrCb (TI_HANDLE hRxXfer, void *fErrCb, TI_HANDLE hErrCb) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| /* Save upper layer (health monitor) CB for recovery from fatal error */ |
| pRxXfer->fErrCb = (TFailureEventCb)fErrCb; |
| pRxXfer->hErrCb = hErrCb; |
| } |
| |
| |
| #ifdef TI_DBG |
| /**************************************************************************** |
| * rxXfer_ClearStats() |
| **************************************************************************** |
| * DESCRIPTION: |
| * |
| * INPUTS: |
| * pRxXfer The object |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: TI_OK. |
| ****************************************************************************/ |
| void rxXfer_ClearStats (TI_HANDLE hRxXfer) |
| { |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| os_memoryZero (pRxXfer->hOs, &pRxXfer->tDbgStat, sizeof(TRxXferDbgStat)); |
| } |
| |
| |
| /**************************************************************************** |
| * rxXfer_PrintStats() |
| **************************************************************************** |
| * DESCRIPTION: . |
| * |
| * INPUTS: |
| * pRxXfer The object |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: TI_OK. |
| ****************************************************************************/ |
| void rxXfer_PrintStats (TI_HANDLE hRxXfer) |
| { |
| #ifdef REPORT_LOG |
| TRxXfer *pRxXfer = (TRxXfer *)hRxXfer; |
| |
| WLAN_OS_REPORT(("Print RX Xfer module info\n")); |
| WLAN_OS_REPORT(("=========================\n")); |
| WLAN_OS_REPORT(("uMaxAggregPkts = %d\n", pRxXfer->uMaxAggregPkts)); |
| WLAN_OS_REPORT(("uMaxAggregLen = %d\n", pRxXfer->uMaxAggregLen)); |
| WLAN_OS_REPORT(("FW counter = %d\n", pRxXfer->uFwRxCntr)); |
| WLAN_OS_REPORT(("Drv counter = %d\n", pRxXfer->uDrvRxCntr)); |
| WLAN_OS_REPORT(("AvailableTxn = %d\n", pRxXfer->uAvailableTxn)); |
| WLAN_OS_REPORT(("uCountFwEvents = %d\n", pRxXfer->tDbgStat.uCountFwEvents)); |
| WLAN_OS_REPORT(("uCountPktsForward = %d\n", pRxXfer->tDbgStat.uCountPktsForward)); |
| WLAN_OS_REPORT(("uCountBufPend = %d\n", pRxXfer->tDbgStat.uCountBufPend)); |
| WLAN_OS_REPORT(("uCountBufNoMem = %d\n", pRxXfer->tDbgStat.uCountBufNoMem)); |
| WLAN_OS_REPORT(("uCountPktAggreg-1 = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[0])); |
| WLAN_OS_REPORT(("uCountPktAggreg-2 = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[1])); |
| WLAN_OS_REPORT(("uCountPktAggreg-3 = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[2])); |
| WLAN_OS_REPORT(("uCountPktAggreg-4 = %d\n", pRxXfer->tDbgStat.uCountPktAggreg[3])); |
| #endif |
| } |
| #endif |