TWD/Data_Service/txHwQueue.c
changeset 0 10c42ec6c05f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TWD/Data_Service/txHwQueue.c	Tue Jun 29 12:34:26 2010 +0100
@@ -0,0 +1,716 @@
+/*
+ * txHwQueue.c
+ *
+ * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved.      
+ * All rights reserved.      
+ * 
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 or BSD License which accompanies
+ * this distribution. The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html and the BSD License is as below.                                   
+ *                                                                       
+ * 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             3
+
+/* 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),MemoryNormal);
+    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 >> 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 & 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.
+ ****************************************************************************/
+void 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);
+    }
+}
+
+
+/****************************************************************************
+ *                  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 */
+