3 * Copyright (c) 2007 Atheros Communications Inc.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation;
11 * Software distributed under the License is distributed on an "AS
12 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13 * implied. See the License for the specific language governing
14 * rights and limitations under the License.
20 #include "htc_internal.h"
22 #define HTCIssueRecv(t, p) \
23 DevRecvPacket(&(t)->Device, \
27 #define DO_RCV_COMPLETION(t,p,e) \
29 if ((p)->ActualLength > 0) { \
30 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" completing packet 0x%X (%d bytes) on ep : %d \n", \
31 (A_UINT32)(p), (p)->ActualLength, (p)->Endpoint)); \
32 (e)->EpCallBacks.EpRecv((e)->EpCallBacks.pContext, \
35 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" recycling empty packet \n")); \
36 HTC_RECYCLE_RX_PKT((t), (p)); \
40 #ifdef HTC_EP_STAT_PROFILING
41 #define HTC_RX_STAT_PROFILE(t,ep,lookAhead) \
44 INC_HTC_EP_STAT((ep), RxReceived, 1); \
45 if ((lookAhead) != 0) { \
46 INC_HTC_EP_STAT((ep), RxLookAheads, 1); \
51 #define HTC_RX_STAT_PROFILE(t,ep,lookAhead)
54 static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target,
57 A_UINT32 *pNextLookAhead,
58 HTC_ENDPOINT_ID FromEndpoint)
60 HTC_RECORD_HDR *pRecord;
62 HTC_LOOKAHEAD_REPORT *pLookAhead;
67 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (length:%d) \n", Length));
69 if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
70 AR_DEBUG_PRINTBUF(pBuffer,Length,"Recv Trailer");
73 pOrigBuffer = pBuffer;
79 if (Length < sizeof(HTC_RECORD_HDR)) {
83 /* these are byte aligned structs */
84 pRecord = (HTC_RECORD_HDR *)pBuffer;
85 Length -= sizeof(HTC_RECORD_HDR);
86 pBuffer += sizeof(HTC_RECORD_HDR);
88 if (pRecord->Length > Length) {
89 /* no room left in buffer for record */
90 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
91 (" invalid record length: %d (id:%d) buffer has: %d bytes left \n",
92 pRecord->Length, pRecord->RecordID, Length));
96 /* start of record follows the header */
99 switch (pRecord->RecordID) {
100 case HTC_RECORD_CREDITS:
101 AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_CREDIT_REPORT));
102 HTCProcessCreditRpt(target,
103 (HTC_CREDIT_REPORT *)pRecordBuf,
104 pRecord->Length / (sizeof(HTC_CREDIT_REPORT)),
107 case HTC_RECORD_LOOKAHEAD:
108 AR_DEBUG_ASSERT(pRecord->Length >= sizeof(HTC_LOOKAHEAD_REPORT));
109 pLookAhead = (HTC_LOOKAHEAD_REPORT *)pRecordBuf;
110 if ((pLookAhead->PreValid == ((~pLookAhead->PostValid) & 0xFF)) &&
111 (pNextLookAhead != NULL)) {
113 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
114 (" LookAhead Report Found (pre valid:0x%X, post valid:0x%X) \n",
115 pLookAhead->PreValid,
116 pLookAhead->PostValid));
118 /* look ahead bytes are valid, copy them over */
119 ((A_UINT8 *)pNextLookAhead)[0] = pLookAhead->LookAhead[0];
120 ((A_UINT8 *)pNextLookAhead)[1] = pLookAhead->LookAhead[1];
121 ((A_UINT8 *)pNextLookAhead)[2] = pLookAhead->LookAhead[2];
122 ((A_UINT8 *)pNextLookAhead)[3] = pLookAhead->LookAhead[3];
124 if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
125 DebugDumpBytes((A_UINT8 *)pNextLookAhead,4,"Next Look Ahead");
130 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" unhandled record: id:%d length:%d \n",
131 pRecord->RecordID, pRecord->Length));
135 if (A_FAILED(status)) {
139 /* advance buffer past this record for next time around */
140 pBuffer += pRecord->Length;
141 Length -= pRecord->Length;
144 if (A_FAILED(status)) {
145 DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer");
148 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n"));
153 /* process a received message (i.e. strip off header, process any trailer data)
154 * note : locks must be released when this function is called */
155 static A_STATUS HTCProcessRecvHeader(HTC_TARGET *target, HTC_PACKET *pPacket, A_UINT32 *pNextLookAhead)
159 A_STATUS status = A_OK;
163 pBuf = pPacket->pBuffer;
165 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader \n"));
167 if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
168 AR_DEBUG_PRINTBUF(pBuf,pPacket->ActualLength,"HTC Recv PKT");
172 /* note, we cannot assume the alignment of pBuffer, so we use the safe macros to
173 * retrieve 16 bit fields */
174 payloadLen = A_GET_UINT16_FIELD(pBuf, HTC_FRAME_HDR, PayloadLen);
176 ((A_UINT8 *)&lookAhead)[0] = pBuf[0];
177 ((A_UINT8 *)&lookAhead)[1] = pBuf[1];
178 ((A_UINT8 *)&lookAhead)[2] = pBuf[2];
179 ((A_UINT8 *)&lookAhead)[3] = pBuf[3];
181 if (lookAhead != pPacket->HTCReserved) {
182 /* somehow the lookahead that gave us the full read length did not
183 * reflect the actual header in the pending message */
184 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
185 ("HTCProcessRecvHeader, lookahead mismatch! \n"));
186 DebugDumpBytes((A_UINT8 *)&pPacket->HTCReserved,4,"Expected Message LookAhead");
187 DebugDumpBytes(pBuf,sizeof(HTC_FRAME_HDR),"Current Frame Header");
188 #ifdef HTC_CAPTURE_LAST_FRAME
189 DebugDumpBytes((A_UINT8 *)&target->LastFrameHdr,sizeof(HTC_FRAME_HDR),"Last Frame Header");
190 if (target->LastTrailerLength != 0) {
191 DebugDumpBytes(target->LastTrailer,
192 target->LastTrailerLength,
201 temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, Flags);
203 if (temp & HTC_FLAGS_RECV_TRAILER) {
204 /* this packet has a trailer */
206 /* extract the trailer length in control byte 0 */
207 temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, ControlBytes[0]);
209 if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) {
210 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
211 ("HTCProcessRecvHeader, invalid header (payloadlength should be :%d, CB[0] is:%d) \n",
217 /* process trailer data that follows HDR + application payload */
218 status = HTCProcessTrailer(target,
219 (pBuf + HTC_HDR_LENGTH + payloadLen - temp),
224 if (A_FAILED(status)) {
228 #ifdef HTC_CAPTURE_LAST_FRAME
229 A_MEMCPY(target->LastTrailer, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp);
230 target->LastTrailerLength = temp;
232 /* trim length by trailer bytes */
233 pPacket->ActualLength -= temp;
235 #ifdef HTC_CAPTURE_LAST_FRAME
237 target->LastTrailerLength = 0;
241 /* if we get to this point, the packet is good */
242 /* remove header and adjust length */
243 pPacket->pBuffer += HTC_HDR_LENGTH;
244 pPacket->ActualLength -= HTC_HDR_LENGTH;
248 if (A_FAILED(status)) {
249 /* dump the whole packet */
250 DebugDumpBytes(pBuf,pPacket->ActualLength,"BAD HTC Recv PKT");
252 #ifdef HTC_CAPTURE_LAST_FRAME
253 A_MEMCPY(&target->LastFrameHdr,pBuf,sizeof(HTC_FRAME_HDR));
255 if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
256 if (pPacket->ActualLength > 0) {
257 AR_DEBUG_PRINTBUF(pPacket->pBuffer,pPacket->ActualLength,"HTC - Application Msg");
262 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessRecvHeader \n"));
266 /* asynchronous completion handler for recv packet fetching, when the device layer
267 * completes a read request, it will call this completion handler */
268 void HTCRecvCompleteHandler(void *Context, HTC_PACKET *pPacket)
270 HTC_TARGET *target = (HTC_TARGET *)Context;
271 HTC_ENDPOINT *pEndpoint;
272 A_UINT32 nextLookAhead = 0;
275 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (status:%d, ep:%d) \n",
276 pPacket->Status, pPacket->Endpoint));
278 AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
279 pEndpoint = &target->EndPoint[pPacket->Endpoint];
280 pPacket->Completion = NULL;
282 /* get completion status */
283 status = pPacket->Status;
286 if (A_FAILED(status)) {
287 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTCRecvCompleteHandler: request failed (status:%d, ep:%d) \n",
288 pPacket->Status, pPacket->Endpoint));
291 /* process the header for any trailer data */
292 status = HTCProcessRecvHeader(target,pPacket,&nextLookAhead);
294 if (A_FAILED(status)) {
297 /* was there a lookahead for the next packet? */
298 if (nextLookAhead != 0) {
300 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
301 ("HTCRecvCompleteHandler - next look ahead was non-zero : 0x%X \n",
303 /* we have another packet, get the next packet fetch started (pipelined) before
304 * we call into the endpoint's callback, this will start another async request */
305 nextStatus = HTCRecvMessagePendingHandler(target,nextLookAhead,NULL);
306 if (A_EPROTO == nextStatus) {
307 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
308 ("Next look ahead from recv header was INVALID\n"));
309 DebugDumpBytes((A_UINT8 *)&nextLookAhead,
311 "BAD lookahead from lookahead report");
314 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
315 ("HTCRecvCompleteHandler - rechecking for more messages...\n"));
316 /* if we did not get anything on the look-ahead,
317 * call device layer to asynchronously re-check for messages. If we can keep the async
318 * processing going we get better performance. If there is a pending message we will keep processing
319 * messages asynchronously which should pipeline things nicely */
320 DevCheckPendingRecvMsgsAsync(&target->Device);
323 HTC_RX_STAT_PROFILE(target,pEndpoint,nextLookAhead);
324 DO_RCV_COMPLETION(target,pPacket,pEndpoint);
328 if (A_FAILED(status)) {
329 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
330 ("HTCRecvCompleteHandler , message fetch failed (status = %d) \n",
332 /* recyle this packet */
333 HTC_RECYCLE_RX_PKT(target, pPacket);
336 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCRecvCompleteHandler\n"));
339 /* synchronously wait for a control message from the target,
340 * This function is used at initialization time ONLY. At init messages
341 * on ENDPOINT 0 are expected. */
342 A_STATUS HTCWaitforControlMessage(HTC_TARGET *target, HTC_PACKET **ppControlPacket)
346 HTC_PACKET *pPacket = NULL;
349 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCWaitforControlMessage \n"));
353 *ppControlPacket = NULL;
355 /* call the polling function to see if we have a message */
356 status = DevPollMboxMsgRecv(&target->Device,
358 HTC_TARGET_RESPONSE_TIMEOUT);
360 if (A_FAILED(status)) {
364 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
365 ("HTCWaitforControlMessage : lookAhead : 0x%X \n", lookAhead));
367 /* check the lookahead */
368 pHdr = (HTC_FRAME_HDR *)&lookAhead;
370 if (pHdr->EndpointID != ENDPOINT_0) {
371 /* unexpected endpoint number, should be zero */
372 AR_DEBUG_ASSERT(FALSE);
377 if (A_FAILED(status)) {
379 AR_DEBUG_ASSERT(FALSE);
384 pPacket = HTC_ALLOC_CONTROL_RX(target);
386 if (pPacket == NULL) {
387 AR_DEBUG_ASSERT(FALSE);
388 status = A_NO_MEMORY;
392 pPacket->HTCReserved = lookAhead;
393 pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
395 if (pPacket->ActualLength > pPacket->BufferLength) {
396 AR_DEBUG_ASSERT(FALSE);
401 /* we want synchronous operation */
402 pPacket->Completion = NULL;
404 /* get the message from the device, this will block */
405 status = HTCIssueRecv(target, pPacket);
407 if (A_FAILED(status)) {
411 /* process receive header */
412 status = HTCProcessRecvHeader(target,pPacket,NULL);
414 pPacket->Status = status;
416 if (A_FAILED(status)) {
417 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
418 ("HTCWaitforControlMessage, HTCProcessRecvHeader failed (status = %d) \n",
423 /* give the caller this control message packet, they are responsible to free */
424 *ppControlPacket = pPacket;
428 if (A_FAILED(status)) {
429 if (pPacket != NULL) {
430 /* cleanup buffer on error */
431 HTC_FREE_CONTROL_RX(target,pPacket);
435 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCWaitforControlMessage \n"));
440 /* callback when device layer or lookahead report parsing detects a pending message */
441 A_STATUS HTCRecvMessagePendingHandler(void *Context, A_UINT32 LookAhead, A_BOOL *pAsyncProc)
443 HTC_TARGET *target = (HTC_TARGET *)Context;
444 A_STATUS status = A_OK;
445 HTC_PACKET *pPacket = NULL;
447 HTC_ENDPOINT *pEndpoint;
448 A_BOOL asyncProc = FALSE;
450 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler LookAhead:0x%X \n",LookAhead));
452 if (IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(&target->Device)) {
453 /* We use async mode to get the packets if the device layer supports it.
454 * The device layer interfaces with HIF in which HIF may have restrictions on
455 * how interrupts are processed */
459 if (pAsyncProc != NULL) {
460 /* indicate to caller how we decided to process this */
461 *pAsyncProc = asyncProc;
466 pHdr = (HTC_FRAME_HDR *)&LookAhead;
468 if (pHdr->EndpointID >= ENDPOINT_MAX) {
469 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Invalid Endpoint in look-ahead: %d \n",pHdr->EndpointID));
470 /* invalid endpoint */
475 if (pHdr->PayloadLen > HTC_MAX_PAYLOAD_LENGTH) {
476 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Payload length %d exceeds max HTC : %d !\n",
477 pHdr->PayloadLen, HTC_MAX_PAYLOAD_LENGTH));
482 pEndpoint = &target->EndPoint[pHdr->EndpointID];
484 if (0 == pEndpoint->ServiceID) {
485 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Endpoint %d is not connected !\n",pHdr->EndpointID));
486 /* endpoint isn't even connected */
491 /* lock RX to get a buffer */
494 /* get a packet from the endpoint recv queue */
495 pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
497 if (NULL == pPacket) {
498 /* check for refill handler */
499 if (pEndpoint->EpCallBacks.EpRecvRefill != NULL) {
500 UNLOCK_HTC_RX(target);
501 /* call the re-fill handler */
502 pEndpoint->EpCallBacks.EpRecvRefill(pEndpoint->EpCallBacks.pContext,
505 /* check if we have more buffers */
506 pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
511 if (NULL == pPacket) {
512 /* this is not an error, we simply need to mark that we are waiting for buffers.*/
513 target->HTCStateFlags |= HTC_STATE_WAIT_BUFFERS;
514 target->EpWaitingForBuffers = pHdr->EndpointID;
515 status = A_NO_MEMORY;
518 UNLOCK_HTC_RX(target);
520 if (A_FAILED(status)) {
525 AR_DEBUG_ASSERT(pPacket->Endpoint == pHdr->EndpointID);
527 /* make sure this message can fit in the endpoint buffer */
528 if ((pHdr->PayloadLen + HTC_HDR_LENGTH) > pPacket->BufferLength) {
529 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
530 ("Payload Length Error : header reports payload of: %d, endpoint buffer size: %d \n",
531 pHdr->PayloadLen, pPacket->BufferLength));
536 pPacket->HTCReserved = LookAhead; /* set expected look ahead */
537 /* set the amount of data to fetch */
538 pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH;
541 /* we use async mode to get the packet if the device layer supports it
542 * set our callback and context */
543 pPacket->Completion = HTCRecvCompleteHandler;
544 pPacket->pContext = target;
546 /* fully synchronous */
547 pPacket->Completion = NULL;
550 /* go fetch the packet */
551 status = HTCIssueRecv(target, pPacket);
553 if (A_FAILED(status)) {
558 /* we did this asynchronously so we can get out of the loop, the asynch processing
559 * creates a chain of requests to continue processing pending messages in the
560 * context of callbacks */
564 /* in the sync case, we process the packet, check lookaheads and then repeat */
567 status = HTCProcessRecvHeader(target,pPacket,&LookAhead);
569 if (A_FAILED(status)) {
573 HTC_RX_STAT_PROFILE(target,pEndpoint,LookAhead);
574 DO_RCV_COMPLETION(target,pPacket,pEndpoint);
578 if (0 == LookAhead) {
584 if (A_NO_MEMORY == status) {
585 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
586 (" Endpoint :%d has no buffers, blocking receiver to prevent overrun.. \n",
588 /* try to stop receive at the device layer */
589 DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC);
591 } else if (A_FAILED(status)) {
592 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
593 ("Failed to get pending message : LookAhead Value: 0x%X (status = %d) \n",
595 if (pPacket != NULL) {
596 /* clean up packet on error */
597 HTC_RECYCLE_RX_PKT(target, pPacket);
601 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n"));
606 /* Makes a buffer available to the HTC module */
607 A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
609 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
610 HTC_ENDPOINT *pEndpoint;
611 A_BOOL unblockRecv = FALSE;
612 A_STATUS status = A_OK;
614 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
615 ("+- HTCAddReceivePkt: endPointId: %d, buffer: 0x%X, length: %d\n",
616 pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->BufferLength));
620 if (HTC_STOPPING(target)) {
621 status = A_ECANCELED;
625 AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
627 pEndpoint = &target->EndPoint[pPacket->Endpoint];
631 /* store receive packet */
632 HTC_PACKET_ENQUEUE(&pEndpoint->RxBuffers, pPacket);
634 /* check if we are blocked waiting for a new buffer */
635 if (target->HTCStateFlags & HTC_STATE_WAIT_BUFFERS) {
636 if (target->EpWaitingForBuffers == pPacket->Endpoint) {
637 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" receiver was blocked on ep:%d, unblocking.. \n",
638 target->EpWaitingForBuffers));
639 target->HTCStateFlags &= ~HTC_STATE_WAIT_BUFFERS;
640 target->EpWaitingForBuffers = ENDPOINT_MAX;
645 UNLOCK_HTC_RX(target);
647 if (unblockRecv && !HTC_STOPPING(target)) {
648 /* TODO : implement a buffer threshold count? */
649 DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC);
657 static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint)
664 pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers);
665 if (NULL == pPacket) {
668 UNLOCK_HTC_RX(target);
669 pPacket->Status = A_ECANCELED;
670 pPacket->ActualLength = 0;
671 AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" Flushing RX packet:0x%X, length:%d, ep:%d \n",
672 (A_UINT32)pPacket, pPacket->BufferLength, pPacket->Endpoint));
673 /* give the packet back */
674 pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext,
679 UNLOCK_HTC_RX(target);
684 void HTCFlushRecvBuffers(HTC_TARGET *target)
686 HTC_ENDPOINT *pEndpoint;
689 /* NOTE: no need to flush endpoint 0, these buffers were
690 * allocated as part of the HTC struct */
691 for (i = ENDPOINT_1; i < ENDPOINT_MAX; i++) {
692 pEndpoint = &target->EndPoint[i];
693 if (pEndpoint->ServiceID == 0) {
697 HTCFlushEndpointRX(target,pEndpoint);