openwrt/target/linux/s3c24xx/files-2.6.30/drivers/ar6000/htc/htc_recv.c

704 lines
24 KiB
C

/*
*
* Copyright (c) 2007 Atheros Communications Inc.
* 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.
*
*
*
*/
#include "htc_internal.h"
#define HTCIssueRecv(t, p) \
DevRecvPacket(&(t)->Device, \
(p), \
(p)->ActualLength)
#define DO_RCV_COMPLETION(t,p,e) \
{ \
if ((p)->ActualLength > 0) { \
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" completing packet 0x%X (%d bytes) on ep : %d \n", \
(A_UINT32)(p), (p)->ActualLength, (p)->Endpoint)); \
(e)->EpCallBacks.EpRecv((e)->EpCallBacks.pContext, \
(p)); \
} else { \
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" recycling empty packet \n")); \
HTC_RECYCLE_RX_PKT((t), (p)); \
} \
}
#ifdef HTC_EP_STAT_PROFILING
#define HTC_RX_STAT_PROFILE(t,ep,lookAhead) \
{ \
LOCK_HTC_RX((t)); \
INC_HTC_EP_STAT((ep), RxReceived, 1); \
if ((lookAhead) != 0) { \
INC_HTC_EP_STAT((ep), RxLookAheads, 1); \
} \
UNLOCK_HTC_RX((t)); \
}
#else
#define HTC_RX_STAT_PROFILE(t,ep,lookAhead)
#endif
static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target,
A_UINT8 *pBuffer,
int Length,
A_UINT32 *pNextLookAhead,
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)) &&
(pNextLookAhead != 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 *)pNextLookAhead)[0] = pLookAhead->LookAhead[0];
((A_UINT8 *)pNextLookAhead)[1] = pLookAhead->LookAhead[1];
((A_UINT8 *)pNextLookAhead)[2] = pLookAhead->LookAhead[2];
((A_UINT8 *)pNextLookAhead)[3] = pLookAhead->LookAhead[3];
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
DebugDumpBytes((A_UINT8 *)pNextLookAhead,4,"Next Look Ahead");
}
}
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 *pNextLookAhead)
{
A_UINT8 temp;
A_UINT8 *pBuf;
A_STATUS status = A_OK;
A_UINT16 payloadLen;
A_UINT32 lookAhead;
pBuf = pPacket->pBuffer;
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 (lookAhead != pPacket->HTCReserved) {
/* 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! \n"));
DebugDumpBytes((A_UINT8 *)&pPacket->HTCReserved,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;
}
/* process trailer data that follows HDR + application payload */
status = HTCProcessTrailer(target,
(pBuf + HTC_HDR_LENGTH + payloadLen - temp),
temp,
pNextLookAhead,
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,"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;
}
/* 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 nextLookAhead = 0;
A_STATUS status;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (status:%d, ep:%d) \n",
pPacket->Status, pPacket->Endpoint));
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,&nextLookAhead);
if (A_FAILED(status)) {
break;
}
/* was there a lookahead for the next packet? */
if (nextLookAhead != 0) {
A_STATUS nextStatus;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("HTCRecvCompleteHandler - next look ahead was non-zero : 0x%X \n",
nextLookAhead));
/* we have another packet, get the next packet fetch started (pipelined) before
* we call into the endpoint's callback, this will start another async request */
nextStatus = HTCRecvMessagePendingHandler(target,nextLookAhead,NULL);
if (A_EPROTO == nextStatus) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Next look ahead from recv header was INVALID\n"));
DebugDumpBytes((A_UINT8 *)&nextLookAhead,
4,
"BAD lookahead from lookahead report");
}
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("HTCRecvCompleteHandler - 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);
}
HTC_RX_STAT_PROFILE(target,pEndpoint,nextLookAhead);
DO_RCV_COMPLETION(target,pPacket,pEndpoint);
} while (FALSE);
if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("HTCRecvCompleteHandler , message fetch failed (status = %d) \n",
status));
/* recyle this packet */
HTC_RECYCLE_RX_PKT(target, pPacket);
}
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->HTCReserved = 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);
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;
}
/* callback when device layer or lookahead report parsing detects a pending message */
A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 LookAhead, A_BOOL *pAsyncProc)
{
HTC_TARGET *target = (HTC_TARGET *)Context;
A_STATUS status = A_OK;
HTC_PACKET *pPacket = NULL;
HTC_FRAME_HDR *pHdr;
HTC_ENDPOINT *pEndpoint;
A_BOOL asyncProc = FALSE;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler LookAhead:0x%X \n",LookAhead));
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;
}
while (TRUE) {
pHdr = (HTC_FRAME_HDR *)&LookAhead;
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->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;
}
pEndpoint = &target->EndPoint[pHdr->EndpointID];
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;
}
/* lock RX to get a buffer */
LOCK_HTC_RX(target);
/* 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,
pHdr->EndpointID);
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->HTCStateFlags |= HTC_STATE_WAIT_BUFFERS;
target->EpWaitingForBuffers = pHdr->EndpointID;
status = A_NO_MEMORY;
}
UNLOCK_HTC_RX(target);
if (A_FAILED(status)) {
/* no buffers */
break;
}
AR_DEBUG_ASSERT(pPacket->Endpoint == pHdr->EndpointID);
/* make sure this message can fit in the endpoint buffer */
if ((pHdr->PayloadLen + HTC_HDR_LENGTH) > pPacket->BufferLength) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Payload Length Error : header reports payload of: %d, endpoint buffer size: %d \n",
pHdr->PayloadLen, pPacket->BufferLength));
status = A_EPROTO;
break;
}
pPacket->HTCReserved = LookAhead; /* set expected look ahead */
/* set the amount of data to fetch */
pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
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;
}
/* go fetch the packet */
status = HTCIssueRecv(target, pPacket);
if (A_FAILED(status)) {
break;
}
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;
}
/* in the sync case, we process the packet, check lookaheads and then repeat */
LookAhead = 0;
status = HTCProcessRecvHeader(target,pPacket,&LookAhead);
if (A_FAILED(status)) {
break;
}
HTC_RX_STAT_PROFILE(target,pEndpoint,LookAhead);
DO_RCV_COMPLETION(target,pPacket,pEndpoint);
pPacket = NULL;
if (0 == LookAhead) {
break;
}
}
if (A_NO_MEMORY == status) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
(" Endpoint :%d has no buffers, blocking receiver to prevent overrun.. \n",
pHdr->EndpointID));
/* try to stop receive at the device layer */
DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC);
status = A_OK;
} else if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Failed to get pending message : LookAhead Value: 0x%X (status = %d) \n",
LookAhead, status));
if (pPacket != NULL) {
/* clean up packet on error */
HTC_RECYCLE_RX_PKT(target, pPacket);
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n"));
return status;
}
/* Makes a buffer available to the HTC module */
A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_ENDPOINT *pEndpoint;
A_BOOL unblockRecv = FALSE;
A_STATUS status = A_OK;
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
("+- HTCAddReceivePkt: endPointId: %d, buffer: 0x%X, length: %d\n",
pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->BufferLength));
do {
if (HTC_STOPPING(target)) {
status = A_ECANCELED;
break;
}
AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
pEndpoint = &target->EndPoint[pPacket->Endpoint];
LOCK_HTC_RX(target);
/* store receive packet */
HTC_PACKET_ENQUEUE(&pEndpoint->RxBuffers, pPacket);
/* check if we are blocked waiting for a new buffer */
if (target->HTCStateFlags & HTC_STATE_WAIT_BUFFERS) {
if (target->EpWaitingForBuffers == pPacket->Endpoint) {
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" receiver was blocked on ep:%d, unblocking.. \n",
target->EpWaitingForBuffers));
target->HTCStateFlags &= ~HTC_STATE_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;
}
static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
{
HTC_PACKET *pPacket;
LOCK_HTC_RX(target);
while (1) {
pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
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));
/* give the packet back */
pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext,
pPacket);
LOCK_HTC_RX(target);
}
UNLOCK_HTC_RX(target);
}
void HTCFlushRecvBuffers(HTC_TARGET *target)
{
HTC_ENDPOINT *pEndpoint;
int i;
/* NOTE: no need to flush endpoint 0, these buffers were
* allocated as part of the HTC struct */
for (i = ENDPOINT_1; i < ENDPOINT_MAX; i++) {
pEndpoint = &target->EndPoint[i];
if (pEndpoint->ServiceID == 0) {
/* not in use.. */
continue;
}
HTCFlushEndpointRX(target,pEndpoint);
}
}