mirror of https://github.com/hak5/openwrt.git
704 lines
24 KiB
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);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|