/*
 * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */



#include "htc_debug.h"
#include "htc_internal.h"
#include <adf_nbuf.h> /* adf_nbuf_t */
#include <adf_os_mem.h> /* adf_os_mem_alloc */
#include <vos_getBin.h>
#include "epping_main.h"
#include "adf_trace.h"

#define HTC_DATA_RESOURCE_THRS 256
#define HTC_DATA_MINDESC_PERPACKET 2

typedef enum _HTC_SEND_QUEUE_RESULT {
    HTC_SEND_QUEUE_OK = 0,    /* packet was queued */
    HTC_SEND_QUEUE_DROP = 1,  /* this packet should be dropped */
} HTC_SEND_QUEUE_RESULT;

#ifndef DEBUG_CREDIT
#define DEBUG_CREDIT 0
#endif

#if DEBUG_CREDIT
/* bit mask to enable debug certain endpoint */
static unsigned ep_debug_mask = (1 << ENDPOINT_0) | (1 << ENDPOINT_1) | (1 << ENDPOINT_2);
#endif

/* HTC Control Path Credit History */
A_UINT32 g_htc_credit_history_idx = 0;
HTC_CREDIT_HISTORY htc_credit_history_buffer[HTC_CREDIT_HISTORY_MAX];

void htc_credit_record(htc_credit_exchange_type type, A_UINT32 tx_credit,
                       A_UINT32 htc_tx_queue_depth)
{
    if (HTC_CREDIT_HISTORY_MAX <= g_htc_credit_history_idx)
        g_htc_credit_history_idx = 0;

    htc_credit_history_buffer[g_htc_credit_history_idx].type = type;
    htc_credit_history_buffer[g_htc_credit_history_idx].time =
        adf_get_boottime();
    htc_credit_history_buffer[g_htc_credit_history_idx].tx_credit = tx_credit;
    htc_credit_history_buffer[g_htc_credit_history_idx].htc_tx_queue_depth =
        htc_tx_queue_depth;
    g_htc_credit_history_idx++;
}

void HTC_dump_counter_info(HTC_HANDLE HTCHandle)
{
    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);

    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("\n%s: CE_send_cnt = %d, TX_comp_cnt = %d\n",
                     __func__, target->CE_send_cnt, target->TX_comp_cnt));
}

void HTCGetControlEndpointTxHostCredits(HTC_HANDLE HTCHandle, int *credits)
{
    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT *pEndpoint;
    int i;

    if (!credits || !target) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: invalid args", __func__));
        return;
    }

    *credits = 0;
    LOCK_HTC_TX(target);
    for (i = 0; i < ENDPOINT_MAX; i++) {
        pEndpoint = &target->EndPoint[i];
        if (pEndpoint->ServiceID == WMI_CONTROL_SVC) {
            *credits = pEndpoint->TxCredits;
            break;
        }
    }
    UNLOCK_HTC_TX(target);
}

static INLINE void RestoreTxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
    adf_nbuf_t netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);

    if (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) {
        adf_nbuf_unmap(target->osdev, netbuf, ADF_OS_DMA_TO_DEVICE);
        pPacket->PktInfo.AsTx.Flags &= ~HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
        adf_nbuf_pull_head(netbuf, sizeof(HTC_FRAME_HDR));
    }
}

static void DoSendCompletion(HTC_ENDPOINT       *pEndpoint,
                             HTC_PACKET_QUEUE   *pQueueToIndicate)
{
    do {

        if (HTC_QUEUE_EMPTY(pQueueToIndicate)) {
                /* nothing to indicate */
            break;
        }

        if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) {
            AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d, send complete multiple callback (%d pkts) \n",
                     pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate)));
                /* a multiple send complete handler is being used, pass the queue to the handler */
            pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint->EpCallBacks.pContext,
                                                        pQueueToIndicate);
                /* all packets are now owned by the callback, reset queue to be safe */
            INIT_HTC_PACKET_QUEUE(pQueueToIndicate);
        } else {
            HTC_PACKET *pPacket;
            /* using legacy EpTxComplete */
            do {
                pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
                AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d send complete callback on packet %pK \n",
                        pEndpoint->Id, pPacket));
                pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);
            } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));
        }

    } while (FALSE);

}

static void SendPacketCompletion(HTC_TARGET *target, HTC_PACKET *pPacket)
{
    HTC_ENDPOINT    *pEndpoint = &target->EndPoint[pPacket->Endpoint];
    HTC_PACKET_QUEUE container;

    RestoreTxPacket(target, pPacket);
    INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);

    /* do completion */
    DoSendCompletion(pEndpoint,&container);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
void
HTCSendCompleteCheckCleanup(struct timer_list *t)
{
	HTC_ENDPOINT *pEndpoint = from_timer(pEndpoint, t, ul_poll_timer);
	HTCSendCompleteCheck(pEndpoint, 1);
}
#else
void
HTCSendCompleteCheckCleanup(void *context)
{
    HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *) context;
    HTCSendCompleteCheck(pEndpoint, 1);
}
#endif

HTC_PACKET *AllocateHTCBundleTxPacket(HTC_TARGET *target)
{
    HTC_PACKET *pPacket;
    HTC_PACKET_QUEUE *pQueueSave;
    adf_nbuf_t netbuf;

    LOCK_HTC_TX(target);
    if (NULL == target->pBundleFreeTxList) {
        UNLOCK_HTC_TX(target);

        /* for HTT packets, if AltDataCreditSize is non zero, we will have
         allocated more space per packet (i.e., TargetCreditSize-AltDataCreditSize)
         per bundle packet, but this should is required since we reuse the packet
         for all services and all endpoints */

        netbuf = adf_nbuf_alloc(NULL,
                HTC_MAX_MSG_PER_BUNDLE_TX * target->TargetCreditSize,
                0,
                4,
                FALSE);
        AR_DEBUG_ASSERT(netbuf);
        if (!netbuf)
        {
            return NULL;
        }
        pPacket = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET));
        AR_DEBUG_ASSERT(pPacket);
        if (!pPacket)
        {
            adf_nbuf_free(netbuf);
            return NULL;
        }
        pQueueSave = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET_QUEUE));
        AR_DEBUG_ASSERT(pQueueSave);
        if (!pQueueSave)
        {
            adf_nbuf_free(netbuf);
            adf_os_mem_free(pPacket);
            return NULL;
        }
        INIT_HTC_PACKET_QUEUE(pQueueSave);
        pPacket->pContext = pQueueSave;
        SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
        pPacket->pBuffer = adf_nbuf_data(netbuf);
        pPacket->BufferLength = adf_nbuf_len(netbuf);

        //store the original head room so that we can restore this when we "free" the packet
        //free packet puts the packet back on the free list
        pPacket->netbufOrigHeadRoom = adf_nbuf_headroom(netbuf);
        return pPacket;
    }

    //already done malloc - restore from free list
    pPacket = target->pBundleFreeTxList;
    AR_DEBUG_ASSERT(pPacket);
    if (!pPacket)
    {
        UNLOCK_HTC_TX(target);
        return NULL;
    }
    target->pBundleFreeTxList = (HTC_PACKET *)pPacket->ListLink.pNext;
    UNLOCK_HTC_TX(target);
    pPacket->ListLink.pNext = NULL;

    return pPacket;
}

void FreeHTCBundleTxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
    A_UINT32 curentHeadRoom;
    adf_nbuf_t netbuf;
    HTC_PACKET_QUEUE *pQueueSave;

    netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
    AR_DEBUG_ASSERT(netbuf);
    if (!netbuf)
    {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("\n%s: Invalid netbuf in HTC "
                                        "Packet\n", __func__));
        return;
    }

    // HIF adds data to the headroom section of the nbuf, restore the original
    // size. If this is not done, headroom keeps shrinking with every HIF send
    // and eventually HIF ends up doing another malloc big enough to store the
    // data + its header

    curentHeadRoom = adf_nbuf_headroom(netbuf);
    adf_nbuf_pull_head(netbuf, pPacket->netbufOrigHeadRoom - curentHeadRoom);
    adf_nbuf_trim_tail(netbuf, adf_nbuf_len(netbuf));

    //restore the pBuffer pointer. HIF changes this
    pPacket->pBuffer = adf_nbuf_data(netbuf);
    pPacket->BufferLength = adf_nbuf_len(netbuf);

    //restore queue
    pQueueSave = (HTC_PACKET_QUEUE*)pPacket->pContext;
    AR_DEBUG_ASSERT(pQueueSave);

    INIT_HTC_PACKET_QUEUE(pQueueSave);

    LOCK_HTC_TX(target);
    if (target->pBundleFreeTxList == NULL) {
        target->pBundleFreeTxList = pPacket;
        pPacket->ListLink.pNext = NULL;
    } else {
        pPacket->ListLink.pNext = (DL_LIST *)target->pBundleFreeTxList;
        target->pBundleFreeTxList = pPacket;
    }
    UNLOCK_HTC_TX(target);
}

HTC_PACKET *AllocateHTCBundleRxPacket(HTC_TARGET *target)
{
    HTC_PACKET *pPacket;
    HTC_PACKET_QUEUE *pQueueSave;
    adf_nbuf_t netbuf;
    LOCK_HTC_TX(target);
    if (NULL == target->pBundleFreeRxList) {
        UNLOCK_HTC_TX(target);
        netbuf = adf_nbuf_alloc(NULL,
                HTC_MAX_MSG_PER_BUNDLE_RX * target->TargetCreditSize,
                0,
                4,
                FALSE);
        AR_DEBUG_ASSERT(netbuf);
        if (!netbuf)
        {
            return NULL;
        }
        pPacket = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET));
        AR_DEBUG_ASSERT(pPacket);
        if (!pPacket)
        {
            adf_nbuf_free(netbuf);
            return NULL;
        }
        pQueueSave = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET_QUEUE));
        AR_DEBUG_ASSERT(pQueueSave);
        if (!pQueueSave)
        {
            adf_nbuf_free(netbuf);
            adf_os_mem_free(pPacket);
            return NULL;
        }
        INIT_HTC_PACKET_QUEUE(pQueueSave);
        pPacket->pContext = pQueueSave;
        SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
        pPacket->pBuffer = adf_nbuf_data(netbuf);
        pPacket->BufferLength = adf_nbuf_len(netbuf);

        //store the original head room so that we can restore this when we "free" the packet
        //free packet puts the packet back on the free list
        pPacket->netbufOrigHeadRoom = adf_nbuf_headroom(netbuf);
        return pPacket;
    }

    //already done malloc - restore from free list
    pPacket = target->pBundleFreeRxList;
    AR_DEBUG_ASSERT(pPacket);
    if (!pPacket)
    {
        UNLOCK_HTC_TX(target);
        return NULL;
    }
    target->pBundleFreeRxList = (HTC_PACKET *)pPacket->ListLink.pNext;
    UNLOCK_HTC_TX(target);
    pPacket->ListLink.pNext = NULL;

    return pPacket;
}

void FreeHTCBundleRxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
    A_UINT32 curentHeadRoom;
    adf_nbuf_t netbuf;
    HTC_PACKET_QUEUE *pQueueSave;

    netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
    AR_DEBUG_ASSERT(netbuf);
    if (!netbuf)
    {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("\n%s: Invalid netbuf in HTC "
                                        "Packet\n", __func__));
        return;
    }

    // HIF adds data to the headroom section of the nbuf, restore the original
    // size. If this is not done, headroom keeps shrinking with every HIF send
    // and eventually HIF ends up doing another malloc big enough to store the
    // data + its header

    curentHeadRoom = adf_nbuf_headroom(netbuf);
    adf_nbuf_pull_head(netbuf, pPacket->netbufOrigHeadRoom - curentHeadRoom);
    adf_nbuf_trim_tail(netbuf, adf_nbuf_len(netbuf));

    //restore the pBuffer pointer. HIF changes this
    pPacket->pBuffer = adf_nbuf_data(netbuf);
    pPacket->BufferLength = adf_nbuf_len(netbuf);

    //restore queue
    pQueueSave = (HTC_PACKET_QUEUE*)pPacket->pContext;
    AR_DEBUG_ASSERT(pQueueSave);

    INIT_HTC_PACKET_QUEUE(pQueueSave);

    LOCK_HTC_TX(target);
    if (target->pBundleFreeRxList == NULL) {
        target->pBundleFreeRxList = pPacket;
        pPacket->ListLink.pNext = NULL;
    } else {
        pPacket->ListLink.pNext = (DL_LIST *)target->pBundleFreeRxList;
        target->pBundleFreeRxList = pPacket;
    }
    UNLOCK_HTC_TX(target);
}


#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
static A_STATUS HTCSendBundledNetbuf(HTC_TARGET *target,
        HTC_ENDPOINT *pEndpoint,
        unsigned char *pBundleBuffer,
        HTC_PACKET *pPacketTx)
{
    adf_os_size_t data_len;
    A_STATUS status;
    adf_nbuf_t bundleBuf;
    bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
    data_len = pBundleBuffer - adf_nbuf_data(bundleBuf);
    adf_nbuf_put_tail(bundleBuf, data_len);
    SET_HTC_PACKET_INFO_TX(pPacketTx,
            target,
            pBundleBuffer,
            data_len,
            pEndpoint->Id,
            HTC_TX_PACKET_TAG_BUNDLED);
    LOCK_HTC_TX(target);
    HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacketTx);
    pEndpoint->ul_outstanding_cnt++;
    UNLOCK_HTC_TX(target);
#if DEBUG_BUNDLE
    adf_os_print(" Send bundle EP%d buffer size:0x%x, total:0x%x, count:%d.\n",
            pEndpoint->Id,
            pEndpoint->TxCreditSize,
            (int)data_len,
            (int)data_len / pEndpoint->TxCreditSize);
#endif

#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
    if ((data_len / pEndpoint->TxCreditSize) <= HTC_MAX_MSG_PER_BUNDLE_TX) {
        target->tx_bundle_stats[(data_len / pEndpoint->TxCreditSize) - 1]++;
    }
#endif

    status = HIFSend_head(target->hif_dev,
               pEndpoint->UL_PipeID,
               pEndpoint->Id,
               data_len,
               bundleBuf);
    if (status != A_OK){
        adf_os_print("%s:HIFSend_head failed(len=%zu).\n", __FUNCTION__,
                data_len);
        LOCK_HTC_TX(target);
        HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacketTx);
        UNLOCK_HTC_TX(target);
    }
    return status;
}

static A_STATUS HTCIssuePacketsBundle(HTC_TARGET *target,
                                      HTC_ENDPOINT *pEndpoint,
                                      HTC_PACKET_QUEUE *pPktQueue)
{
   int              i, frag_count, nbytes;
   adf_nbuf_t       netbuf, bundleBuf;
   unsigned char    *pBundleBuffer = NULL;
   HTC_PACKET       *pPacket = NULL, *pPacketTx = NULL;
#ifndef HIF_USB
   HTC_FRAME_HDR    *pHtcHdr;
#endif
   int              last_creditPad = 0;
   int              creditPad, creditRemainder,transferLength, bundlesSpaceRemaining = 0;
   HTC_PACKET_QUEUE *pQueueSave = NULL;
   A_STATUS ret;

   bundlesSpaceRemaining = HTC_MAX_MSG_PER_BUNDLE_TX * pEndpoint->TxCreditSize;

   pPacketTx = AllocateHTCBundleTxPacket(target);
   if (!pPacketTx)
   {
       //good time to panic
       AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AllocateHTCBundleTxPacket failed \n"));
       AR_DEBUG_ASSERT(FALSE);
       ret = A_NO_MEMORY;
       goto failed1;
   }
   bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
   pBundleBuffer = adf_nbuf_data(bundleBuf);
   pQueueSave = (HTC_PACKET_QUEUE*)pPacketTx->pContext;
   while (1) {
       pPacket = HTC_PACKET_DEQUEUE(pPktQueue);
       if (pPacket == NULL){
           break;
       }
       creditPad = 0;
       transferLength = pPacket->ActualLength + HTC_HDR_LENGTH;
       creditRemainder = transferLength % pEndpoint->TxCreditSize;
       if(creditRemainder != 0){
            if(transferLength < pEndpoint->TxCreditSize){
                creditPad = pEndpoint->TxCreditSize - transferLength;
            } else {
                creditPad = creditRemainder;
            }
            transferLength += creditPad;
       }

       if (bundlesSpaceRemaining < transferLength){
           /* send out previous buffer */
           if (A_OK != HTCSendBundledNetbuf(target, pEndpoint,
                                pBundleBuffer - last_creditPad, pPacketTx)) {
               ret = A_EBUSY;
               HTC_PACKET_ENQUEUE(pQueueSave, pPacket);
               goto failed2;
           }

           /* One packet has been dequeued from sending queue when enter
            * this loop, so need to add 1 back for this checking.
            */
           if ((HTC_PACKET_QUEUE_DEPTH(pPktQueue) + 1) < HTC_MIN_MSG_PER_BUNDLE){
               HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
               goto success;
           }
           bundlesSpaceRemaining = HTC_MAX_MSG_PER_BUNDLE_TX * pEndpoint->TxCreditSize;
           pPacketTx = AllocateHTCBundleTxPacket(target);
           if (!pPacketTx)
           {
               HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
               //good time to panic
               AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AllocateHTCBundleTxPacket failed \n"));
               AR_DEBUG_ASSERT(FALSE);
               ret = A_NO_MEMORY;
               goto failed1;
           }
           bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
           pBundleBuffer = adf_nbuf_data(bundleBuf);
           pQueueSave = (HTC_PACKET_QUEUE*)pPacketTx->pContext;
       }

       bundlesSpaceRemaining -= transferLength;
       netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
#ifndef HIF_USB
       pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
       HTC_WRITE32(pHtcHdr, SM(pPacket->ActualLength, HTC_FRAME_HDR_PAYLOADLEN) |
               SM(pPacket->PktInfo.AsTx.SendFlags | HTC_FLAGS_SEND_BUNDLE, HTC_FRAME_HDR_FLAGS) |
               SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID));
       HTC_WRITE32((A_UINT32 *)pHtcHdr + 1,
                 SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1) |
                 SM(creditPad, HTC_FRAME_HDR_RESERVED));
#ifdef HIF_SDIO
       if(target->enable_b2b)
#endif
       pHtcHdr->reserved = creditPad;
#endif
       frag_count = adf_nbuf_get_num_frags(netbuf);
       nbytes = pPacket->ActualLength + HTC_HDR_LENGTH;
       for (i = 0; i < frag_count && nbytes > 0; i ++){
           int frag_len = adf_nbuf_get_frag_len(netbuf, i);
           unsigned char *frag_addr = adf_nbuf_get_frag_vaddr(netbuf, i);
           if (frag_len > nbytes){
               frag_len = nbytes;
           }
           A_MEMCPY(pBundleBuffer, frag_addr, frag_len);
           nbytes -= frag_len;
           pBundleBuffer += frag_len;
       }
       HTC_PACKET_ENQUEUE(pQueueSave, pPacket);
       pBundleBuffer += creditPad;

#ifdef HIF_USB
       /* last one can't be packed. */
       last_creditPad = creditPad;
#endif
   }
   if (pBundleBuffer != adf_nbuf_data(bundleBuf)){
       /* send out remaining buffer */
       if (A_OK != HTCSendBundledNetbuf(target, pEndpoint,
                            pBundleBuffer - last_creditPad, pPacketTx))
       {
           ret = A_EBUSY;
           goto failed2;
       }
   } else {
       FreeHTCBundleTxPacket(target, pPacketTx);
   }

success:
   return A_OK;

failed2:
    if (!HTC_QUEUE_EMPTY(pQueueSave))
       HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(pPktQueue, pQueueSave);
    FreeHTCBundleTxPacket(target, pPacketTx);
failed1:
    AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("send bundle buffer failed(%d) \n", ret));
    return ret;
}
#endif /* ENABLE_BUNDLE_TX */
#endif

static A_STATUS HTCIssuePackets(HTC_TARGET       *target,
                                HTC_ENDPOINT     *pEndpoint,
                                HTC_PACKET_QUEUE *pPktQueue)
{
    A_STATUS            status = A_OK;
    adf_nbuf_t          netbuf;
    HTC_PACKET          *pPacket = NULL;
    u_int16_t           payloadLen;
    HTC_FRAME_HDR       *pHtcHdr;
    bool                is_tx_runtime_put = false;

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCIssuePackets: Queue: %pK, Pkts %d \n",
                    pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
    while (TRUE) {
#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
        if(
#ifndef HIF_USB
           IS_TX_CREDIT_FLOW_ENABLED(pEndpoint) &&
#endif
                HTC_ENABLE_BUNDLE(target) &&
                HTC_PACKET_QUEUE_DEPTH(pPktQueue) >= HTC_MIN_MSG_PER_BUNDLE){
            HTCIssuePacketsBundle(target, pEndpoint, pPktQueue);
        }
#endif
#endif
        /* if not bundling or there was a packet that could not be placed in a bundle,
         * and send it by normal way
         */
        pPacket = HTC_PACKET_DEQUEUE(pPktQueue);
        if(NULL == pPacket){
            /* local queue is fully drained */
            break;
        }

        netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
        AR_DEBUG_ASSERT(netbuf);
        /* Non-credit enabled endpoints have been mapped and setup by now,
         * so no need to revisit the HTC headers
         */
        if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {

            payloadLen = pPacket->ActualLength;
            /* setup HTC frame header */

            pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
            AR_DEBUG_ASSERT(pHtcHdr);


            HTC_WRITE32(pHtcHdr, SM(payloadLen, HTC_FRAME_HDR_PAYLOADLEN) |
                    SM(pPacket->PktInfo.AsTx.SendFlags, HTC_FRAME_HDR_FLAGS) |
                    SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID));
            HTC_WRITE32(((A_UINT32 *)pHtcHdr) + 1,
                    SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));

            /*
             * Now that the HTC frame header has been added, the netbuf can be
             * mapped.  This only applies to non-data frames, since data frames
             * were already mapped as they entered into the driver.
             */
            pPacket->PktInfo.AsTx.Flags |= HTC_TX_PACKET_FLAG_FIXUP_NETBUF;

            adf_nbuf_map(
                    target->osdev,
                    GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
                    ADF_OS_DMA_TO_DEVICE);
        }
        LOCK_HTC_TX(target);
            /* store in look up queue to match completions */
#ifdef ATH_11AC_TXCOMPACT
        if (HTT_DATA_MSG_SVC != pEndpoint->ServiceID)
#endif /* ATH_11AC_TXCOMPACT */
        {
            HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue,pPacket);
        }
        INC_HTC_EP_STAT(pEndpoint,TxIssued,1);
        pEndpoint->ul_outstanding_cnt++;
        UNLOCK_HTC_TX(target);

        if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_RUNTIME_PUT)
            is_tx_runtime_put = true;

        status = HIFSend_head(target->hif_dev,
                              pEndpoint->UL_PipeID, pEndpoint->Id,
                              HTC_HDR_LENGTH + pPacket->ActualLength,
                              netbuf);
#if DEBUG_BUNDLE
        adf_os_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
            pEndpoint->Id,
            pEndpoint->TxCreditSize,
            (int)HTC_HDR_LENGTH + pPacket->ActualLength);
#endif

#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
        target->tx_bundle_stats[0]++;
#endif

	target->CE_send_cnt++;

        if (adf_os_unlikely(A_FAILED(status))) {
            /* TODO : if more than 1 endpoint maps to the same PipeID it is possible
             * to run out of resources in the HIF layer. Don't emit the error */
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HIFSend Failed status: %d\n", status));
            LOCK_HTC_TX(target);
	    target->CE_send_cnt--;
            pEndpoint->ul_outstanding_cnt--;
            HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue,pPacket);
                /* reclaim credits */
            pEndpoint->TxCredits += pPacket->PktInfo.AsTx.CreditsUsed;
                /* put it back into the callers queue */
            HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue,pPacket);
            UNLOCK_HTC_TX(target);
            break;
        }

        /*
         * For HTT messages the Tx complete is disabled and for some HTT
         * messages which doesn't have response from FW, runtime pm put
         * is not happening. To address that the HTT packet which is tagged
         * with HTC_TX_PACKET_TAG_RUNTIME_PUT releases the count after the
         * packet sent is successful
         */
        if (is_tx_runtime_put) {
            is_tx_runtime_put = false;
            hif_pm_runtime_put(target->hif_dev);
        }
    }

    if (adf_os_unlikely(A_FAILED(status)))
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
            ("HTCIssuePackets, failed pkt:0x%pK status:%d",
            pPacket, status));

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCIssuePackets \n"));

    return status;
}

#ifdef FEATURE_RUNTIME_PM
static void GetHTCAutoPmSendPackets(HTC_TARGET        *target,
                                    HTC_ENDPOINT      *pEndpoint,
                                    HTC_PACKET_QUEUE  *pQueue)
{
	HTC_PACKET   *pPacket;
	/*
	 * If the EP is not WMI EP then it is not required to go through
	 * the queue to find the tagged packkets.
	 */
	if (pEndpoint->ServiceID != WMI_CONTROL_SVC)
		return;

	ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket,
			HTC_PACKET, ListLink) {

		if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM)
			continue;

		HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket);
		HTC_PACKET_ENQUEUE(pQueue, pPacket);
	} ITERATE_END
}

static void RequeueHTCAutoPmSendPackets(HTC_TARGET        *target,
                                        HTC_ENDPOINT      *pEndpoint,
                                        HTC_PACKET_QUEUE  *pQueue)
{
	if (pEndpoint->ServiceID != WMI_CONTROL_SVC)
		return;

	/*
	 * Go through remaining packets in the temporary queue and add them to
	 * head of the queue so that those would be sent at priority than other
	 * packets
	 */
	HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, pQueue);
}
#else
static inline void GetHTCAutoPmSendPackets(HTC_TARGET        *target,
                                           HTC_ENDPOINT      *pEndpoint,
                                           HTC_PACKET_QUEUE  *pQueue)
{
	return;
}
static inline void RequeueHTCAutoPmSendPackets(HTC_TARGET        *target,
                                               HTC_ENDPOINT      *pEndpoint,
                                               HTC_PACKET_QUEUE  *pQueue)
{
	return;
}
#endif

    /* get HTC send packets from the TX queue on an endpoint, based on available credits */
void GetHTCSendPacketsCreditBased(HTC_TARGET        *target,
                                  HTC_ENDPOINT      *pEndpoint,
                                  HTC_PACKET_QUEUE  *pQueue)
{
    int          creditsRequired;
    int          remainder;
    A_UINT8      sendFlags;
    HTC_PACKET   *pPacket;
    unsigned int transferLength;
    HTC_PACKET_QUEUE *pTxQueue;
    HTC_PACKET_QUEUE tempQueue;
    A_BOOL AutoPMQueue = FALSE;

    /****** NOTE : the TX lock is held when this function is called *****************/
    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPacketsCreditBased \n"));

    INIT_HTC_PACKET_QUEUE(&tempQueue);

    GetHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);

    if (!HTC_QUEUE_EMPTY(&tempQueue)) {
        pTxQueue = &tempQueue;
        AutoPMQueue = TRUE;
    } else
        pTxQueue = &pEndpoint->TxQueue;

        /* loop until we can grab as many packets out of the queue as we can */
    while (TRUE) {

        sendFlags = 0;

        if (!AutoPMQueue && hif_pm_runtime_get(target->hif_dev)) {
           A_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
           break;
        }

        /* get packet at head, but don't remove it */
        pPacket = HTC_GET_PKT_AT_HEAD(pTxQueue);
        if (pPacket == NULL) {
            if (!AutoPMQueue)
                hif_pm_runtime_put(target->hif_dev);
            break;
        }

        AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:%pK , Queue Depth: %d\n",
                pPacket, HTC_PACKET_QUEUE_DEPTH(pTxQueue)));

        transferLength = pPacket->ActualLength + HTC_HDR_LENGTH;

        if (transferLength <= pEndpoint->TxCreditSize) {
            creditsRequired = 1;
        } else {
                /* figure out how many credits this message requires */
            creditsRequired = transferLength / pEndpoint->TxCreditSize;
            remainder = transferLength % pEndpoint->TxCreditSize;

            if (remainder) {
                creditsRequired++;
            }
        }

        AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Credits Required:%d   Got:%d\n",
                            creditsRequired, pEndpoint->TxCredits));

        if (pEndpoint->Id == ENDPOINT_0) {
            /* endpoint 0 is special, it always has a credit and does not require credit based
             * flow control */
            creditsRequired = 0;
        } else {

            if (pEndpoint->TxCredits < creditsRequired) {
#if DEBUG_CREDIT
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" EP%d, No Credit now. %d < %d\n",
                        pEndpoint->Id, pEndpoint->TxCredits, creditsRequired));
#endif
                if (!AutoPMQueue)
                   hif_pm_runtime_put(target->hif_dev);
                break;
            }

            pEndpoint->TxCredits -= creditsRequired;
            INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired);

                /* check if we need credits back from the target */
            if (pEndpoint->TxCredits <= pEndpoint->TxCreditsPerMaxMsg) {
                /* tell the target we need credits ASAP! */
                sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE;

                if (pEndpoint->ServiceID == WMI_CONTROL_SVC) {
                    LOCK_HTC_CREDIT(target);
                    htc_credit_record(HTC_REQUEST_CREDIT, pEndpoint->TxCredits,
                        HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue));
                    UNLOCK_HTC_CREDIT(target);
                }

                INC_HTC_EP_STAT(pEndpoint, TxCreditLowIndications, 1);
#if DEBUG_CREDIT
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" EP%d Needs Credits\n", pEndpoint->Id));
#endif
            }
        }

            /* now we can fully dequeue */
        pPacket = HTC_PACKET_DEQUEUE(pTxQueue);
	if (pPacket) {
		/* save the number of credits this packet consumed */
		pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired;
		/* save send flags */
		pPacket->PktInfo.AsTx.SendFlags = sendFlags;

		/* queue this packet into the caller's queue */
		HTC_PACKET_ENQUEUE(pQueue,pPacket);
	}
    }

    if (AutoPMQueue)
        RequeueHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPacketsCreditBased \n"));
}

void GetHTCSendPackets(HTC_TARGET        *target,
                       HTC_ENDPOINT      *pEndpoint,
                       HTC_PACKET_QUEUE  *pQueue,
                       int                Resources)
{

    HTC_PACKET   *pPacket;
    HTC_PACKET_QUEUE *pTxQueue;
    HTC_PACKET_QUEUE tempQueue;
    A_BOOL AutoPMQueue = FALSE;

    /****** NOTE : the TX lock is held when this function is called *****************/
    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets %d resources\n",Resources));

    INIT_HTC_PACKET_QUEUE(&tempQueue);

    GetHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);

    if (!HTC_QUEUE_EMPTY(&tempQueue)) {
        pTxQueue = &tempQueue;
        AutoPMQueue = TRUE;
    } else
        pTxQueue = &pEndpoint->TxQueue;
        /* loop until we can grab as many packets out of the queue as we can */
    while (Resources > 0) {
        int num_frags;

        if (!AutoPMQueue && hif_pm_runtime_get(target->hif_dev)) {
           A_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
           break;
        }

        pPacket = HTC_PACKET_DEQUEUE(pTxQueue);
        if (pPacket == NULL) {
            if (!AutoPMQueue)
               hif_pm_runtime_put(target->hif_dev);
            break;
        }

        AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got packet:%pK , New Queue Depth: %d\n",
                pPacket, HTC_PACKET_QUEUE_DEPTH(pTxQueue)));
        /* For non-credit path the sequence number is already embedded
         * in the constructed HTC header
         */
#if 0
        pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
        pEndpoint->SeqNo++;
#endif
        pPacket->PktInfo.AsTx.SendFlags = 0;
        pPacket->PktInfo.AsTx.CreditsUsed = 0;
        /* queue this packet into the caller's queue */
        HTC_PACKET_ENQUEUE(pQueue,pPacket);

        /*
         * FIX THIS:
         * For now, avoid calling adf_nbuf_get_num_frags before calling
         * adf_nbuf_map, because the MacOS version of adf_nbuf_t doesn't
         * support adf_nbuf_get_num_frags until after adf_nbuf_map has
         * been done.
         * Assume that the non-data netbufs, i.e. the WMI message netbufs,
         * consist of a single fragment.
         */
        num_frags =
            (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) ?
            1 /* WMI messages are in a single-fragment network buffer */ :
            adf_nbuf_get_num_frags(GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket));
        Resources -= num_frags;
    }

    if (AutoPMQueue)
        RequeueHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n"));

}

static HTC_SEND_QUEUE_RESULT HTCTrySend(HTC_TARGET       *target,
                                        HTC_ENDPOINT     *pEndpoint,
                                        HTC_PACKET_QUEUE *pCallersSendQueue)
{
    HTC_PACKET_QUEUE      sendQueue; /* temp queue to hold packets at various stages */
    HTC_PACKET            *pPacket;
    int                   tx_resources;
    int                   overflow;
    HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (Queue:%pK Depth:%d)\n",
            pCallersSendQueue,
            (pCallersSendQueue == NULL) ? 0 : HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue)));

        /* init the local send queue */
    INIT_HTC_PACKET_QUEUE(&sendQueue);

    do {

        if (NULL == pCallersSendQueue) {
                /* caller didn't provide a queue, just wants us to check queues and send */
            break;
        }

        if (HTC_QUEUE_EMPTY(pCallersSendQueue)) {
                /* empty queue */
            OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,HTC_PKT_Q_EMPTY);
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Empty queue\n"));
            result = HTC_SEND_QUEUE_DROP;
            break;
        }

        if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >= pEndpoint->MaxTxQueueDepth) {
                    /* we've already overflowed */
            overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
        } else {
                /* figure out how much we will overflow by */
            overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
            overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
                /* figure out how much we will overflow the TX queue by */
            overflow -= pEndpoint->MaxTxQueueDepth;
        }

            /* if overflow is negative or zero, we are okay */
        if (overflow > 0) {
            AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
                (" Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d \n",
                pEndpoint->Id, overflow, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue), pEndpoint->MaxTxQueueDepth));
        }
        if ((overflow <= 0) || (pEndpoint->EpCallBacks.EpSendFull == NULL)) {
                /* all packets will fit or caller did not provide send full indication handler
                 * --  just move all of them to the local sendQueue object */
		HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue, pCallersSendQueue);
        } else {
            int               i;
            int               goodPkts = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) - overflow;

            A_ASSERT(goodPkts >= 0);
                /* we have overflowed, and a callback is provided */
                /* dequeue all non-overflow packets into the sendqueue */
            for (i = 0; i < goodPkts; i++) {
                    /* pop off caller's queue*/
                pPacket = HTC_PACKET_DEQUEUE(pCallersSendQueue);
                A_ASSERT(pPacket != NULL);
                    /* insert into local queue */
                HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
            }

                /* the caller's queue has all the packets that won't fit*/
                /* walk through the caller's queue and indicate each one to the send full handler */
            ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue->QueueHead, pPacket, HTC_PACKET, ListLink) {

                AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Indicating overflowed TX packet: %pK \n",
                                pPacket));
                /*
                 * Remove headroom reserved for HTC_FRAME_HDR before giving
                 * the packet back to the user via the EpSendFull callback.
                 */
                RestoreTxPacket(target, pPacket);

                if (pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext,
                                                      pPacket) == HTC_SEND_FULL_DROP) {
                        /* callback wants the packet dropped */
                    INC_HTC_EP_STAT(pEndpoint, TxDropped, 1);
                        /* leave this one in the caller's queue for cleanup */
                } else {
                        /* callback wants to keep this packet, remove from caller's queue */
                    HTC_PACKET_REMOVE(pCallersSendQueue, pPacket);
                        /* put it in the send queue */
                    /* add HTC_FRAME_HDR space reservation again */
                    adf_nbuf_push_head(
                        GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
                        sizeof(HTC_FRAME_HDR));

                    HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
                }

            } ITERATE_END;

            if (HTC_QUEUE_EMPTY(&sendQueue)) {
                    /* no packets made it in, caller will cleanup */
                OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,HTC_SEND_Q_EMPTY);
                result = HTC_SEND_QUEUE_DROP;
                break;
            }
        }

    } while (FALSE);

    if (result != HTC_SEND_QUEUE_OK) {
        AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend:  \n"));
        return result;
    }

    if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
        tx_resources = HIFGetFreeQueueNumber(target->hif_dev,pEndpoint->UL_PipeID);
    } else {
        tx_resources = 0;
    }

    LOCK_HTC_TX(target);

    if (!HTC_QUEUE_EMPTY(&sendQueue)) {
        if (target->is_nodrop_pkt) {
            /*
            * nodrop pkts have higher priority than normal pkts, insert nodrop pkt
            * to head for proper start/termination of test.
            */
            HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,&sendQueue);
            target->is_nodrop_pkt = FALSE;
        } else {
            /* transfer packets to tail */
            HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue,&sendQueue);
            A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue));
            INIT_HTC_PACKET_QUEUE(&sendQueue);
        }
    }

        /* increment tx processing count on entry */
    adf_os_atomic_inc(&pEndpoint->TxProcessCount);
    if (adf_os_atomic_read(&pEndpoint->TxProcessCount) > 1) {
            /* another thread or task is draining the TX queues on this endpoint
             * that thread will reset the tx processing count when the queue is drained */
        adf_os_atomic_dec(&pEndpoint->TxProcessCount);
        UNLOCK_HTC_TX(target);
        AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (busy) \n"));
        return HTC_SEND_QUEUE_OK;
    }

    /***** beyond this point only 1 thread may enter ******/

        /* now drain the endpoint TX queue for transmission as long as we have enough
         * transmit resources */
    while (TRUE) {

        if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) {
            break;
        }

        if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
#if DEBUG_CREDIT
            int cred = pEndpoint->TxCredits;
#endif
            /* credit based mechanism provides flow control based on target transmit resource availability, we
             * assume that the HIF layer will always have bus resources greater than target transmit resources */
            GetHTCSendPacketsCreditBased(target,pEndpoint,&sendQueue);
#if DEBUG_CREDIT
        if (ep_debug_mask & (1 << pEndpoint->Id)){
            if (cred - pEndpoint->TxCredits > 0){
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" <HTC> Decrease EP%d %d - %d = %d credits.\n",
                        pEndpoint->Id, cred, cred - pEndpoint->TxCredits, pEndpoint->TxCredits));
            }
        }
#endif
        } else {
#ifdef HIF_USB
#ifdef ENABLE_BUNDLE_TX
            /*
             * Header and payload belongs to the different fragments and
             * consume 2 resource for one HTC package but USB conbime into
             * one transfer. And one WMI message only consumes one single
             * resource.
             */
            if (HTC_ENABLE_BUNDLE(target) && tx_resources) {
                if (pEndpoint->ServiceID == WMI_CONTROL_SVC)
                    tx_resources = HTC_MAX_MSG_PER_BUNDLE_TX;
                else
                    tx_resources = (HTC_MAX_MSG_PER_BUNDLE_TX * 2);
            }
#endif
#endif
                /* get all the packets for this endpoint that we can for this pass */
            GetHTCSendPackets(target,pEndpoint,&sendQueue,tx_resources);
        }

        if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) {
                /* didn't get any packets due to a lack of resources or TX queue was drained */
            break;
        }

        UNLOCK_HTC_TX(target);

            /* send what we can */
        result = HTCIssuePackets(target,pEndpoint,&sendQueue);
        if (result) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
               ("HTCIssuePackets, failed status:%d put it back to head of callers SendQueue",
               result));
            LOCK_HTC_TX(target);
            HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,
                             &sendQueue);
            break;
        }

        if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
            tx_resources = HIFGetFreeQueueNumber(target->hif_dev,pEndpoint->UL_PipeID);
        }

        LOCK_HTC_TX(target);

    }

        /* done with this endpoint, we can clear the count */
    adf_os_atomic_init(&pEndpoint->TxProcessCount);
    UNLOCK_HTC_TX(target);

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend:  \n"));

    return HTC_SEND_QUEUE_OK;
}

A_STATUS HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
{
    HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT    *pEndpoint;
    HTC_PACKET      *pPacket;
    adf_nbuf_t      netbuf;
    HTC_FRAME_HDR       *pHtcHdr;

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: %pK, Pkts %d \n",
                    pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));

        /* get packet at head to figure out which endpoint these packets will go into */
    pPacket = HTC_GET_PKT_AT_HEAD(pPktQueue);
    if (NULL == pPacket) {
        OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,GET_HTC_PKT_Q_FAIL);
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("-HTCSendPktsMultiple \n"));
        return A_EINVAL;
    }

    AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
    pEndpoint = &target->EndPoint[pPacket->Endpoint];

    if (!pEndpoint->ServiceID) {
       AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: ServiceID is invalid\n",
                                                 __func__));
       return A_EINVAL;
    }

#ifdef HTC_EP_STAT_PROFILING
    LOCK_HTC_TX(target);
    INC_HTC_EP_STAT(pEndpoint,TxPosted,HTC_PACKET_QUEUE_DEPTH(pPktQueue));
    UNLOCK_HTC_TX(target);
#endif

    /* provide room in each packet's netbuf for the HTC frame header */
    HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
        netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
        AR_DEBUG_ASSERT(netbuf);

        adf_nbuf_push_head(netbuf, sizeof(HTC_FRAME_HDR));
        /* setup HTC frame header */
        pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
        AR_DEBUG_ASSERT(pHtcHdr);
        HTC_WRITE32(pHtcHdr, SM(pPacket->ActualLength, HTC_FRAME_HDR_PAYLOADLEN) |
                SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID));

            LOCK_HTC_TX(target);

            pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
            pEndpoint->SeqNo++;

            HTC_WRITE32(((A_UINT32 *)pHtcHdr) + 1,
                    SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));

            UNLOCK_HTC_TX(target);
            /*
             * Now that the HTC frame header has been added, the netbuf can be
             * mapped.  This only applies to non-data frames, since data frames
             * were already mapped as they entered into the driver.
             * For flow control enabled endpoints mapping is done in
             * HTCIssuePackets and for non flow control enabled endpoints
             * its done here.
             */

            if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
                pPacket->PktInfo.AsTx.Flags |= HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
                adf_nbuf_map(
                        target->osdev,
                        GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
                        ADF_OS_DMA_TO_DEVICE);

            }
    } HTC_PACKET_QUEUE_ITERATE_END;

    HTCTrySend(target,pEndpoint,pPktQueue);

        /* do completion on any packets that couldn't get in */
    if (!HTC_QUEUE_EMPTY(pPktQueue)) {

        HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
            /* remove the headroom reserved for HTC_FRAME_HDR */
            RestoreTxPacket(target, pPacket);

            if (HTC_STOPPING(target)) {
                pPacket->Status = A_ECANCELED;
            } else {
                pPacket->Status = A_NO_RESOURCE;
            }
        } HTC_PACKET_QUEUE_ITERATE_END;

        DoSendCompletion(pEndpoint,pPktQueue);
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));

    return A_OK;
}

/* HTC API - HTCSendPkt */
A_STATUS    HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
    HTC_PACKET_QUEUE queue;
    A_STATUS  send_status;

    if (HTCHandle == NULL || pPacket == NULL) {
        return A_ERROR;
    }
    a_mem_trace(GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket));
    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
                    ("+-HTCSendPkt: Enter endPointId: %d, buffer: %pK, length: %d \n",
                    pPacket->Endpoint, pPacket->pBuffer, pPacket->ActualLength));
    INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
    send_status = HTCSendPktsMultiple(HTCHandle, &queue);
    pPacket = NULL;
    return send_status;
}

#ifdef ATH_11AC_TXCOMPACT

A_STATUS HTCSendDataPkt(HTC_HANDLE HTCHandle, adf_nbuf_t       netbuf, int Epid, int ActualLength)
{
    HTC_TARGET       *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT     *pEndpoint;
    HTC_FRAME_HDR    *pHtcHdr;
    A_STATUS         status = A_OK;
    int              tx_resources;

    pEndpoint = &target->EndPoint[Epid];

    tx_resources = HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID);

    if(tx_resources <  HTC_DATA_RESOURCE_THRS){
        if (pEndpoint->ul_is_polled){
            HIFSendCompleteCheck(
                    pEndpoint->target->hif_dev, pEndpoint->UL_PipeID, 1);
            tx_resources = HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID);
        }
        if(tx_resources < HTC_DATA_MINDESC_PERPACKET){
            return A_ERROR;
        }
    }

    if (hif_pm_runtime_get(target->hif_dev))
        return A_ERROR;

    pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
    AR_DEBUG_ASSERT(pHtcHdr);

    HTC_WRITE32(pHtcHdr, SM(ActualLength, HTC_FRAME_HDR_PAYLOADLEN) |
			 SM(Epid, HTC_FRAME_HDR_ENDPOINTID));
    /*
     * If the HIF pipe for the data endpoint is polled rather than
     * interrupt-driven, this is a good point to check whether any
     * data previously sent through the HIF pipe have finished being
     * sent.
     * Since this may result in callbacks to HTCTxCompletionHandler,
     * which can take the HTC tx lock, make the HIFSendCompleteCheck
     * call before acquiring the HTC tx lock.
     * Call HIFSendCompleteCheck directly, rather than calling
     * HTCSendCompleteCheck, and call the PollTimerStart separately
     * after calling HIFSend_head, so the timer will be started to
     * check for completion of the new outstanding download (in the
     * unexpected event that other polling calls don't catch it).
     */

    LOCK_HTC_TX(target);

    HTC_WRITE32(((A_UINT32 *)pHtcHdr) + 1, SM(pEndpoint->SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));

    pEndpoint->SeqNo++;

    NBUF_UPDATE_TX_PKT_COUNT(netbuf, NBUF_TX_PKT_HTC);
    DPTRACE(adf_dp_trace(netbuf, ADF_DP_TRACE_HTC_PACKET_PTR_RECORD,
                adf_nbuf_data_addr(netbuf),
                sizeof(adf_nbuf_data(netbuf)), ADF_TX));

    status = HIFSend_head(target->hif_dev,
            pEndpoint->UL_PipeID,
            pEndpoint->Id,
            ActualLength,
            netbuf);


    UNLOCK_HTC_TX(target);
    return status ;
}
#else /*ATH_11AC_TXCOMPACT*/

A_STATUS HTCSendDataPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
                        A_UINT8 more_data)
{
    HTC_TARGET       *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT     *pEndpoint;
    HTC_FRAME_HDR    *pHtcHdr;
    HTC_PACKET_QUEUE sendQueue;
    adf_nbuf_t       netbuf;
    int              tx_resources;
    A_STATUS         status = A_OK;
    if (pPacket){
        AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
        pEndpoint = &target->EndPoint[pPacket->Endpoint];

        /* add HTC_FRAME_HDR in the initial fragment */
        netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
        pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
        AR_DEBUG_ASSERT(pHtcHdr);

        if (pPacket->ActualLength > (pEndpoint->TxCreditSize - HTC_HDR_LENGTH)) {
            pPacket->ActualLength = pEndpoint->TxCreditSize - HTC_HDR_LENGTH;
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                            ("Packet Length %d exceeds endpoint credit size %d, set the packet length to credit size\n",
                             pPacket->ActualLength, pEndpoint->TxCreditSize));
        }
        HTC_WRITE32(pHtcHdr, SM(pPacket->ActualLength, HTC_FRAME_HDR_PAYLOADLEN) |
                 SM(pPacket->PktInfo.AsTx.SendFlags, HTC_FRAME_HDR_FLAGS) |
                 SM(pPacket->Endpoint, HTC_FRAME_HDR_ENDPOINTID));
        /*
         * If the HIF pipe for the data endpoint is polled rather than
         * interrupt-driven, this is a good point to check whether any
         * data previously sent through the HIF pipe have finished being
         * sent.
         * Since this may result in callbacks to HTCTxCompletionHandler,
         * which can take the HTC tx lock, make the HIFSendCompleteCheck
         * call before acquiring the HTC tx lock.
         * Call HIFSendCompleteCheck directly, rather than calling
         * HTCSendCompleteCheck, and call the PollTimerStart separately
         * after calling HIFSend_head, so the timer will be started to
         * check for completion of the new outstanding download (in the
         * unexpected event that other polling calls don't catch it).
         */
        if (pEndpoint->ul_is_polled) {
            HTCSendCompletePollTimerStop(pEndpoint);
            HIFSendCompleteCheck(
                pEndpoint->target->hif_dev, pEndpoint->UL_PipeID, 0);
        }

        LOCK_HTC_TX(target);

        pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
        pEndpoint->SeqNo++;


        HTC_WRITE32(((A_UINT32 *)pHtcHdr) + 1, SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));

        /* append new packet to pEndpoint->TxQueue */
        HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue, pPacket);
#ifdef ENABLE_BUNDLE_TX
        if (HTC_ENABLE_BUNDLE(target) && (more_data)) {
            UNLOCK_HTC_TX(target);
            return A_OK;
        }
#endif
    } else {
        LOCK_HTC_TX(target);
        pEndpoint = &target->EndPoint[1];
    }

    /* increment tx processing count on entry */
    adf_os_atomic_inc(&pEndpoint->TxProcessCount);
    if (adf_os_atomic_read(&pEndpoint->TxProcessCount) > 1) {
        /*
         * Another thread or task is draining the TX queues on this endpoint.
         * That thread will reset the tx processing count when the queue is
         * drained.
         */
        adf_os_atomic_dec(&pEndpoint->TxProcessCount);
        UNLOCK_HTC_TX(target);
        return A_OK;
    }

    /***** beyond this point only 1 thread may enter ******/

    INIT_HTC_PACKET_QUEUE(&sendQueue);
    if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
#if DEBUG_CREDIT
        int cred = pEndpoint->TxCredits;
#endif
        GetHTCSendPacketsCreditBased(target, pEndpoint, &sendQueue);
#if DEBUG_CREDIT
        if (ep_debug_mask & (1 << pEndpoint->Id)){
            if (cred - pEndpoint->TxCredits > 0){
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" <HTC> Decrease EP%d %d - %d = %d credits.\n",
                        pEndpoint->Id, cred, cred - pEndpoint->TxCredits, pEndpoint->TxCredits));
            }
        }
#endif
        UNLOCK_HTC_TX(target);
    }
#ifdef ENABLE_BUNDLE_TX
    else if (HTC_ENABLE_BUNDLE(target)) {
#ifdef HIF_USB
        if (HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID)) {
            /*
             * Header and payload belongs to the different fragments and
             * consume 2 resource for one HTC package but USB conbime into
             * one transfer.
             */
            GetHTCSendPackets(target, pEndpoint, &sendQueue,
                              (HTC_MAX_MSG_PER_BUNDLE_TX * 2));
        }
#else
        /* Dequeue max packets from endpoint tx queue */
        GetHTCSendPackets(target, pEndpoint, &sendQueue,
                          HTC_MAX_TX_BUNDLE_SEND_LIMIT);
#endif
        UNLOCK_HTC_TX(target);
    }
#endif
    else {
        /*
         * Now drain the endpoint TX queue for transmission as long as we have
         * enough transmit resources
         */
        tx_resources = HIFGetFreeQueueNumber(target->hif_dev,pEndpoint->UL_PipeID);
        GetHTCSendPackets(target, pEndpoint, &sendQueue, tx_resources);
        UNLOCK_HTC_TX(target);
    }
    /* send what we can */
    while (TRUE) {
#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
        if (HTC_ENABLE_BUNDLE(target) &&
           HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_MSG_PER_BUNDLE) {
            HTCIssuePacketsBundle(target, pEndpoint, &sendQueue);
        }
#endif
#endif
        pPacket = HTC_PACKET_DEQUEUE(&sendQueue);
        if (pPacket == NULL){
            break;
        }
        netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);

        LOCK_HTC_TX(target);
        /* store in look up queue to match completions */
        HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue,pPacket);
        INC_HTC_EP_STAT(pEndpoint,TxIssued,1);
        pEndpoint->ul_outstanding_cnt++;
        UNLOCK_HTC_TX(target);

        status = HIFSend_head(target->hif_dev,
                              pEndpoint->UL_PipeID,
                              pEndpoint->Id,
                              HTC_HDR_LENGTH + pPacket->ActualLength,
                              netbuf);
#if DEBUG_BUNDLE
        adf_os_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
            pEndpoint->Id,
            pEndpoint->TxCreditSize,
            (int)HTC_HDR_LENGTH + pPacket->ActualLength);
#endif

#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
        target->tx_bundle_stats[0]++;
#endif

        if (adf_os_unlikely(A_FAILED(status))) {
            LOCK_HTC_TX(target);
            pEndpoint->ul_outstanding_cnt--;
            /* remove this packet from the tx completion queue */
            HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue,pPacket);

            /*
             * Don't bother reclaiming credits - HTC flow control
             * is not applicable to tx data.
             * In LL systems, there is no download flow control,
             * since there's virtually no download delay.
             * In HL systems, the txrx SW explicitly performs the
             * tx flow control.
             */
            //pEndpoint->TxCredits += pPacket->PktInfo.AsTx.CreditsUsed;

            /* put this frame back at the front of the sendQueue */
            HTC_PACKET_ENQUEUE_TO_HEAD(&sendQueue, pPacket);

            /* put the sendQueue back at the front of pEndpoint->TxQueue */
            HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, &sendQueue);
            UNLOCK_HTC_TX(target);
            break; /* still need to reset TxProcessCount */
        }
    }
    /* done with this endpoint, we can clear the count */
    adf_os_atomic_init(&pEndpoint->TxProcessCount);

    if (pEndpoint->ul_is_polled) {
        /*
         * Start a cleanup timer to poll for download completion.
         * The download completion should be noticed promptly from
         * other polling calls, but the timer provides a safety net
         * in case other polling calls don't occur as expected.
         */
        HTCSendCompletePollTimerStart(pEndpoint);
    }

    return status;
}
#endif /*ATH_11AC_TXCOMPACT*/

/*
 * In the adapted HIF layer, adf_nbuf_t are passed between HIF and HTC, since upper layers expects
 * HTC_PACKET containers we use the completed netbuf and lookup its corresponding HTC packet buffer
 * from a lookup list.
 * This is extra overhead that can be fixed by re-aligning HIF interfaces with HTC.
 *
 */
static HTC_PACKET *HTCLookupTxPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, adf_nbuf_t netbuf)
{
    HTC_PACKET *pPacket = NULL;
    HTC_PACKET *pFoundPacket = NULL;
    HTC_PACKET_QUEUE lookupQueue;

    INIT_HTC_PACKET_QUEUE(&lookupQueue);
    LOCK_HTC_TX(target);

    /* mark that HIF has indicated the send complete for another packet */
    pEndpoint->ul_outstanding_cnt--;

    /* Dequeue first packet directly because of in-order completion */
    pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxLookupQueue);
    if (adf_os_unlikely(!pPacket)) {
        UNLOCK_HTC_TX(target);
        return NULL;
    }
    if (netbuf == (adf_nbuf_t)GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) {
        UNLOCK_HTC_TX(target);
        return pPacket;
    } else {
        HTC_PACKET_ENQUEUE(&lookupQueue, pPacket);
    }

    /*
     * Move TX lookup queue to temp queue because most of packets that are not index 0
     * are not top 10 packets.
     */
    HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&lookupQueue, &pEndpoint->TxLookupQueue);
    UNLOCK_HTC_TX(target);

    ITERATE_OVER_LIST_ALLOW_REMOVE(&lookupQueue.QueueHead,pPacket,HTC_PACKET,ListLink) {

        if (NULL == pPacket){
            pFoundPacket = pPacket;
            break;
        }
            /* check for removal */
        if (netbuf == (adf_nbuf_t)GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) {
                /* found it */
            HTC_PACKET_REMOVE(&lookupQueue, pPacket);
            pFoundPacket = pPacket;
            break;
        }

    } ITERATE_END;

    LOCK_HTC_TX(target);
    HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxLookupQueue, &lookupQueue);
    UNLOCK_HTC_TX(target);

    return pFoundPacket;
}


A_STATUS HTCTxCompletionHandler(void *Context,
                                adf_nbuf_t netbuf,
                                unsigned int EpID)
{
    HTC_TARGET      *target = (HTC_TARGET *)Context;
    HTC_ENDPOINT    *pEndpoint;
    HTC_PACKET      *pPacket;

    pEndpoint = &target->EndPoint[EpID];
    target->TX_comp_cnt++;

    do {
        pPacket = HTCLookupTxPacket(target, pEndpoint, netbuf);
        if (NULL == pPacket) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTC TX lookup failed!\n"));
                /* may have already been flushed and freed */
            netbuf = NULL;
            break;
        }
        a_mem_trace(netbuf);

        if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM)
           hif_pm_runtime_put(target->hif_dev);

        if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_BUNDLED){
            HTC_PACKET      *pPacketTemp;
            HTC_PACKET_QUEUE *pQueueSave = (HTC_PACKET_QUEUE *)pPacket->pContext;
            HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pQueueSave, pPacketTemp){
                pPacket->Status = A_OK;
                SendPacketCompletion(target,pPacketTemp);
            }HTC_PACKET_QUEUE_ITERATE_END;
            FreeHTCBundleTxPacket(target, pPacket);

#ifdef HIF_USB
            if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
                HTCTrySend(target,pEndpoint,NULL);
            }
#endif
            return A_OK;
        }
            /* will be giving this buffer back to upper layers */
        netbuf = NULL;
        pPacket->Status = A_OK;
        SendPacketCompletion(target,pPacket);

    } while (FALSE);

    if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
        /* note: when using TX credit flow, the re-checking of queues happens
         * when credits flow back from the target.
         * in the non-TX credit case, we recheck after the packet completes */
        HTCTrySend(target,pEndpoint,NULL);
    }

    return A_OK;
}

    /* callback when TX resources become available */
void  HTCTxResourceAvailHandler(void *context, A_UINT8 pipeID)
{
    int             i;
    HTC_TARGET      *target = (HTC_TARGET *)context;
    HTC_ENDPOINT    *pEndpoint = NULL;

    for (i = 0; i < ENDPOINT_MAX; i++) {
        pEndpoint = &target->EndPoint[i];
        if (pEndpoint->ServiceID != 0) {
            if (pEndpoint->UL_PipeID == pipeID) {
                break;
            }
        }
    }

    if (i >= ENDPOINT_MAX) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid pipe indicated for TX resource avail : %d!\n",pipeID));
        return;
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("HIF indicated more resources for pipe:%d \n",pipeID));

    HTCTrySend(target,pEndpoint,NULL);
}

void HTCTxResumeAllHandler(void *context)
{
	int             i;
	HTC_TARGET      *target = (HTC_TARGET *)context;
	HTC_ENDPOINT    *pEndpoint = NULL;

	for (i = 0; i < ENDPOINT_MAX; i++) {
		pEndpoint = &target->EndPoint[i];

		if (pEndpoint->ServiceID == 0)
			continue;

		if (pEndpoint->EpCallBacks.EpResumeTxQueue)
			pEndpoint->EpCallBacks.EpResumeTxQueue
					(pEndpoint->EpCallBacks.pContext);

		HTCTrySend(target, pEndpoint, NULL);
	}
}

/* flush endpoint TX queue */
void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag)
{
    HTC_PACKET *pPacket;

    LOCK_HTC_TX(target);
    while(HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)){
        pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue);

	if (pPacket) {
		/* let the sender know the packet was not delivered */
		pPacket->Status = A_ECANCELED;
		SendPacketCompletion(target, pPacket);
	}
    }
    UNLOCK_HTC_TX(target);
}

/* HTC API to flush an endpoint's TX queue*/
void HTCFlushEndpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, HTC_TX_TAG Tag)
{
    HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT    *pEndpoint = &target->EndPoint[Endpoint];

    if (pEndpoint->ServiceID == 0) {
        AR_DEBUG_ASSERT(FALSE);
        /* not in use.. */
        return;
    }

    HTCFlushEndpointTX(target,pEndpoint,Tag);
}

/* HTC API to indicate activity to the credit distribution function */
void HTCIndicateActivityChange(HTC_HANDLE      HTCHandle,
                               HTC_ENDPOINT_ID Endpoint,
                               A_BOOL          Active)
{
   /*  TODO */
}

A_BOOL HTCIsEndpointActive(HTC_HANDLE      HTCHandle,
                           HTC_ENDPOINT_ID Endpoint)
{
    return TRUE;
}

void HTCSetNodropPkt(HTC_HANDLE HTCHandle, A_BOOL isNodropPkt)
{
    HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    target->is_nodrop_pkt = isNodropPkt;
}

/* process credit reports and call distribution function */
void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
{
    int             i;
    HTC_ENDPOINT    *pEndpoint;
    int             totalCredits = 0;
    A_UINT8         rpt_credits, rpt_ep_id;

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries));

        /* lock out TX while we update credits */
    LOCK_HTC_TX(target);


    for (i = 0; i < NumEntries; i++, pRpt++) {

        rpt_ep_id = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, ENDPOINTID);

        if (rpt_ep_id >= ENDPOINT_MAX) {
            AR_DEBUG_ASSERT(FALSE);
            break;
        }

        rpt_credits = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, CREDITS);

        pEndpoint = &target->EndPoint[rpt_ep_id];
#if DEBUG_CREDIT
        if (ep_debug_mask & (1 << pEndpoint->Id)){
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" <HTC> Increase EP%d %d + %d = %d credits\n",
                    rpt_ep_id, pEndpoint->TxCredits, rpt_credits, pEndpoint->TxCredits + rpt_credits));
        }
#endif

#ifdef HTC_EP_STAT_PROFILING

        INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1);
        INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, rpt_credits);

        if (FromEndpoint == rpt_ep_id) {
                /* this credit report arrived on the same endpoint indicating it arrived in an RX
                 * packet */
            INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, rpt_credits);
            INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1);
        } else if (FromEndpoint == ENDPOINT_0) {
                /* this credit arrived on endpoint 0 as a NULL message */
            INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, rpt_credits);
            INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1);
        } else {
                /* arrived on another endpoint */
            INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, rpt_credits);
            INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1);
        }

#endif
        pEndpoint->TxCredits += rpt_credits;

        if (pEndpoint->ServiceID == WMI_CONTROL_SVC) {
            LOCK_HTC_CREDIT(target);
            htc_credit_record(HTC_PROCESS_CREDIT_REPORT, pEndpoint->TxCredits,
                              HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue));
            UNLOCK_HTC_CREDIT(target);
        }

        if (pEndpoint->TxCredits && HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) {
            UNLOCK_HTC_TX(target);
#ifdef ATH_11AC_TXCOMPACT
            HTCTrySend(target,pEndpoint,NULL);
#else
            if (pEndpoint->ServiceID == HTT_DATA_MSG_SVC){
                HTCSendDataPkt(target, NULL, 0);
            } else {
                HTCTrySend(target,pEndpoint,NULL);
            }
#endif
            LOCK_HTC_TX(target);
        }
        totalCredits += rpt_credits;
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("  Report indicated %d credits to distribute \n", totalCredits));

    UNLOCK_HTC_TX(target);

    AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n"));
}

/* function to fetch stats from htc layer*/
struct ol_ath_htc_stats *ieee80211_ioctl_get_htc_stats(HTC_HANDLE HTCHandle)
{
    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);

    return(&(target->htc_pkt_stats));
}
