| /* |
| * txHwQueue.c |
| * |
| * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name Texas Instruments nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| /**************************************************************************** |
| * |
| * MODULE: txHwQueue.c |
| * |
| * PURPOSE: manage the wlan hardware Tx memory blocks allocation per queue. |
| * |
| * DESCRIPTION: |
| * ============ |
| * This module is responsible for the HW Tx data-blocks and descriptors allocation. |
| * The HW Tx blocks are allocated in the driver by rough calculations without |
| * accessing the FW. |
| * They are freed according to FW counters that are provided by the FwEvent module |
| * on every FW interrupt. |
| ****************************************************************************/ |
| #define __FILE_ID__ FILE_ID_100 |
| #include "osApi.h" |
| #include "report.h" |
| #include "TWDriver.h" |
| #include "txCtrlBlk_api.h" |
| #include "txHwQueue_api.h" |
| |
| |
| /* Translate input TID to AC */ |
| /* Note: This structure is shared with other modules */ |
| const EAcTrfcType WMEQosTagToACTable[MAX_NUM_OF_802_1d_TAGS] = |
| {QOS_AC_BE, QOS_AC_BK, QOS_AC_BK, QOS_AC_BE, QOS_AC_VI, QOS_AC_VI, QOS_AC_VO, QOS_AC_VO}; |
| |
| /* |
| * Local definitions: |
| */ |
| |
| /* Spare blocks written in extraMemBlks field in TxDescriptor for HW use */ |
| #define BLKS_HW_ALLOC_SPARE 2 |
| |
| /* Set queue's backpressure bit (indicates queue state changed from ready to busy or inversely). */ |
| #define SET_QUEUE_BACKPRESSURE(pBackpressure, uQueueId) (*pBackpressure |= (1 << uQueueId)) |
| |
| /* Callback function definition for UpdateBusyMap */ |
| typedef void (* tUpdateBusyMapCb)(TI_HANDLE hCbHndl, TI_UINT32 uBackpressure); |
| |
| /* Per Queue HW blocks accounting data: */ |
| typedef struct |
| { |
| TI_UINT32 uNumBlksThresh; /* Minimum HW blocks that must be reserved for this Queue. */ |
| TI_UINT32 uNumBlksUsed; /* Number of HW blocks that are currently allocated for this Queue. */ |
| TI_UINT32 uNumBlksReserved; /* Number of HW blocks currently reserved for this Queue (to guarentee the low threshold). */ |
| TI_UINT32 uAllocatedBlksCntr; /* Accumulates allocated blocks for FW freed-blocks counter coordination. */ |
| TI_UINT32 uFwFreedBlksCntr; /* Accumulated freed blocks in FW. */ |
| TI_UINT32 uNumBlksCausedBusy; /* Number of HW blocks that caused queue busy state. */ |
| TI_BOOL bQueueBusy; /* If TI_TRUE, this queue is currently stopped. */ |
| TI_UINT16 uPercentOfBlkLowThresh; /* Configured percentage of blocks to use as the queue's low allocation threshold */ |
| TI_UINT16 uPercentOfBlkHighThresh; /* Configured percentage of blocks to use as the queue's high allocation threshold */ |
| |
| } TTxHwQueueInfo; |
| |
| typedef struct |
| { |
| TI_HANDLE hOs; |
| TI_HANDLE hReport; |
| |
| tUpdateBusyMapCb fUpdateBusyMapCb; /* The upper layers UpdateBusyMap callback */ |
| TI_HANDLE hUpdateBusyMapHndl;/* The handle for the fUpdateBusyMapCb */ |
| |
| TI_UINT32 uNumTotalBlks; /* The total number of Tx blocks */ |
| TI_UINT32 uNumTotalBlksFree; /* Total number of free HW blocks */ |
| TI_UINT32 uNumTotalBlksReserved; /* Total number of free but reserved HW blocks */ |
| TI_UINT32 uNumUsedDescriptors; /* Total number of packets in the FW. */ |
| TI_UINT8 uFwTxResultsCntr; /* Accumulated freed descriptors in FW. */ |
| TI_UINT8 uDrvTxPacketsCntr; /* Accumulated allocated descriptors in driver. */ |
| |
| TTxHwQueueInfo aTxHwQueueInfo[MAX_NUM_OF_AC]; /* The per queue variables */ |
| |
| } TTxHwQueue; |
| |
| |
| static void txHwQueue_UpdateFreeBlocks (TTxHwQueue *pTxHwQueue, TI_UINT32 uQueueId, TI_UINT32 uFreeBlocks); |
| static TI_UINT32 txHwQueue_CheckResources (TTxHwQueue *pTxHwQueue, TTxHwQueueInfo *pQueueInfo); |
| |
| |
| |
| /**************************************************************************** |
| * txHwQueue_Create() |
| **************************************************************************** |
| * DESCRIPTION: Create the Tx buffers pool object |
| * |
| * INPUTS: None |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: The Created object |
| ****************************************************************************/ |
| TI_HANDLE txHwQueue_Create (TI_HANDLE hOs) |
| { |
| TTxHwQueue *pTxHwQueue; |
| |
| pTxHwQueue = os_memoryAlloc(hOs, sizeof(TTxHwQueue)); |
| if (pTxHwQueue == NULL) |
| { |
| return NULL; |
| } |
| |
| os_memoryZero(hOs, pTxHwQueue, sizeof(TTxHwQueue)); |
| |
| pTxHwQueue->hOs = hOs; |
| |
| return (TI_HANDLE)pTxHwQueue; |
| } |
| |
| /**************************************************************************** |
| * txHwQueue_Destroy() |
| **************************************************************************** |
| * DESCRIPTION: Destroy the Tx buffers pool object |
| * |
| * INPUTS: hTxHwQueue - The object to free |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: TI_OK or TI_NOK |
| ****************************************************************************/ |
| TI_STATUS txHwQueue_Destroy (TI_HANDLE hTxHwQueue) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| |
| if (pTxHwQueue) |
| { |
| os_memoryFree(pTxHwQueue->hOs, pTxHwQueue, sizeof(TTxHwQueue)); |
| } |
| return TI_OK; |
| } |
| |
| |
| |
| |
| /**************************************************************************** |
| * txHwQueue_Init() |
| **************************************************************************** |
| |
| DESCRIPTION: Initialize module handles. |
| |
| ****************************************************************************/ |
| TI_STATUS txHwQueue_Init (TI_HANDLE hTxHwQueue, TI_HANDLE hReport) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| |
| pTxHwQueue->hReport = hReport; |
| |
| return TI_OK; |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_Config() |
| **************************************************************************** |
| * DESCRIPTION: Configure the Tx buffers pool object |
| * |
| * INPUTS: None |
| * |
| * OUTPUT: None |
| * |
| * RETURNS: |
| ****************************************************************************/ |
| TI_STATUS txHwQueue_Config (TI_HANDLE hTxHwQueue, TTwdInitParams *pInitParams) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| TI_UINT32 TxQid; |
| |
| /* Configure queue parameters to Tx-HW queue module */ |
| for (TxQid = 0; TxQid < MAX_NUM_OF_AC; TxQid++) |
| { |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uNumBlksThresh = pInitParams->tGeneral.TxBlocksThresholdPerAc[TxQid]; |
| } |
| |
| return TI_OK; |
| } |
| |
| |
| |
| /**************************************************************************** |
| * txHwQueue_SetHwInfo() |
| **************************************************************************** |
| |
| DESCRIPTION: |
| |
| Called after the HW configuration in the driver init or recovery process. |
| Configure Tx HW information, including Tx-HW-blocks number, and per queue |
| Tx-descriptors number. Than, restart the module variables. |
| |
| Two thresholds are defined per queue: |
| a) TxBlocksLowPercentPerQueue[queue] - The lower threshold is the minimal number of |
| Tx blocks guaranteed for each queue. |
| The sum of all low thresholds should be less than 100%. |
| b) TxBlocksHighPercentPerQueue[queue] - The higher threshold is the maximal number of |
| Tx blocks that may be allocated to the queue. |
| The extra blocks above the low threshold can be allocated when needed only |
| if they are currently available and are not needed in order to guarantee |
| the other queues low threshold. |
| The sum of all high thresholds should be more than 100%. |
| ****************************************************************************/ |
| TI_STATUS txHwQueue_SetHwInfo (TI_HANDLE hTxHwQueue, TDmaParams *pDmaParams) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| |
| pTxHwQueue->uNumTotalBlks = pDmaParams->NumTxBlocks - 1; /* One block must be always free for FW use. */ |
| |
| /* Restart the module variables. */ |
| txHwQueue_Restart (hTxHwQueue); |
| |
| return TI_OK; |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_Restart() |
| **************************************************************************** |
| DESCRIPTION: |
| ============ |
| Called after the HW configuration in the driver init or recovery process. |
| Restarts the Tx-HW-Queue module. |
| ****************************************************************************/ |
| TI_STATUS txHwQueue_Restart (TI_HANDLE hTxHwQueue) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| TTxHwQueueInfo *pQueueInfo; |
| TI_UINT32 TxQid; |
| |
| |
| /* |
| * All blocks are free at restart. |
| * Note that free means all blocks that are currently not in use, while reserved are |
| * a part of the free blocks that are the summary of all queues reserved blocks. |
| * Each queue may take from the reserved part only up to its own reservation (according to |
| * its low threshold). |
| */ |
| pTxHwQueue->uNumTotalBlksFree = pTxHwQueue->uNumTotalBlks; |
| pTxHwQueue->uNumTotalBlksReserved = 0; |
| pTxHwQueue->uNumUsedDescriptors = 0; |
| pTxHwQueue->uFwTxResultsCntr = 0; |
| pTxHwQueue->uDrvTxPacketsCntr = 0; |
| |
| for (TxQid = 0; TxQid < MAX_NUM_OF_AC; TxQid++) |
| { |
| pQueueInfo = &pTxHwQueue->aTxHwQueueInfo[TxQid]; |
| |
| pQueueInfo->uNumBlksUsed = 0; |
| pQueueInfo->uAllocatedBlksCntr = 0; |
| pQueueInfo->uFwFreedBlksCntr = 0; |
| pQueueInfo->uNumBlksCausedBusy = 0; |
| pQueueInfo->bQueueBusy = TI_FALSE; |
| |
| /* Since no blocks are used yet, reserved blocks number equals to the low threshold. */ |
| pQueueInfo->uNumBlksReserved = pQueueInfo->uNumBlksThresh; |
| |
| /* Accumulate total reserved blocks. */ |
| pTxHwQueue->uNumTotalBlksReserved += pQueueInfo->uNumBlksReserved; |
| } |
| |
| return TI_OK; |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_AllocResources() |
| **************************************************************************** |
| * DESCRIPTION: |
| ============ |
| 1. Estimate required HW-blocks number. |
| 2. If the required blocks are not available or no free descriptor, |
| return STOP_CURRENT (to stop current queue and requeue the packet). |
| 3. Resources are available so update allocated blocks and descriptors counters. |
| 4. If no resources for another similar packet, return STOP_NEXT (to stop current queue). |
| Else, return SUCCESS |
| ****************************************************************************/ |
| ETxHwQueStatus txHwQueue_AllocResources (TI_HANDLE hTxHwQueue, TTxCtrlBlk *pTxCtrlBlk) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| TI_UINT32 uNumBlksToAlloc; /* The number of blocks required for the current packet. */ |
| TI_UINT32 uExcludedLength; /* The data length not included in the rough blocks calculation */ |
| TI_UINT32 uAvailableBlks; /* Max blocks that are currently available for this queue. */ |
| TI_UINT32 uReservedBlks; /* How many blocks are reserved for this queue before this allocation. */ |
| TI_UINT32 uQueueId = WMEQosTagToACTable[pTxCtrlBlk->tTxDescriptor.tid]; |
| TTxHwQueueInfo *pQueueInfo = &(pTxHwQueue->aTxHwQueueInfo[uQueueId]); |
| |
| |
| /***********************************************************************/ |
| /* Calculate packet required HW blocks. */ |
| /***********************************************************************/ |
| |
| /* Divide length by 256 instead of 252 (block size) to save CPU */ |
| uNumBlksToAlloc = ( pTxCtrlBlk->tTxDescriptor.length + 20 ) >> 8; |
| |
| /* The length not yet included in the uNumBlksToAlloc is the sum of: |
| 1) 4 bytes per block as a result of using 256 instead of 252 block size. |
| 2) The remainder of the division by 256. |
| 3) Overhead due to header translation, security and LLC header (subtracting ethernet header). |
| */ |
| uExcludedLength = (uNumBlksToAlloc << 2) + ((pTxCtrlBlk->tTxDescriptor.length + 20) & 0xFF) + MAX_HEADER_SIZE - 14; |
| |
| /* Add 1 or 2 blocks for the excluded length, according to its size */ |
| uNumBlksToAlloc += (uExcludedLength > 252) ? 2 : 1; |
| |
| /* Add extra blocks needed in case of fragmentation */ |
| uNumBlksToAlloc += BLKS_HW_ALLOC_SPARE; |
| |
| /***********************************************************************/ |
| /* Check if the required resources are available */ |
| /***********************************************************************/ |
| |
| /* Find max available blocks for this queue (0 could indicate no descriptors). */ |
| uAvailableBlks = txHwQueue_CheckResources (pTxHwQueue, pQueueInfo); |
| |
| /* If we need more blocks than available, return STOP_CURRENT (stop current queue and requeue packet). */ |
| if (uNumBlksToAlloc > uAvailableBlks) |
| { |
| TRACE6(pTxHwQueue->hReport, REPORT_SEVERITY_INFORMATION, ": No resources, Queue=%d, ReqBlks=%d, FreeBlks=%d, UsedBlks=%d, AvailBlks=%d, UsedPkts=%d\n", uQueueId, uNumBlksToAlloc, pTxHwQueue->uNumTotalBlksFree, pQueueInfo->uNumBlksUsed, uAvailableBlks, pTxHwQueue->uNumUsedDescriptors); |
| pQueueInfo->uNumBlksCausedBusy = uNumBlksToAlloc; |
| pQueueInfo->bQueueBusy = TI_TRUE; |
| |
| return TX_HW_QUE_STATUS_STOP_CURRENT; /**** Exit! (we should stop queue and requeue packet) ****/ |
| } |
| |
| /***********************************************************************/ |
| /* Allocate required resources */ |
| /***********************************************************************/ |
| |
| /* Update blocks numbers in Tx descriptor */ |
| pTxCtrlBlk->tTxDescriptor.extraMemBlks = BLKS_HW_ALLOC_SPARE; |
| pTxCtrlBlk->tTxDescriptor.totalMemBlks = uNumBlksToAlloc; |
| |
| /* Update packet allocation info: */ |
| pTxHwQueue->uNumUsedDescriptors++; /* Update number of packets in FW (for descriptors allocation check). */ |
| pTxHwQueue->uDrvTxPacketsCntr++; |
| pQueueInfo->uAllocatedBlksCntr += uNumBlksToAlloc; /* For FW counter coordination. */ |
| uReservedBlks = pQueueInfo->uNumBlksReserved; |
| |
| /* If we are currently using less than the low threshold (i.e. we have some reserved blocks), |
| blocks allocation should reduce the reserved blocks number as follows: |
| */ |
| if (uReservedBlks) |
| { |
| |
| /* If adding the allocated blocks to the used blocks will pass the low-threshold, |
| only the part up to the low-threshold is subtracted from the reserved blocks. |
| This is because blocks are reserved for the Queue only up to its low-threshold. |
| |
| 0 old used low new used high |
| |######| | | | |
| |######| | | | |
| <------------ allocated -----------> |
| <----- old reserved ----> |
| new reserved = 0 (we passed the low threshold) |
| */ |
| if (uNumBlksToAlloc > uReservedBlks) |
| { |
| pQueueInfo->uNumBlksReserved = 0; |
| pTxHwQueue->uNumTotalBlksReserved -= uReservedBlks; /* reduce change from total reserved.*/ |
| } |
| |
| |
| /* Else, if allocating less than reserved, |
| the allocated blocks are subtracted from the reserved blocks: |
| |
| 0 old used new used low high |
| |######| | | | |
| |######| | | | |
| <- allocated -> |
| <--------- old reserved ----------> |
| <-- new reserved --> |
| */ |
| else |
| { |
| pQueueInfo->uNumBlksReserved -= uNumBlksToAlloc; |
| pTxHwQueue->uNumTotalBlksReserved -= uNumBlksToAlloc; /* reduce change from total reserved.*/ |
| } |
| } |
| |
| |
| /* Update total free blocks and Queue used blocks with the allocated blocks number. */ |
| pTxHwQueue->uNumTotalBlksFree -= uNumBlksToAlloc; |
| pQueueInfo->uNumBlksUsed += uNumBlksToAlloc; |
| |
| TRACE6(pTxHwQueue->hReport, REPORT_SEVERITY_INFORMATION, ": SUCCESS, Queue=%d, Req-blks=%d , Free=%d, Used=%d, Reserved=%d, Accumulated=%d\n", uQueueId, uNumBlksToAlloc, pTxHwQueue->uNumTotalBlksFree, pQueueInfo->uNumBlksUsed, pQueueInfo->uNumBlksReserved, pQueueInfo->uAllocatedBlksCntr); |
| |
| /* If no resources for another similar packet, return STOP_NEXT (to stop current queue). */ |
| /* Note: Current packet transmission is continued */ |
| if ( (uNumBlksToAlloc << 1) > uAvailableBlks ) |
| { |
| TRACE6(pTxHwQueue->hReport, REPORT_SEVERITY_INFORMATION, ": No resources for next pkt, Queue=%d, ReqBlks=%d, FreeBlks=%d, UsedBlks=%d, AvailBlks=%d, UsedPkts=%d\n", uQueueId, uNumBlksToAlloc, pTxHwQueue->uNumTotalBlksFree, pQueueInfo->uNumBlksUsed, uAvailableBlks, pTxHwQueue->uNumUsedDescriptors); |
| pQueueInfo->uNumBlksCausedBusy = uNumBlksToAlloc; |
| pQueueInfo->bQueueBusy = TI_TRUE; |
| return TX_HW_QUE_STATUS_STOP_NEXT; |
| } |
| |
| /* Return SUCCESS (resources are available). */ |
| return TX_HW_QUE_STATUS_SUCCESS; |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_UpdateFreeBlocks() |
| **************************************************************************** |
| * DESCRIPTION: |
| =========== |
| This function is called per queue after reading the freed blocks counters from the FwStatus. |
| It updates the queue's blocks status according to the freed blocks. |
| ****************************************************************************/ |
| static void txHwQueue_UpdateFreeBlocks (TTxHwQueue *pTxHwQueue, TI_UINT32 uQueueId, TI_UINT32 uFreeBlocks) |
| { |
| TTxHwQueueInfo *pQueueInfo = &(pTxHwQueue->aTxHwQueueInfo[uQueueId]); |
| TI_UINT32 lowThreshold; /* Minimum blocks that are guaranteed for this Queue. */ |
| TI_UINT32 newUsedBlks; /* Blocks that are used by this Queue after updating free blocks. */ |
| TI_UINT32 newReserved; /* How many blocks are reserved to this Queue after freeing. */ |
| TI_UINT32 numBlksToFree; /* The number of blocks freed in the current queue. */ |
| |
| /* If the FW free blocks counter didn't change, exit */ |
| uFreeBlocks = ENDIAN_HANDLE_LONG(uFreeBlocks); |
| if (uFreeBlocks == pQueueInfo->uFwFreedBlksCntr) |
| { |
| return; |
| } |
| |
| pQueueInfo->uFwFreedBlksCntr = uFreeBlocks; |
| |
| /* The uFreeBlocks is the accumulated number of blocks freed by the FW for the uQueueId. |
| * Subtracting it from the accumulated number of blocks allocated by the driver should |
| * give the current number of used blocks in this queue. |
| * Since the difference is always a small positive number, a simple subtraction should work |
| * also for wrap around. |
| */ |
| newUsedBlks = pQueueInfo->uAllocatedBlksCntr - uFreeBlocks; |
| |
| numBlksToFree = pQueueInfo->uNumBlksUsed - newUsedBlks; |
| |
| #ifdef TI_DBG /* Sanity check: make sure we don't free more than is allocated. */ |
| if (numBlksToFree > pQueueInfo->uNumBlksUsed) |
| { |
| TRACE5(pTxHwQueue->hReport, REPORT_SEVERITY_ERROR, ": Try to free more blks than used: Queue %d, ToFree %d, Used %d, HostAlloc=0x%x, FwFree=0x%x\n", uQueueId, numBlksToFree, pQueueInfo->uNumBlksUsed, pQueueInfo->uAllocatedBlksCntr, uFreeBlocks); |
| } |
| #endif |
| |
| /* Update total free blocks and Queue used blocks with the freed blocks number. */ |
| pTxHwQueue->uNumTotalBlksFree += numBlksToFree; |
| pQueueInfo->uNumBlksUsed = newUsedBlks; |
| |
| lowThreshold = pQueueInfo->uNumBlksThresh; |
| |
| /* If after freeing the blocks we are using less than the low threshold, |
| update total reserved blocks number as follows: |
| (note: if we are above the low threshold after freeing the blocks we still have no reservation.) |
| */ |
| if (newUsedBlks < lowThreshold) |
| { |
| newReserved = lowThreshold - newUsedBlks; |
| pQueueInfo->uNumBlksReserved = newReserved; |
| |
| |
| /* If freeing the blocks reduces the used blocks from above to below the low-threshold, |
| only the part from the low-threshold to the new used number is added to the |
| reserved blocks (because blocks are reserved for the Queue only up to its low-threshold): |
| |
| 0 new used low old used high |
| |###########|####################|################| | |
| |###########|####################|################| | |
| <-------------- freed --------------> |
| <-- new reserved --> |
| old reserved = 0 |
| */ |
| if (numBlksToFree > newReserved) |
| pTxHwQueue->uNumTotalBlksReserved += newReserved; /* Add change to total reserved.*/ |
| |
| |
| /* Else, if we were under the low-threshold before freeing these blocks, |
| all freed blocks are added to the reserved blocks: |
| |
| 0 new used old used low high |
| |################|#################| | | |
| |################|#################| | | |
| <---- freed ----> |
| <- old reserved -> |
| <---------- new reserved ----------> |
| */ |
| else |
| pTxHwQueue->uNumTotalBlksReserved += numBlksToFree; /* Add change to total reserved.*/ |
| } |
| |
| TRACE5(pTxHwQueue->hReport, REPORT_SEVERITY_INFORMATION, ": Queue %d, ToFree %d, Used %d, HostAlloc=0x%x, FwFree=0x%x\n", uQueueId, numBlksToFree, pQueueInfo->uNumBlksUsed, pQueueInfo->uAllocatedBlksCntr, uFreeBlocks); |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_UpdateFreeResources() |
| **************************************************************************** |
| * DESCRIPTION: |
| =========== |
| Called by FwEvent upon Data interrupt to update freed HW-Queue resources as follows: |
| 1) For all queues, update blocks and descriptors numbers according to FwStatus information. |
| 2) For each busy queue, if now available indicate it in the backpressure bitmap. |
| ****************************************************************************/ |
| ETxnStatus txHwQueue_UpdateFreeResources (TI_HANDLE hTxHwQueue, FwStatus_t *pFwStatus) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| TTxHwQueueInfo *pQueueInfo; |
| TI_UINT32 uQueueId; |
| TI_UINT32 uAvailableBlks; /* Max blocks available for current queue. */ |
| TI_UINT32 uNewNumUsedDescriptors; |
| TI_UINT32 uBackpressure = 0; |
| TI_UINT32 *pFreeBlocks = (TI_UINT32 *)pFwStatus->txReleasedBlks; |
| TI_UINT32 uTempFwCounters; |
| FwStatCntrs_t *pFwStatusCounters; |
| |
| /* |
| * If TxResults counter changed in FwStatus, update descriptors number according to information |
| */ |
| uTempFwCounters = (ENDIAN_HANDLE_LONG(pFwStatus->counters)); |
| pFwStatusCounters = (FwStatCntrs_t *)&uTempFwCounters; |
| if (pFwStatusCounters->txResultsCntr != pTxHwQueue->uFwTxResultsCntr) |
| { |
| pTxHwQueue->uFwTxResultsCntr = pFwStatusCounters->txResultsCntr; |
| |
| /* Calculate new number of used descriptors (the else is for wrap around case) */ |
| if (pTxHwQueue->uFwTxResultsCntr <= pTxHwQueue->uDrvTxPacketsCntr) |
| { |
| uNewNumUsedDescriptors = (TI_UINT32)(pTxHwQueue->uDrvTxPacketsCntr - pTxHwQueue->uFwTxResultsCntr); |
| } |
| else |
| { |
| uNewNumUsedDescriptors = 0x100 - (TI_UINT32)(pTxHwQueue->uFwTxResultsCntr - pTxHwQueue->uDrvTxPacketsCntr); |
| } |
| |
| #ifdef TI_DBG /* Sanity check: make sure we don't free more descriptors than allocated. */ |
| if (uNewNumUsedDescriptors >= pTxHwQueue->uNumUsedDescriptors) |
| { |
| TRACE2(pTxHwQueue->hReport, REPORT_SEVERITY_ERROR, ": Used descriptors number should decrease: UsedDesc %d, NewUsedDesc %d\n", pTxHwQueue->uNumUsedDescriptors, uNewNumUsedDescriptors); |
| } |
| #endif |
| |
| /* Update number of packets left in FW (for descriptors allocation check). */ |
| pTxHwQueue->uNumUsedDescriptors = uNewNumUsedDescriptors; |
| } |
| |
| /* |
| * For all queues, update blocks numbers according to FwStatus information |
| */ |
| for (uQueueId = 0; uQueueId < MAX_NUM_OF_AC; uQueueId++) |
| { |
| pQueueInfo = &(pTxHwQueue->aTxHwQueueInfo[uQueueId]); |
| |
| /* Update per queue number of used, free and reserved blocks. */ |
| txHwQueue_UpdateFreeBlocks (pTxHwQueue, uQueueId, pFreeBlocks[uQueueId]); |
| } |
| |
| /* |
| * For each busy queue, if now available indicate it in the backpressure bitmap |
| */ |
| for (uQueueId = 0; uQueueId < MAX_NUM_OF_AC; uQueueId++) |
| { |
| pQueueInfo = &(pTxHwQueue->aTxHwQueueInfo[uQueueId]); |
| |
| /* If the queue was stopped */ |
| if (pQueueInfo->bQueueBusy) |
| { |
| /* Find max available blocks for this queue (0 could indicate no descriptors). */ |
| uAvailableBlks = txHwQueue_CheckResources (pTxHwQueue, pQueueInfo); |
| |
| /* If the required blocks and a descriptor are available, |
| set the queue's backpressure bit to indicate NOT-busy! */ |
| if (pQueueInfo->uNumBlksCausedBusy <= uAvailableBlks) |
| { |
| TRACE6(pTxHwQueue->hReport, REPORT_SEVERITY_INFORMATION, ": Queue Available, Queue=%d, ReqBlks=%d, FreeBlks=%d, UsedBlks=%d, AvailBlks=%d, UsedPkts=%d\n", uQueueId, pQueueInfo->uNumBlksCausedBusy, pTxHwQueue->uNumTotalBlksFree, pQueueInfo->uNumBlksUsed, uAvailableBlks, pTxHwQueue->uNumUsedDescriptors); |
| SET_QUEUE_BACKPRESSURE(&uBackpressure, uQueueId); /* Start queue. */ |
| pQueueInfo->bQueueBusy = TI_FALSE; |
| } |
| } |
| } |
| |
| /* If released queues map is not 0, send it to the upper layers (if CB available) */ |
| if ((uBackpressure > 0) && (pTxHwQueue->fUpdateBusyMapCb != NULL)) |
| { |
| pTxHwQueue->fUpdateBusyMapCb (pTxHwQueue->hUpdateBusyMapHndl, uBackpressure); |
| } |
| |
| return TXN_STATUS_COMPLETE; |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_CheckResources() |
| **************************************************************************** |
| * DESCRIPTION: |
| ============ |
| Return the given queue's available blocks. |
| If no descriptors available, return 0. |
| ****************************************************************************/ |
| static TI_UINT32 txHwQueue_CheckResources (TTxHwQueue *pTxHwQueue, TTxHwQueueInfo *pQueueInfo) |
| { |
| /* If descriptors are available: */ |
| if (pTxHwQueue->uNumUsedDescriptors < NUM_TX_DESCRIPTORS) |
| { |
| /* Calculate how many buffers are available for this Queue: the total free buffers minus the buffers |
| that are reserved for other Queues (all reserved minus this Queue's reserved). */ |
| return (pTxHwQueue->uNumTotalBlksFree - (pTxHwQueue->uNumTotalBlksReserved - pQueueInfo->uNumBlksReserved)); |
| } |
| |
| /* If no descriptors are available, return 0 (can't transmit anything). */ |
| else |
| { |
| return 0; |
| } |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_RegisterCb() |
| **************************************************************************** |
| * DESCRIPTION: Register the upper driver TxHwQueue callback functions. |
| ****************************************************************************/ |
| void txHwQueue_RegisterCb (TI_HANDLE hTxHwQueue, TI_UINT32 uCallBackId, void *fCbFunc, TI_HANDLE hCbHndl) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| |
| switch (uCallBackId) |
| { |
| case TWD_INT_UPDATE_BUSY_MAP: |
| pTxHwQueue->fUpdateBusyMapCb = (tUpdateBusyMapCb)fCbFunc; |
| pTxHwQueue->hUpdateBusyMapHndl = hCbHndl; |
| break; |
| |
| default: |
| TRACE1(pTxHwQueue->hReport, REPORT_SEVERITY_ERROR, " - Illegal parameter = %d\n", uCallBackId); |
| return; |
| } |
| } |
| |
| |
| /**************************************************************************** |
| * txHwQueue_PrintInfo() |
| **************************************************************************** |
| * DESCRIPTION: Print the Hw Queue module current information |
| ****************************************************************************/ |
| #ifdef TI_DBG |
| void txHwQueue_PrintInfo (TI_HANDLE hTxHwQueue) |
| { |
| TTxHwQueue *pTxHwQueue = (TTxHwQueue *)hTxHwQueue; |
| TI_INT32 TxQid; |
| |
| /* Print the Tx-HW-Queue information: */ |
| WLAN_OS_REPORT(("Hw-Queues Information:\n")); |
| WLAN_OS_REPORT(("======================\n")); |
| WLAN_OS_REPORT(("Total Blocks: %d\n", pTxHwQueue->uNumTotalBlks)); |
| WLAN_OS_REPORT(("Total Free Blocks: %d\n", pTxHwQueue->uNumTotalBlksFree)); |
| WLAN_OS_REPORT(("Total Reserved Blocks: %d\n", pTxHwQueue->uNumTotalBlksReserved)); |
| WLAN_OS_REPORT(("Total Used Descriptors: %d\n", pTxHwQueue->uNumUsedDescriptors)); |
| WLAN_OS_REPORT(("FwTxResultsCntr: %d\n", pTxHwQueue->uFwTxResultsCntr)); |
| WLAN_OS_REPORT(("DrvTxPacketsCntr: %d\n", pTxHwQueue->uDrvTxPacketsCntr)); |
| |
| for(TxQid = 0; TxQid < MAX_NUM_OF_AC; TxQid++) |
| { |
| WLAN_OS_REPORT(("Q=%d: Used=%d, Reserve=%d, Threshold=%d\n", |
| TxQid, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uNumBlksUsed, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uNumBlksReserved, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uNumBlksThresh)); |
| } |
| |
| WLAN_OS_REPORT(("\n")); |
| |
| for(TxQid = 0; TxQid < MAX_NUM_OF_AC; TxQid++) |
| { |
| WLAN_OS_REPORT(("Queue=%d: HostAllocCount=0x%x, FwFreeCount=0x%x, BusyBlks=%d, Busy=%d\n", |
| TxQid, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uAllocatedBlksCntr, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uFwFreedBlksCntr, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].uNumBlksCausedBusy, |
| pTxHwQueue->aTxHwQueueInfo[TxQid].bQueueBusy)); |
| } |
| } |
| |
| |
| #endif /* TI_DBG */ |
| |