//------------------------------------------------------------------------------
// <copyright file="htc_recv.c" company="Atheros">
//    Copyright (c) 2007-2008 Atheros Corporation.  All rights reserved.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation;
//
// Software distributed under the License is distributed on an "AS
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
// implied. See the License for the specific language governing
// rights and limitations under the License.
//
//
//------------------------------------------------------------------------------
//==============================================================================
// Author(s): ="Atheros"
//==============================================================================
#include "htc_internal.h"

#define HTCIssueRecv(t, p) \
    DevRecvPacket(&(t)->Device,  \
                  (p),          \
                  (p)->ActualLength)

#define DO_RCV_COMPLETION(e,q)  DoRecvCompletion(e,q)

#define DUMP_RECV_PKT_INFO(pP) \
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC RECV packet 0x%X (%d bytes) (hdr:0x%X) on ep : %d \n", \
                        (A_UINT32)(pP),                        \
                        (pP)->ActualLength,                    \
                        (pP)->PktInfo.AsRx.ExpectedHdr,        \
                        (pP)->Endpoint))                         
                        
#ifdef HTC_EP_STAT_PROFILING
#define HTC_RX_STAT_PROFILE(t,ep,numLookAheads)        \
{                                                      \
    INC_HTC_EP_STAT((ep), RxReceived, 1);              \
    if ((numLookAheads) == 1) {                        \
        INC_HTC_EP_STAT((ep), RxLookAheads, 1);        \
    } else if ((numLookAheads) > 1) {                  \
        INC_HTC_EP_STAT((ep), RxBundleLookAheads, 1);  \
    }                                                  \
}
#else
#define HTC_RX_STAT_PROFILE(t,ep,lookAhead)
#endif

static void DoRecvCompletion(HTC_ENDPOINT     *pEndpoint,
                             HTC_PACKET_QUEUE *pQueueToIndicate)
{           
    
    do {
        
        if (HTC_QUEUE_EMPTY(pQueueToIndicate)) {
                /* nothing to indicate */
            break;    
        }
 
        if (pEndpoint->EpCallBacks.EpRecvPktMultiple != NULL) {    
            AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC calling ep %d, recv multiple callback (%d pkts) \n",
                     pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate)));
                /* a recv multiple handler is being used, pass the queue to the handler */                             
            pEndpoint->EpCallBacks.EpRecvPktMultiple(pEndpoint->EpCallBacks.pContext,
                                                     pQueueToIndicate);
            INIT_HTC_PACKET_QUEUE(pQueueToIndicate);        
        } else {
            HTC_PACKET *pPacket;  
            /* using legacy EpRecv */         
            do {
                pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
                AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC calling ep %d recv callback on packet 0x%X \n", \
                        pEndpoint->Id, (A_UINT32)(pPacket)));
                pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext, pPacket);                                              
            } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));                                              
        }
        
    } while (FALSE);

}

static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target,
                                         A_UINT8    *pBuffer,
                                         int         Length,
                                         A_UINT32   *pNextLookAheads,
                                         int        *pNumLookAheads,
                                         HTC_ENDPOINT_ID FromEndpoint)
{
    HTC_RECORD_HDR          *pRecord;
    A_UINT8                 *pRecordBuf;
    HTC_LOOKAHEAD_REPORT    *pLookAhead;
    A_UINT8                 *pOrigBuffer;
    int                     origLength;
    A_STATUS                status;

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (length:%d) \n", Length));

    if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
        AR_DEBUG_PRINTBUF(pBuffer,Length,"Recv Trailer");
    }

    pOrigBuffer = pBuffer;
    origLength = Length;
    status = A_OK;
    
    while (Length > 0) {

        if (Length < sizeof(HTC_RECORD_HDR)) {
            status = A_EPROTO;
            break;
        }
            /* these are byte aligned structs */
        pRecord = (HTC_RECORD_HDR *)pBuffer;
        Length -= sizeof(HTC_RECORD_HDR);
        pBuffer += sizeof(HTC_RECORD_HDR);

        if (pRecord->Length > Length) {
                /* no room left in buffer for record */
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                (" invalid record length: %d (id:%d) buffer has: %d bytes left \n",
                        pRecord->Length, pRecord->RecordID, Length));
            status = A_EPROTO;
            break;
        }
            /* start of record follows the header */
        pRecordBuf = pBuffer;

        switch (pRecord->RecordID) {
            case HTC_RECORD_CREDITS:
                AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_CREDIT_REPORT));
                HTCProcessCreditRpt(target,
                                    (HTC_CREDIT_REPORT *)pRecordBuf,
                                    pRecord->Length / (sizeof(HTC_CREDIT_REPORT)),
                                    FromEndpoint);
                break;
            case HTC_RECORD_LOOKAHEAD:
                AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_LOOKAHEAD_REPORT));
                pLookAhead = (HTC_LOOKAHEAD_REPORT *)pRecordBuf;
                if ((pLookAhead->PreValid == ((~pLookAhead->PostValid) & 0xFF)) &&
                    (pNextLookAheads != NULL)) {

                    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                                (" LookAhead Report Found (pre valid:0x%X, post valid:0x%X) \n",
                                pLookAhead->PreValid,
                                pLookAhead->PostValid));

                        /* look ahead bytes are valid, copy them over */
                    ((A_UINT8 *)(&pNextLookAheads[0]))[0] = pLookAhead->LookAhead[0];
                    ((A_UINT8 *)(&pNextLookAheads[0]))[1] = pLookAhead->LookAhead[1];
                    ((A_UINT8 *)(&pNextLookAheads[0]))[2] = pLookAhead->LookAhead[2];
                    ((A_UINT8 *)(&pNextLookAheads[0]))[3] = pLookAhead->LookAhead[3];

                    if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
                        DebugDumpBytes((A_UINT8 *)pNextLookAheads,4,"Next Look Ahead");
                    }
                        /* just one normal lookahead */
                    *pNumLookAheads = 1;
                }
                break;
            case HTC_RECORD_LOOKAHEAD_BUNDLE:
                AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT));
                if (pRecord->Length >= sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT) &&
                    (pNextLookAheads != NULL)) {                   
                    HTC_BUNDLED_LOOKAHEAD_REPORT    *pBundledLookAheadRpt;
                    int                             i;
                    
                    pBundledLookAheadRpt = (HTC_BUNDLED_LOOKAHEAD_REPORT *)pRecordBuf;
                    
                    if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
                        DebugDumpBytes(pRecordBuf,pRecord->Length,"Bundle LookAhead");
                    }
                    
                    if ((pRecord->Length / (sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT))) >
                            HTC_HOST_MAX_MSG_PER_BUNDLE) {
                            /* this should never happen, the target restricts the number
                             * of messages per bundle configured by the host */        
                        A_ASSERT(FALSE);
                        status = A_EPROTO;
                        break;        
                    }
                                         
                    for (i = 0; i < (int)(pRecord->Length / (sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT))); i++) {
                        ((A_UINT8 *)(&pNextLookAheads[i]))[0] = pBundledLookAheadRpt->LookAhead[0];
                        ((A_UINT8 *)(&pNextLookAheads[i]))[1] = pBundledLookAheadRpt->LookAhead[1];
                        ((A_UINT8 *)(&pNextLookAheads[i]))[2] = pBundledLookAheadRpt->LookAhead[2];
                        ((A_UINT8 *)(&pNextLookAheads[i]))[3] = pBundledLookAheadRpt->LookAhead[3];
                        pBundledLookAheadRpt++;
                    }
                    
                    *pNumLookAheads = i;
                }               
                break;
            default:
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" unhandled record: id:%d length:%d \n",
                        pRecord->RecordID, pRecord->Length));
                break;
        }

        if (A_FAILED(status)) {
            break;
        }

            /* advance buffer past this record for next time around */
        pBuffer += pRecord->Length;
        Length -= pRecord->Length;
    }

    if (A_FAILED(status)) {
        DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer");
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n"));
    return status;

}

/* process a received message (i.e. strip off header, process any trailer data)
 * note : locks must be released when this function is called */
static A_STATUS HTCProcessRecvHeader(HTC_TARGET *target, 
                                     HTC_PACKET *pPacket, 
                                     A_UINT32   *pNextLookAheads, 
                                     int        *pNumLookAheads)
{
    A_UINT8   temp;
    A_UINT8   *pBuf;
    A_STATUS  status = A_OK;
    A_UINT16  payloadLen;
    A_UINT32  lookAhead;

    pBuf = pPacket->pBuffer;
    
    if (pNumLookAheads != NULL) {
        *pNumLookAheads = 0;
    }
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader \n"));

    if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
        AR_DEBUG_PRINTBUF(pBuf,pPacket->ActualLength,"HTC Recv PKT");
    }

    do {
        /* note, we cannot assume the alignment of pBuffer, so we use the safe macros to
         * retrieve 16 bit fields */
        payloadLen = A_GET_UINT16_FIELD(pBuf, HTC_FRAME_HDR, PayloadLen);
        
        ((A_UINT8 *)&lookAhead)[0] = pBuf[0];
        ((A_UINT8 *)&lookAhead)[1] = pBuf[1];
        ((A_UINT8 *)&lookAhead)[2] = pBuf[2];
        ((A_UINT8 *)&lookAhead)[3] = pBuf[3];

        if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_REFRESH_HDR) {
                /* refresh expected hdr, since this was unknown at the time we grabbed the packets
                 * as part of a bundle */
            pPacket->PktInfo.AsRx.ExpectedHdr = lookAhead;
                /* refresh actual length since we now have the real header */
            pPacket->ActualLength = payloadLen + HTC_HDR_LENGTH;
            
                /* validate the actual header that was refreshed  */ 
            if (pPacket->ActualLength > pPacket->BufferLength) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("Refreshed HDR payload length (%d) in bundled RECV is invalid (hdr: 0x%X) \n", 
                    payloadLen, lookAhead));
                    /* limit this to max buffer just to print out some of the buffer */    
                pPacket->ActualLength = min(pPacket->ActualLength, pPacket->BufferLength);
                status = A_EPROTO;
                break;    
            }
            
            if (pPacket->Endpoint != A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, EndpointID)) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("Refreshed HDR endpoint (%d) does not match expected endpoint (%d) \n", 
                    A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, EndpointID), pPacket->Endpoint));
                status = A_EPROTO;
                break;      
            }   
        }
                
        if (lookAhead != pPacket->PktInfo.AsRx.ExpectedHdr) {
            /* somehow the lookahead that gave us the full read length did not
             * reflect the actual header in the pending message */
             AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("HTCProcessRecvHeader, lookahead mismatch! (pPkt:0x%X flags:0x%X) \n", 
                        (A_UINT32)pPacket, pPacket->PktInfo.AsRx.HTCRxFlags));
             DebugDumpBytes((A_UINT8 *)&pPacket->PktInfo.AsRx.ExpectedHdr,4,"Expected Message LookAhead");
             DebugDumpBytes(pBuf,sizeof(HTC_FRAME_HDR),"Current Frame Header");
#ifdef HTC_CAPTURE_LAST_FRAME
            DebugDumpBytes((A_UINT8 *)&target->LastFrameHdr,sizeof(HTC_FRAME_HDR),"Last Frame Header");
            if (target->LastTrailerLength != 0) {
                DebugDumpBytes(target->LastTrailer,
                               target->LastTrailerLength,
                               "Last trailer");
            }
#endif
            status = A_EPROTO;
            break;
        }

            /* get flags */
        temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, Flags);

        if (temp & HTC_FLAGS_RECV_TRAILER) {
            /* this packet has a trailer */

                /* extract the trailer length in control byte 0 */
            temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, ControlBytes[0]);

            if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("HTCProcessRecvHeader, invalid header (payloadlength should be :%d, CB[0] is:%d) \n",
                        payloadLen, temp));
                status = A_EPROTO;
                break;
            }

            if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
                    /* this packet was fetched as part of an HTC bundle, the embedded lookahead is
                     * not valid since the next packet may have already been fetched as part of the
                     * bundle */
                pNextLookAheads = NULL;   
                pNumLookAheads = NULL;     
            }
            
                /* process trailer data that follows HDR + application payload */
            status = HTCProcessTrailer(target,
                                       (pBuf + HTC_HDR_LENGTH + payloadLen - temp),
                                       temp,
                                       pNextLookAheads,
                                       pNumLookAheads,
                                       pPacket->Endpoint);

            if (A_FAILED(status)) {
                break;
            }

#ifdef HTC_CAPTURE_LAST_FRAME
            A_MEMCPY(target->LastTrailer, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp);
            target->LastTrailerLength = temp;
#endif
                /* trim length by trailer bytes */
            pPacket->ActualLength -= temp;
        }
#ifdef HTC_CAPTURE_LAST_FRAME
         else {
            target->LastTrailerLength = 0;
        }
#endif

            /* if we get to this point, the packet is good */
            /* remove header and adjust length */
        pPacket->pBuffer += HTC_HDR_LENGTH;
        pPacket->ActualLength -= HTC_HDR_LENGTH;

    } while (FALSE);

    if (A_FAILED(status)) {
            /* dump the whole packet */
        DebugDumpBytes(pBuf,pPacket->ActualLength < 256 ? pPacket->ActualLength : 256 ,"BAD HTC Recv PKT");
    } else {
#ifdef HTC_CAPTURE_LAST_FRAME
        A_MEMCPY(&target->LastFrameHdr,pBuf,sizeof(HTC_FRAME_HDR));
#endif
        if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
            if (pPacket->ActualLength > 0) {
                AR_DEBUG_PRINTBUF(pPacket->pBuffer,pPacket->ActualLength,"HTC - Application Msg");
            }
        }
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessRecvHeader \n"));
    return status;
}

static INLINE void HTCAsyncRecvCheckMorePackets(HTC_TARGET  *target, 
                                                A_UINT32    NextLookAheads[], 
                                                int         NumLookAheads,
                                                A_BOOL      CheckMoreMsgs)
{
        /* was there a lookahead for the next packet? */
    if (NumLookAheads > 0) {
        A_STATUS nextStatus;
        int      fetched = 0;
        AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                        ("HTCAsyncRecvCheckMorePackets - num lookaheads were non-zero : %d \n",
                         NumLookAheads));
            /* force status re-check */                    
        REF_IRQ_STATUS_RECHECK(&target->Device);
            /* we have more packets, get the next packet fetch started */
        nextStatus = HTCRecvMessagePendingHandler(target, NextLookAheads, NumLookAheads, NULL, &fetched);
        if (A_EPROTO == nextStatus) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                        ("Next look ahead from recv header was INVALID\n"));
            DebugDumpBytes((A_UINT8 *)NextLookAheads,
                            NumLookAheads * (sizeof(A_UINT32)),
                            "BAD lookaheads from lookahead report");
        }
        if (A_SUCCESS(nextStatus) && !fetched) {
                /* we could not fetch any more packets due to resources */
            DevAsyncIrqProcessComplete(&target->Device);        
        }
    } else {
        if (CheckMoreMsgs) {
            AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                ("HTCAsyncRecvCheckMorePackets - rechecking for more messages...\n"));
            /* if we did not get anything on the look-ahead,
             * call device layer to asynchronously re-check for messages. If we can keep the async
             * processing going we get better performance.  If there is a pending message we will keep processing
             * messages asynchronously which should pipeline things nicely */
            DevCheckPendingRecvMsgsAsync(&target->Device);
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("HTCAsyncRecvCheckMorePackets - no check \n"));    
        }
    }
    
     
}      

    /* unload the recv completion queue */
static INLINE void DrainRecvIndicationQueue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
{
    HTC_PACKET_QUEUE     recvCompletions;
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+DrainRecvIndicationQueue \n"));
                
    INIT_HTC_PACKET_QUEUE(&recvCompletions);
    
    LOCK_HTC_RX(target);
    
            /* increment rx processing count on entry */    
    pEndpoint->RxProcessCount++;
    if (pEndpoint->RxProcessCount > 1) {
         pEndpoint->RxProcessCount--;
            /* another thread or task is draining the RX completion queue on this endpoint
             * that thread will reset the rx processing count when the queue is drained */
         UNLOCK_HTC_RX(target);
         return;
    }
    
    /******* at this point only 1 thread may enter ******/
     
    while (TRUE) { 
                
            /* transfer items from main recv queue to the local one so we can release the lock */ 
        HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&recvCompletions, &pEndpoint->RecvIndicationQueue);
            
        if (HTC_QUEUE_EMPTY(&recvCompletions)) {
                /* all drained */
            break;    
        }
        
            /* release lock while we do the recv completions 
             * other threads can now queue more recv completions */
        UNLOCK_HTC_RX(target);
        
        AR_DEBUG_PRINTF(ATH_DEBUG_RECV, 
                ("DrainRecvIndicationQueue : completing %d RECV packets \n",
                                        HTC_PACKET_QUEUE_DEPTH(&recvCompletions)));
            /* do completion */
        DO_RCV_COMPLETION(pEndpoint,&recvCompletions);     
              
            /* re-acquire lock to grab some more completions */
        LOCK_HTC_RX(target);    
    }
    
        /* reset count */
    pEndpoint->RxProcessCount = 0;       
    UNLOCK_HTC_RX(target);
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-DrainRecvIndicationQueue \n"));
  
}

    /* optimization for recv packets, we can indicate a "hint" that there are more
     * single-packets to fetch on this endpoint */
#define SET_MORE_RX_PACKET_INDICATION_FLAG(L,N,E,P) \
    if ((N) > 0) { SetRxPacketIndicationFlags((L)[0],(E),(P)); }

    /* for bundled frames, we can force the flag to indicate there are more packets */
#define FORCE_MORE_RX_PACKET_INDICATION_FLAG(P) \
    (P)->PktInfo.AsRx.IndicationFlags |= HTC_RX_FLAGS_INDICATE_MORE_PKTS; 
   
   /* note: this function can be called with the RX lock held */     
static INLINE void SetRxPacketIndicationFlags(A_UINT32      LookAhead, 
                                              HTC_ENDPOINT  *pEndpoint, 
                                              HTC_PACKET    *pPacket)
{
    HTC_FRAME_HDR *pHdr = (HTC_FRAME_HDR *)&LookAhead;
        /* check to see if the "next" packet is from the same endpoint of the
           completing packet */
    if (pHdr->EndpointID == pPacket->Endpoint) {
            /* check that there is a buffer available to actually fetch it */
        if (!HTC_QUEUE_EMPTY(&pEndpoint->RxBuffers)) {                        
                /* provide a hint that there are more RX packets to fetch */
            FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);        
        }             
    }                  
}

     
/* asynchronous completion handler for recv packet fetching, when the device layer
 * completes a read request, it will call this completion handler */
void HTCRecvCompleteHandler(void *Context, HTC_PACKET *pPacket)
{
    HTC_TARGET      *target = (HTC_TARGET *)Context;
    HTC_ENDPOINT    *pEndpoint;
    A_UINT32        nextLookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
    int             numLookAheads = 0;
    A_STATUS        status;
    A_BOOL          checkMorePkts = TRUE;

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (pkt:0x%X, status:%d, ep:%d) \n",
                (A_UINT32)pPacket, pPacket->Status, pPacket->Endpoint));

    A_ASSERT(!IS_DEV_IRQ_PROC_SYNC_MODE(&target->Device));
    AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
    pEndpoint = &target->EndPoint[pPacket->Endpoint];
    pPacket->Completion = NULL;

        /* get completion status */
    status = pPacket->Status;

    do {
        
        if (A_FAILED(status)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCRecvCompleteHandler: request failed (status:%d, ep:%d) \n",
                pPacket->Status, pPacket->Endpoint));
            break;
        }
            /* process the header for any trailer data */
        status = HTCProcessRecvHeader(target,pPacket,nextLookAheads,&numLookAheads);

        if (A_FAILED(status)) {
            break;
        }
        
        if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
                /* this packet was part of a bundle that had to be broken up. 
                 * It was fetched one message at a time.  There may be other asynchronous reads queued behind this one.
                 * Do no issue another check for more packets since the last one in the series of requests
                 * will handle it */
            checkMorePkts = FALSE;    
        }
          
        DUMP_RECV_PKT_INFO(pPacket);    
        LOCK_HTC_RX(target);
        SET_MORE_RX_PACKET_INDICATION_FLAG(nextLookAheads,numLookAheads,pEndpoint,pPacket);
            /* we have a good packet, queue it to the completion queue */
        HTC_PACKET_ENQUEUE(&pEndpoint->RecvIndicationQueue,pPacket);
        HTC_RX_STAT_PROFILE(target,pEndpoint,numLookAheads);
        UNLOCK_HTC_RX(target);     
       
            /* check for more recv packets before indicating */
        HTCAsyncRecvCheckMorePackets(target,nextLookAheads,numLookAheads,checkMorePkts);

    } while (FALSE);

    if (A_FAILED(status)) {
         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                         ("HTCRecvCompleteHandler , message fetch failed (status = %d) \n",
                         status));
            /* recycle this packet */
        HTC_RECYCLE_RX_PKT(target, pPacket, pEndpoint);
    } else {
            /* a good packet was queued, drain the queue */
        DrainRecvIndicationQueue(target,pEndpoint);     
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCRecvCompleteHandler\n"));
}

/* synchronously wait for a control message from the target,
 * This function is used at initialization time ONLY.  At init messages
 * on ENDPOINT 0 are expected. */
A_STATUS HTCWaitforControlMessage(HTC_TARGET *target, HTC_PACKET **ppControlPacket)
{
    A_STATUS        status;
    A_UINT32        lookAhead;
    HTC_PACKET      *pPacket = NULL;
    HTC_FRAME_HDR   *pHdr;

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCWaitforControlMessage \n"));

    do  {

        *ppControlPacket = NULL;

            /* call the polling function to see if we have a message */
        status = DevPollMboxMsgRecv(&target->Device,
                                    &lookAhead,
                                    HTC_TARGET_RESPONSE_TIMEOUT);

        if (A_FAILED(status)) {
            break;
        }

        AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                ("HTCWaitforControlMessage : lookAhead : 0x%X \n", lookAhead));

            /* check the lookahead */
        pHdr = (HTC_FRAME_HDR *)&lookAhead;

        if (pHdr->EndpointID != ENDPOINT_0) {
                /* unexpected endpoint number, should be zero */
            AR_DEBUG_ASSERT(FALSE);
            status = A_EPROTO;
            break;
        }

        if (A_FAILED(status)) {
                /* bad message */
            AR_DEBUG_ASSERT(FALSE);
            status = A_EPROTO;
            break;
        }

        pPacket = HTC_ALLOC_CONTROL_RX(target);

        if (pPacket == NULL) {
            AR_DEBUG_ASSERT(FALSE);
            status = A_NO_MEMORY;
            break;
        }
        
        pPacket->PktInfo.AsRx.HTCRxFlags = 0;
        pPacket->PktInfo.AsRx.ExpectedHdr = lookAhead;
        pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;

        if (pPacket->ActualLength > pPacket->BufferLength) {
            AR_DEBUG_ASSERT(FALSE);
            status = A_EPROTO;
            break;
        }

            /* we want synchronous operation */
        pPacket->Completion = NULL;

            /* get the message from the device, this will block */
        status = HTCIssueRecv(target, pPacket);

        if (A_FAILED(status)) {
            break;
        }

            /* process receive header */
        status = HTCProcessRecvHeader(target,pPacket,NULL,NULL);

        pPacket->Status = status;

        if (A_FAILED(status)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                    ("HTCWaitforControlMessage, HTCProcessRecvHeader failed (status = %d) \n",
                     status));
            break;
        }

            /* give the caller this control message packet, they are responsible to free */
        *ppControlPacket = pPacket;

    } while (FALSE);

    if (A_FAILED(status)) {
        if (pPacket != NULL) {
                /* cleanup buffer on error */
            HTC_FREE_CONTROL_RX(target,pPacket);
        }
    }

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCWaitforControlMessage \n"));

    return status;
}

static A_STATUS AllocAndPrepareRxPackets(HTC_TARGET       *target, 
                                         A_UINT32         LookAheads[], 
                                         int              Messages,                                        
                                         HTC_ENDPOINT     *pEndpoint, 
                                         HTC_PACKET_QUEUE *pQueue)
{
    A_STATUS         status = A_OK;
    HTC_PACKET      *pPacket;
    HTC_FRAME_HDR   *pHdr;
    int              i,j;
    int              numMessages;
    int              fullLength;
    A_BOOL           noRecycle;
            
        /* lock RX while we assemble the packet buffers */
    LOCK_HTC_RX(target);
                        
    for (i = 0; i < Messages; i++) {   
         
        pHdr = (HTC_FRAME_HDR *)&LookAheads[i];

        if (pHdr->EndpointID >= ENDPOINT_MAX) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Endpoint in look-ahead: %d \n",pHdr->EndpointID));
                /* invalid endpoint */
            status = A_EPROTO;
            break;
        }

        if (pHdr->EndpointID != pEndpoint->Id) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Endpoint in look-ahead: %d should be : %d (index:%d)\n",
                pHdr->EndpointID, pEndpoint->Id, i));
                /* invalid endpoint */
            status = A_EPROTO;
            break;    
        }    
       
        if (pHdr->PayloadLen > HTC_MAX_PAYLOAD_LENGTH) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Payload length %d exceeds max HTC : %d !\n",
                    pHdr->PayloadLen, HTC_MAX_PAYLOAD_LENGTH));
            status = A_EPROTO;
            break;
        }

        if (0 == pEndpoint->ServiceID) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Endpoint %d is not connected !\n",pHdr->EndpointID));
                /* endpoint isn't even connected */
            status = A_EPROTO;
            break;
        }

        if ((pHdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK) == 0) {
                /* HTC header only indicates 1 message to fetch */
            numMessages = 1;
        } else {
                /* HTC header indicates that every packet to follow has the same padded length so that it can
                 * be optimally fetched as a full bundle */
            numMessages = (pHdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK) >> HTC_FLAGS_RECV_BUNDLE_CNT_SHIFT;
                /* the count doesn't include the starter frame, just a count of frames to follow */
            numMessages++;
            A_ASSERT(numMessages <= target->MaxMsgPerBundle);          
            INC_HTC_EP_STAT(pEndpoint, RxBundleIndFromHdr, 1);
            AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                ("HTC header indicates :%d messages can be fetched as a bundle \n",numMessages));           
        }
     
        fullLength = DEV_CALC_RECV_PADDED_LEN(&target->Device,pHdr->PayloadLen + sizeof(HTC_FRAME_HDR));
            
            /* get packet buffers for each message, if there was a bundle detected in the header,
             * use pHdr as a template to fetch all packets in the bundle */        
        for (j = 0; j < numMessages; j++) {  
            
                /* reset flag, any packets allocated using the RecvAlloc() API cannot be recycled on cleanup,
                 * they must be explicitly returned */
            noRecycle = FALSE;
                                                                                   
            if (pEndpoint->EpCallBacks.EpRecvAlloc != NULL) {
                UNLOCK_HTC_RX(target);
                noRecycle = TRUE;
                    /* user is using a per-packet allocation callback */
                pPacket = pEndpoint->EpCallBacks.EpRecvAlloc(pEndpoint->EpCallBacks.pContext,
                                                             pEndpoint->Id,
                                                             fullLength);
                LOCK_HTC_RX(target);
    
            } else if ((pEndpoint->EpCallBacks.EpRecvAllocThresh != NULL) &&
                       (fullLength > pEndpoint->EpCallBacks.RecvAllocThreshold)) { 
                INC_HTC_EP_STAT(pEndpoint,RxAllocThreshHit,1);
                INC_HTC_EP_STAT(pEndpoint,RxAllocThreshBytes,pHdr->PayloadLen);                
                    /* threshold was hit, call the special recv allocation callback */        
                UNLOCK_HTC_RX(target);
                noRecycle = TRUE;
                    /* user wants to allocate packets above a certain threshold */
                pPacket = pEndpoint->EpCallBacks.EpRecvAllocThresh(pEndpoint->EpCallBacks.pContext,
                                                                   pEndpoint->Id,
                                                                   fullLength);
                LOCK_HTC_RX(target);        
                        
            } else {
                    /* user is using a refill handler that can refill multiple HTC buffers */
                    
                    /* get a packet from the endpoint recv queue */
                pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
    
                if (NULL == pPacket) {
                        /* check for refill handler */
                    if (pEndpoint->EpCallBacks.EpRecvRefill != NULL) {
                        UNLOCK_HTC_RX(target);
                            /* call the re-fill handler */
                        pEndpoint->EpCallBacks.EpRecvRefill(pEndpoint->EpCallBacks.pContext,
                                                            pEndpoint->Id);
                        LOCK_HTC_RX(target);
                            /* check if we have more buffers */
                        pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
                            /* fall through */
                    }
                }
            }
    
            if (NULL == pPacket) {
                    /* this is not an error, we simply need to mark that we are waiting for buffers.*/
                target->RecvStateFlags |= HTC_RECV_WAIT_BUFFERS;
                target->EpWaitingForBuffers = pEndpoint->Id;
                status = A_NO_RESOURCE;
                break;
            }
                             
            AR_DEBUG_ASSERT(pPacket->Endpoint == pEndpoint->Id);
                /* clear flags */
            pPacket->PktInfo.AsRx.HTCRxFlags = 0;
            pPacket->PktInfo.AsRx.IndicationFlags = 0;
            pPacket->Status = A_OK;
            
            if (noRecycle) {
                    /* flag that these packets cannot be recycled, they have to be returned to the 
                     * user */
                pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_NO_RECYCLE; 
            }
                /* add packet to queue (also incase we need to cleanup down below)  */
            HTC_PACKET_ENQUEUE(pQueue,pPacket);
            
            if (HTC_STOPPING(target)) {
                status = A_ECANCELED;
                break;
            }
    
                /* make sure this message can fit in the endpoint buffer */
            if ((A_UINT32)fullLength > pPacket->BufferLength) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                        ("Payload Length Error : header reports payload of: %d (%d) endpoint buffer size: %d \n",
                        pHdr->PayloadLen, fullLength, pPacket->BufferLength));
                status = A_EPROTO;
                break;
            }
            
            if (j > 0) {
                    /* for messages fetched in a bundle the expected lookahead is unknown since we
                     * are only using the lookahead of the first packet as a template of what to
                     * expect for lengths */
                    /* flag that once we get the real HTC header we need to refesh the information */     
                pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_REFRESH_HDR;
                    /* set it to something invalid */
                pPacket->PktInfo.AsRx.ExpectedHdr = 0xFFFFFFFF;    
            } else {
            
                pPacket->PktInfo.AsRx.ExpectedHdr = LookAheads[i]; /* set expected look ahead */
            }
                /* set the amount of data to fetch */
            pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
        }
        
        if (A_FAILED(status)) {
            if (A_NO_RESOURCE == status) {
                    /* this is actually okay */
                status = A_OK;    
            }
            break;    
        }
                
    }
    
    UNLOCK_HTC_RX(target);
    
    if (A_FAILED(status)) {
        while (!HTC_QUEUE_EMPTY(pQueue)) {
            pPacket = HTC_PACKET_DEQUEUE(pQueue);
                /* recycle all allocated packets */
            HTC_RECYCLE_RX_PKT(target,pPacket,&target->EndPoint[pPacket->Endpoint]);
        }        
    }
        
    return status; 
}

static void HTCAsyncRecvScatterCompletion(HIF_SCATTER_REQ *pScatterReq)
{
    int                 i;    
    HTC_PACKET          *pPacket;
    HTC_ENDPOINT        *pEndpoint;
    A_UINT32            lookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
    int                 numLookAheads = 0;
    HTC_TARGET          *target = (HTC_TARGET *)pScatterReq->Context;
    A_STATUS            status;
    A_BOOL              partialBundle = FALSE;
    HTC_PACKET_QUEUE    localRecvQueue;
    A_BOOL              procError = FALSE;
           
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCAsyncRecvScatterCompletion  TotLen: %d  Entries: %d\n",
        pScatterReq->TotalLength, pScatterReq->ValidScatterEntries));
    
    A_ASSERT(!IS_DEV_IRQ_PROC_SYNC_MODE(&target->Device));
           
    if (A_FAILED(pScatterReq->CompletionStatus)) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Recv Scatter Request Failed: %d \n",pScatterReq->CompletionStatus));            
    }
    
    if (pScatterReq->CallerFlags & HTC_SCATTER_REQ_FLAGS_PARTIAL_BUNDLE) {
        partialBundle = TRUE;    
    }
    
    DEV_FINISH_SCATTER_OPERATION(pScatterReq);
    
    INIT_HTC_PACKET_QUEUE(&localRecvQueue);
        
    pPacket = (HTC_PACKET *)pScatterReq->ScatterList[0].pCallerContexts[0];
        /* note: all packets in a scatter req are for the same endpoint ! */
    pEndpoint = &target->EndPoint[pPacket->Endpoint];
         
        /* walk through the scatter list and process */
        /* **** NOTE: DO NOT HOLD ANY LOCKS here, HTCProcessRecvHeader can take the TX lock
         * as it processes credit reports */
    for (i = 0; i < pScatterReq->ValidScatterEntries; i++) {
        pPacket = (HTC_PACKET *)pScatterReq->ScatterList[i].pCallerContexts[0];
        A_ASSERT(pPacket != NULL);       
            /* reset count, we are only interested in the look ahead in the last packet when we
             * break out of this loop */
        numLookAheads = 0;
        
        if (A_SUCCESS(pScatterReq->CompletionStatus)) {      
                /* process header for each of the recv packets */            
            status = HTCProcessRecvHeader(target,pPacket,lookAheads,&numLookAheads);
        } else {
            status = A_ERROR;    
        }
        
        if (A_SUCCESS(status)) {    
#ifdef HTC_EP_STAT_PROFILING
            LOCK_HTC_RX(target);              
            HTC_RX_STAT_PROFILE(target,pEndpoint,numLookAheads);
            INC_HTC_EP_STAT(pEndpoint, RxPacketsBundled, 1);
            UNLOCK_HTC_RX(target);
#endif      
            if (i == (pScatterReq->ValidScatterEntries - 1)) {
                    /* last packet's more packets flag is set based on the lookahead */
                SET_MORE_RX_PACKET_INDICATION_FLAG(lookAheads,numLookAheads,pEndpoint,pPacket);
            } else {
                    /* packets in a bundle automatically have this flag set */
                FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);
            }
             
            DUMP_RECV_PKT_INFO(pPacket);            
                /* since we can't hold a lock in this loop, we insert into our local recv queue for
                 * storage until we can transfer them to the recv completion queue */
            HTC_PACKET_ENQUEUE(&localRecvQueue,pPacket);
            
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" Recv packet scatter entry %d failed (out of %d) \n",
                    i, pScatterReq->ValidScatterEntries));
                /* recycle failed recv */
            HTC_RECYCLE_RX_PKT(target, pPacket, pEndpoint);
                /* set flag and continue processing the remaining scatter entries */
            procError = TRUE;
        }   
    
    }
  
        /* free scatter request */
    DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
   
    LOCK_HTC_RX(target);   
        /* transfer the packets in the local recv queue to the recv completion queue */
    HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RecvIndicationQueue, &localRecvQueue);  
    
    UNLOCK_HTC_RX(target);
    
    if (!procError) {  
            /* pipeline the next check (asynchronously) for more packets */           
        HTCAsyncRecvCheckMorePackets(target,
                                     lookAheads,
                                     numLookAheads,
                                     partialBundle ? FALSE : TRUE);
    }
    
        /* now drain the indication queue */
    DrainRecvIndicationQueue(target,pEndpoint);
          
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCAsyncRecvScatterCompletion \n"));
}

static A_STATUS HTCIssueRecvPacketBundle(HTC_TARGET        *target,
                                         HTC_PACKET_QUEUE  *pRecvPktQueue, 
                                         HTC_PACKET_QUEUE  *pSyncCompletionQueue,
                                         int               *pNumPacketsFetched,
                                         A_BOOL             PartialBundle)
{
    A_STATUS        status = A_OK;
    HIF_SCATTER_REQ *pScatterReq;
    int             i, totalLength;
    int             pktsToScatter;
    HTC_PACKET      *pPacket;
    A_BOOL          asyncMode = (pSyncCompletionQueue == NULL) ? TRUE : FALSE;
    int             scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_RECV_LENGTH(&target->Device);
        
    pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue);
    pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle);
        
    if ((HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue) - pktsToScatter) > 0) {
            /* we were forced to split this bundle receive operation
             * all packets in this partial bundle must have their lookaheads ignored */
        PartialBundle = TRUE;
            /* this would only happen if the target ignored our max bundle limit */
        AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
                         ("HTCIssueRecvPacketBundle : partial bundle detected num:%d , %d \n",
                         HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue), pktsToScatter));       
    }
    
    totalLength = 0;

    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCIssueRecvPacketBundle (Numpackets: %d , actual : %d) \n", 
        HTC_PACKET_QUEUE_DEPTH(pRecvPktQueue), pktsToScatter));
    
    do {
        
        pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device); 
        
        if (pScatterReq == NULL) {
                /* no scatter resources left, just let caller handle it the legacy way */
            break;    
        }        
    
        pScatterReq->CallerFlags = 0;
             
        if (PartialBundle) {
                /* mark that this is a partial bundle, this has special ramifications to the
                 * scatter completion routine */
            pScatterReq->CallerFlags |= HTC_SCATTER_REQ_FLAGS_PARTIAL_BUNDLE;
        }
                   
            /* convert HTC packets to scatter list */                   
        for (i = 0; i < pktsToScatter; i++) {
            int paddedLength;
            
            pPacket = HTC_PACKET_DEQUEUE(pRecvPktQueue);
            A_ASSERT(pPacket != NULL);
            
            paddedLength = DEV_CALC_RECV_PADDED_LEN(&target->Device, pPacket->ActualLength);
     
            if ((scatterSpaceRemaining - paddedLength) < 0) {
                    /* exceeds what we can transfer, put the packet back */  
                HTC_PACKET_ENQUEUE_TO_HEAD(pRecvPktQueue,pPacket);
                break;    
            }
                        
            scatterSpaceRemaining -= paddedLength;
                       
            if (PartialBundle || (i < (pktsToScatter - 1))) {
                    /* packet 0..n-1 cannot be checked for look-aheads since we are fetching a bundle
                     * the last packet however can have it's lookahead used */
                pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_IGNORE_LOOKAHEAD;
            }
            
            /* note: 1 HTC packet per scatter entry */           
                /* setup packet into */   
            pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer;
            pScatterReq->ScatterList[i].Length = paddedLength;
            
            pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_PART_OF_BUNDLE;
            
            if (asyncMode) {
                    /* save HTC packet for async completion routine */
                pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket;
            } else {
                    /* queue to caller's sync completion queue, caller will unload this when we return */
                HTC_PACKET_ENQUEUE(pSyncCompletionQueue,pPacket);    
            }             
                   
            A_ASSERT(pScatterReq->ScatterList[i].Length);
            totalLength += pScatterReq->ScatterList[i].Length;
        }            
        
        pScatterReq->TotalLength = totalLength;
        pScatterReq->ValidScatterEntries = i;
        
        if (asyncMode) {
            pScatterReq->CompletionRoutine = HTCAsyncRecvScatterCompletion;
            pScatterReq->Context = target;
        }
        
        status = DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_READ, asyncMode);
        
        if (A_SUCCESS(status)) {
            *pNumPacketsFetched = i;    
        }
        
        if (!asyncMode) {
                /* free scatter request */
            DEV_FREE_SCATTER_REQ(&target->Device, pScatterReq);   
        }
        
    } while (FALSE);
   
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCIssueRecvPacketBundle (status:%d) (fetched:%d) \n",
            status,*pNumPacketsFetched));
        
    return status;
}

static INLINE void CheckRecvWaterMark(HTC_ENDPOINT    *pEndpoint)
{  
        /* see if endpoint is using a refill watermark 
         * ** no need to use a lock here, since we are only inspecting...
         * caller may must not hold locks when calling this function */
    if (pEndpoint->EpCallBacks.RecvRefillWaterMark > 0) {
        if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->RxBuffers) < pEndpoint->EpCallBacks.RecvRefillWaterMark) {
                /* call the re-fill handler before we continue */
            pEndpoint->EpCallBacks.EpRecvRefill(pEndpoint->EpCallBacks.pContext,
                                                pEndpoint->Id);
        }
    }  
}

/* callback when device layer or lookahead report parsing detects a pending message */
A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 MsgLookAheads[], int NumLookAheads, A_BOOL *pAsyncProc, int *pNumPktsFetched)
{
    HTC_TARGET      *target = (HTC_TARGET *)Context;
    A_STATUS         status = A_OK;
    HTC_PACKET      *pPacket;
    HTC_ENDPOINT    *pEndpoint;
    A_BOOL          asyncProc = FALSE;
    A_UINT32        lookAheads[HTC_HOST_MAX_MSG_PER_BUNDLE];
    int             pktsFetched;
    HTC_PACKET_QUEUE recvPktQueue, syncCompletedPktsQueue;
    A_BOOL          partialBundle;
    HTC_ENDPOINT_ID id;
    int             totalFetched = 0;
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler NumLookAheads: %d \n",NumLookAheads));
    
    if (pNumPktsFetched != NULL) {
        *pNumPktsFetched = 0;    
    }
    
    if (IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(&target->Device)) {
            /* We use async mode to get the packets if the device layer supports it.
             * The device layer interfaces with HIF in which HIF may have restrictions on
             * how interrupts are processed */
        asyncProc = TRUE;
    }

    if (pAsyncProc != NULL) {
            /* indicate to caller how we decided to process this */
        *pAsyncProc = asyncProc;
    }
    
    if (NumLookAheads > HTC_HOST_MAX_MSG_PER_BUNDLE) {
        A_ASSERT(FALSE);
        return A_EPROTO; 
    }
        
        /* on first entry copy the lookaheads into our temp array for processing */
    A_MEMCPY(lookAheads, MsgLookAheads, (sizeof(A_UINT32)) * NumLookAheads);  
            
    while (TRUE) {
        
            /* reset packets queues */
        INIT_HTC_PACKET_QUEUE(&recvPktQueue);
        INIT_HTC_PACKET_QUEUE(&syncCompletedPktsQueue);
        
        if (NumLookAheads > HTC_HOST_MAX_MSG_PER_BUNDLE) {
            status = A_EPROTO;
            A_ASSERT(FALSE);
            break;    
        }
   
            /* first lookahead sets the expected endpoint IDs for all packets in a bundle */
        id = ((HTC_FRAME_HDR *)&lookAheads[0])->EndpointID;
        pEndpoint = &target->EndPoint[id];
        
        if (id >= ENDPOINT_MAX) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("MsgPend, Invalid Endpoint in look-ahead: %d \n",id));
            status = A_EPROTO;
            break;
        }
        
            /* try to allocate as many HTC RX packets indicated by the lookaheads
             * these packets are stored in the recvPkt queue */
        status = AllocAndPrepareRxPackets(target, 
                                          lookAheads, 
                                          NumLookAheads,
                                          pEndpoint, 
                                          &recvPktQueue);        
        if (A_FAILED(status)) {
            break;    
        }
 
        if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) >= 2) {
                /* a recv bundle was detected, force IRQ status re-check again */
            REF_IRQ_STATUS_RECHECK(&target->Device);
        }
        
        totalFetched += HTC_PACKET_QUEUE_DEPTH(&recvPktQueue);
               
            /* we've got packet buffers for all we can currently fetch, 
             * this count is not valid anymore  */
        NumLookAheads = 0;
        partialBundle = FALSE;
       
            /* now go fetch the list of HTC packets */
        while (!HTC_QUEUE_EMPTY(&recvPktQueue)) {   
            
            pktsFetched = 0;
                       
            if (target->RecvBundlingEnabled && (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) > 1)) {             
                    /* there are enough packets to attempt a bundle transfer and recv bundling is allowed  */
                status = HTCIssueRecvPacketBundle(target,
                                                  &recvPktQueue,
                                                  asyncProc ? NULL : &syncCompletedPktsQueue,
                                                  &pktsFetched,
                                                  partialBundle);                                                   
                if (A_FAILED(status)) {
                    break;
                }
                
                if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) != 0) {
                        /* we couldn't fetch all packets at one time, this creates a broken
                         * bundle  */
                    partialBundle = TRUE;    
                }                                                                     
            }
            
                /* see if the previous operation fetched any packets using bundling */
            if (0 == pktsFetched) {  
                    /* dequeue one packet */
                pPacket = HTC_PACKET_DEQUEUE(&recvPktQueue);
                A_ASSERT(pPacket != NULL);                 
                                     
                if (asyncProc) {
                        /* we use async mode to get the packet if the device layer supports it
                         * set our callback and context */
                    pPacket->Completion = HTCRecvCompleteHandler;
                    pPacket->pContext = target;
                } else {
                        /* fully synchronous */
                    pPacket->Completion = NULL;
                }
                
                if (HTC_PACKET_QUEUE_DEPTH(&recvPktQueue) > 0) {
                        /* lookaheads in all packets except the last one in the bundle must be ignored */
                    pPacket->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_IGNORE_LOOKAHEAD;
                }
                                    
                    /* go fetch the packet */
                status = HTCIssueRecv(target, pPacket);              
                if (A_FAILED(status)) {
                    break;
                }  
                               
                if (!asyncProc) {               
                        /* sent synchronously, queue this packet for synchronous completion */
                    HTC_PACKET_ENQUEUE(&syncCompletedPktsQueue,pPacket);
                } 
                               
            }
            
        }

        if (A_SUCCESS(status)) {  
            CheckRecvWaterMark(pEndpoint);
        }
            
        if (asyncProc) {
                /* we did this asynchronously so we can get out of the loop, the asynch processing
                 * creates a chain of requests to continue processing pending messages in the
                 * context of callbacks  */
            break;
        }

            /* synchronous handling */
        if (target->Device.DSRCanYield) {
                /* for the SYNC case, increment count that tracks when the DSR should yield */
            target->Device.CurrentDSRRecvCount++;    
        }
            
            /* in the sync case, all packet buffers are now filled, 
             * we can process each packet, check lookaheads and then repeat */ 
             
             /* unload sync completion queue */      
        while (!HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {
            HTC_PACKET_QUEUE    container;
           
            pPacket = HTC_PACKET_DEQUEUE(&syncCompletedPktsQueue);
            A_ASSERT(pPacket != NULL);
            
            pEndpoint = &target->EndPoint[pPacket->Endpoint];           
                /* reset count on each iteration, we are only interested in the last packet's lookahead
                 * information when we break out of this loop */
            NumLookAheads = 0;
                /* process header for each of the recv packets
                 * note: the lookahead of the last packet is useful for us to continue in this loop */            
            status = HTCProcessRecvHeader(target,pPacket,lookAheads,&NumLookAheads);
            if (A_FAILED(status)) {
                break;
            }
            
            if (HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {
                    /* last packet's more packets flag is set based on the lookahead */
                SET_MORE_RX_PACKET_INDICATION_FLAG(lookAheads,NumLookAheads,pEndpoint,pPacket);
            } else {
                    /* packets in a bundle automatically have this flag set */
                FORCE_MORE_RX_PACKET_INDICATION_FLAG(pPacket);
            }
                /* good packet, indicate it */
            HTC_RX_STAT_PROFILE(target,pEndpoint,NumLookAheads);
            
            if (pPacket->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_PART_OF_BUNDLE) {
                INC_HTC_EP_STAT(pEndpoint, RxPacketsBundled, 1);
            }
            
            INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
            DO_RCV_COMPLETION(pEndpoint,&container);
        }

        if (A_FAILED(status)) {
            break;
        }
            
        if (NumLookAheads == 0) {
                /* no more look aheads */
            break;    
        }

            /* when we process recv synchronously we need to check if we should yield and stop
             * fetching more packets indicated by the embedded lookaheads */
        if (target->Device.DSRCanYield) {
            if (DEV_CHECK_RECV_YIELD(&target->Device)) {
                    /* break out, don't fetch any more packets */
                break;  
            }  
        }
            

        /* check whether other OS contexts have queued any WMI command/data for WLAN. 
         * This check is needed only if WLAN Tx and Rx happens in same thread context */
        A_CHECK_DRV_TX();
        
            /* for SYNCH processing, if we get here, we are running through the loop again due to a detected lookahead.
             * Set flag that we should re-check IRQ status registers again before leaving IRQ processing,
             * this can net better performance in high throughput situations */
        REF_IRQ_STATUS_RECHECK(&target->Device);
    }
    
    if (A_FAILED(status)) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
                        ("Failed to get pending recv messages (%d) \n",status));
            /* cleanup any packets we allocated but didn't use to actually fetch any packets */                        
        while (!HTC_QUEUE_EMPTY(&recvPktQueue)) {   
            pPacket = HTC_PACKET_DEQUEUE(&recvPktQueue);
                /* clean up packets */
            HTC_RECYCLE_RX_PKT(target, pPacket, &target->EndPoint[pPacket->Endpoint]);
        }
            /* cleanup any packets in sync completion queue */
        while (!HTC_QUEUE_EMPTY(&syncCompletedPktsQueue)) {   
            pPacket = HTC_PACKET_DEQUEUE(&syncCompletedPktsQueue);
                /* clean up packets */
            HTC_RECYCLE_RX_PKT(target, pPacket, &target->EndPoint[pPacket->Endpoint]);
        }
    }
    
        /* before leaving, check to see if host ran out of buffers and needs to stop the
         * receiver */
    if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
        AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
                (" Host has no RX buffers, blocking receiver to prevent overrun.. \n"));
            /* try to stop receive at the device layer */
        DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC);
    }
    
    if (pNumPktsFetched != NULL) {
        *pNumPktsFetched = totalFetched;    
    }
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n"));

    return status;
}

A_STATUS HTCAddReceivePktMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
{
    HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    HTC_ENDPOINT    *pEndpoint;
    A_BOOL          unblockRecv = FALSE;
    A_STATUS        status = A_OK;
    HTC_PACKET      *pFirstPacket;

    pFirstPacket = HTC_GET_PKT_AT_HEAD(pPktQueue);
    
    if (NULL == pFirstPacket) {
        A_ASSERT(FALSE);
        return A_EINVAL;    
    }
    
    AR_DEBUG_ASSERT(pFirstPacket->Endpoint < ENDPOINT_MAX);
    
    AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
                    ("+- HTCAddReceivePktMultiple : endPointId: %d, cnt:%d, length: %d\n",
                    pFirstPacket->Endpoint,
                    HTC_PACKET_QUEUE_DEPTH(pPktQueue), 
                    pFirstPacket->BufferLength));

    do {

        pEndpoint = &target->EndPoint[pFirstPacket->Endpoint];

        LOCK_HTC_RX(target);

        if (HTC_STOPPING(target)) {
            HTC_PACKET *pPacket;
            
            UNLOCK_HTC_RX(target);
            
                /* walk through queue and mark each one canceled */
            HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
                pPacket->Status = A_ECANCELED;    
            } HTC_PACKET_QUEUE_ITERATE_END;
            
            DO_RCV_COMPLETION(pEndpoint,pPktQueue);
            break;
        }

            /* store receive packets */
        HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RxBuffers, pPktQueue);

            /* check if we are blocked waiting for a new buffer */
        if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
            if (target->EpWaitingForBuffers == pFirstPacket->Endpoint) {
                AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" receiver was blocked on ep:%d, unblocking.. \n",
                    target->EpWaitingForBuffers));
                target->RecvStateFlags &= ~HTC_RECV_WAIT_BUFFERS;
                target->EpWaitingForBuffers = ENDPOINT_MAX;
                unblockRecv = TRUE;
            }
        }

        UNLOCK_HTC_RX(target);

        if (unblockRecv && !HTC_STOPPING(target)) {
                /* TODO : implement a buffer threshold count? */
            DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
        }

    } while (FALSE);

    return status;
}

/* Makes a buffer available to the HTC module */
A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
    HTC_PACKET_QUEUE queue;
    INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket); 
    return HTCAddReceivePktMultiple(HTCHandle, &queue);       
}

void HTCUnblockRecv(HTC_HANDLE HTCHandle)
{
    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
    A_BOOL      unblockRecv = FALSE;

    LOCK_HTC_RX(target);

        /* check if we are blocked waiting for a new buffer */
    if (target->RecvStateFlags & HTC_RECV_WAIT_BUFFERS) {
        AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("HTCUnblockRx : receiver was blocked on ep:%d, unblocking.. \n",
            target->EpWaitingForBuffers));
        target->RecvStateFlags &= ~HTC_RECV_WAIT_BUFFERS;
        target->EpWaitingForBuffers = ENDPOINT_MAX;
        unblockRecv = TRUE;
    }

    UNLOCK_HTC_RX(target);

    if (unblockRecv && !HTC_STOPPING(target)) {
            /* re-enable */
        DevEnableRecv(&target->Device,DEV_ENABLE_RECV_ASYNC);
    }
}

static void HTCFlushRxQueue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET_QUEUE *pQueue)
{
    HTC_PACKET  *pPacket;
    HTC_PACKET_QUEUE container;
    
    LOCK_HTC_RX(target);

    while (1) {
        pPacket = HTC_PACKET_DEQUEUE(pQueue);
        if (NULL == pPacket) {
            break;
        }
        UNLOCK_HTC_RX(target);
        pPacket->Status = A_ECANCELED;
        pPacket->ActualLength = 0;
        AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("  Flushing RX packet:0x%X, length:%d, ep:%d \n",
                (A_UINT32)pPacket, pPacket->BufferLength, pPacket->Endpoint));
        INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
            /* give the packet back */
        DO_RCV_COMPLETION(pEndpoint,&container);
        LOCK_HTC_RX(target);
    }
    
    UNLOCK_HTC_RX(target);
}

static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
{
        /* flush any recv indications not already made */
    HTCFlushRxQueue(target,pEndpoint,&pEndpoint->RecvIndicationQueue);
        /* flush any rx buffers */
    HTCFlushRxQueue(target,pEndpoint,&pEndpoint->RxBuffers);
}

void HTCFlushRecvBuffers(HTC_TARGET *target)
{
    HTC_ENDPOINT    *pEndpoint;
    int             i;

    for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
        pEndpoint = &target->EndPoint[i];
        if (pEndpoint->ServiceID == 0) {
                /* not in use.. */
            continue;
        }
        HTCFlushEndpointRX(target,pEndpoint);
    }
}


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

    if (!HTC_STOPPING(target)) {
            /* re-enable */
        DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
    }
}

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

    if (!HTC_STOPPING(target)) {
            /* disable */
        DevStopRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
    }
}

int HTCGetNumRecvBuffers(HTC_HANDLE      HTCHandle,
                         HTC_ENDPOINT_ID Endpoint)
{
    HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);    
    return HTC_PACKET_QUEUE_DEPTH(&(target->EndPoint[Endpoint].RxBuffers));
}

