/* * * 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); } }