| 1 | /* |
| 2 | * |
| 3 | * Copyright (c) 2007 Atheros Communications Inc. |
| 4 | * All rights reserved. |
| 5 | * |
| 6 | * |
| 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; |
| 10 | * |
| 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. |
| 15 | * |
| 16 | * |
| 17 | * |
| 18 | */ |
| 19 | |
| 20 | #include "htc_internal.h" |
| 21 | |
| 22 | #define HTCIssueRecv(t, p) \ |
| 23 | DevRecvPacket(&(t)->Device, \ |
| 24 | (p), \ |
| 25 | (p)->ActualLength) |
| 26 | |
| 27 | #define DO_RCV_COMPLETION(t,p,e) \ |
| 28 | { \ |
| 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, \ |
| 33 | (p)); \ |
| 34 | } else { \ |
| 35 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" recycling empty packet \n")); \ |
| 36 | HTC_RECYCLE_RX_PKT((t), (p)); \ |
| 37 | } \ |
| 38 | } |
| 39 | |
| 40 | #ifdef HTC_EP_STAT_PROFILING |
| 41 | #define HTC_RX_STAT_PROFILE(t,ep,lookAhead) \ |
| 42 | { \ |
| 43 | LOCK_HTC_RX((t)); \ |
| 44 | INC_HTC_EP_STAT((ep), RxReceived, 1); \ |
| 45 | if ((lookAhead) != 0) { \ |
| 46 | INC_HTC_EP_STAT((ep), RxLookAheads, 1); \ |
| 47 | } \ |
| 48 | UNLOCK_HTC_RX((t)); \ |
| 49 | } |
| 50 | #else |
| 51 | #define HTC_RX_STAT_PROFILE(t,ep,lookAhead) |
| 52 | #endif |
| 53 | |
| 54 | static INLINE A_STATUS HTCProcessTrailer(HTC_TARGET *target, |
| 55 | A_UINT8 *pBuffer, |
| 56 | int Length, |
| 57 | A_UINT32 *pNextLookAhead, |
| 58 | HTC_ENDPOINT_ID FromEndpoint) |
| 59 | { |
| 60 | HTC_RECORD_HDR *pRecord; |
| 61 | A_UINT8 *pRecordBuf; |
| 62 | HTC_LOOKAHEAD_REPORT *pLookAhead; |
| 63 | A_UINT8 *pOrigBuffer; |
| 64 | int origLength; |
| 65 | A_STATUS status; |
| 66 | |
| 67 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessTrailer (length:%d) \n", Length)); |
| 68 | |
| 69 | if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { |
| 70 | AR_DEBUG_PRINTBUF(pBuffer,Length,"Recv Trailer"); |
| 71 | } |
| 72 | |
| 73 | pOrigBuffer = pBuffer; |
| 74 | origLength = Length; |
| 75 | status = A_OK; |
| 76 | |
| 77 | while (Length > 0) { |
| 78 | |
| 79 | if (Length < sizeof(HTC_RECORD_HDR)) { |
| 80 | status = A_EPROTO; |
| 81 | break; |
| 82 | } |
| 83 | /* these are byte aligned structs */ |
| 84 | pRecord = (HTC_RECORD_HDR *)pBuffer; |
| 85 | Length -= sizeof(HTC_RECORD_HDR); |
| 86 | pBuffer += sizeof(HTC_RECORD_HDR); |
| 87 | |
| 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)); |
| 93 | status = A_EPROTO; |
| 94 | break; |
| 95 | } |
| 96 | /* start of record follows the header */ |
| 97 | pRecordBuf = pBuffer; |
| 98 | |
| 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)), |
| 105 | FromEndpoint); |
| 106 | break; |
| 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)) { |
| 112 | |
| 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)); |
| 117 | |
| 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]; |
| 123 | |
| 124 | if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { |
| 125 | DebugDumpBytes((A_UINT8 *)pNextLookAhead,4,"Next Look Ahead"); |
| 126 | } |
| 127 | } |
| 128 | break; |
| 129 | default: |
| 130 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" unhandled record: id:%d length:%d \n", |
| 131 | pRecord->RecordID, pRecord->Length)); |
| 132 | break; |
| 133 | } |
| 134 | |
| 135 | if (A_FAILED(status)) { |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | /* advance buffer past this record for next time around */ |
| 140 | pBuffer += pRecord->Length; |
| 141 | Length -= pRecord->Length; |
| 142 | } |
| 143 | |
| 144 | if (A_FAILED(status)) { |
| 145 | DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer"); |
| 146 | } |
| 147 | |
| 148 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n")); |
| 149 | return status; |
| 150 | |
| 151 | } |
| 152 | |
| 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) |
| 156 | { |
| 157 | A_UINT8 temp; |
| 158 | A_UINT8 *pBuf; |
| 159 | A_STATUS status = A_OK; |
| 160 | A_UINT16 payloadLen; |
| 161 | A_UINT32 lookAhead; |
| 162 | |
| 163 | pBuf = pPacket->pBuffer; |
| 164 | |
| 165 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader \n")); |
| 166 | |
| 167 | if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) { |
| 168 | AR_DEBUG_PRINTBUF(pBuf,pPacket->ActualLength,"HTC Recv PKT"); |
| 169 | } |
| 170 | |
| 171 | do { |
| 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); |
| 175 | |
| 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]; |
| 180 | |
| 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, |
| 193 | "Last trailer"); |
| 194 | } |
| 195 | #endif |
| 196 | status = A_EPROTO; |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | /* get flags */ |
| 201 | temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, Flags); |
| 202 | |
| 203 | if (temp & HTC_FLAGS_RECV_TRAILER) { |
| 204 | /* this packet has a trailer */ |
| 205 | |
| 206 | /* extract the trailer length in control byte 0 */ |
| 207 | temp = A_GET_UINT8_FIELD(pBuf, HTC_FRAME_HDR, ControlBytes[0]); |
| 208 | |
| 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", |
| 212 | payloadLen, temp)); |
| 213 | status = A_EPROTO; |
| 214 | break; |
| 215 | } |
| 216 | |
| 217 | /* process trailer data that follows HDR + application payload */ |
| 218 | status = HTCProcessTrailer(target, |
| 219 | (pBuf + HTC_HDR_LENGTH + payloadLen - temp), |
| 220 | temp, |
| 221 | pNextLookAhead, |
| 222 | pPacket->Endpoint); |
| 223 | |
| 224 | if (A_FAILED(status)) { |
| 225 | break; |
| 226 | } |
| 227 | |
| 228 | #ifdef HTC_CAPTURE_LAST_FRAME |
| 229 | A_MEMCPY(target->LastTrailer, (pBuf + HTC_HDR_LENGTH + payloadLen - temp), temp); |
| 230 | target->LastTrailerLength = temp; |
| 231 | #endif |
| 232 | /* trim length by trailer bytes */ |
| 233 | pPacket->ActualLength -= temp; |
| 234 | } |
| 235 | #ifdef HTC_CAPTURE_LAST_FRAME |
| 236 | else { |
| 237 | target->LastTrailerLength = 0; |
| 238 | } |
| 239 | #endif |
| 240 | |
| 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; |
| 245 | |
| 246 | } while (FALSE); |
| 247 | |
| 248 | if (A_FAILED(status)) { |
| 249 | /* dump the whole packet */ |
| 250 | DebugDumpBytes(pBuf,pPacket->ActualLength,"BAD HTC Recv PKT"); |
| 251 | } else { |
| 252 | #ifdef HTC_CAPTURE_LAST_FRAME |
| 253 | A_MEMCPY(&target->LastFrameHdr,pBuf,sizeof(HTC_FRAME_HDR)); |
| 254 | #endif |
| 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"); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessRecvHeader \n")); |
| 263 | return status; |
| 264 | } |
| 265 | |
| 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) |
| 269 | { |
| 270 | HTC_TARGET *target = (HTC_TARGET *)Context; |
| 271 | HTC_ENDPOINT *pEndpoint; |
| 272 | A_UINT32 nextLookAhead = 0; |
| 273 | A_STATUS status; |
| 274 | |
| 275 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCRecvCompleteHandler (status:%d, ep:%d) \n", |
| 276 | pPacket->Status, pPacket->Endpoint)); |
| 277 | |
| 278 | AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX); |
| 279 | pEndpoint = &target->EndPoint[pPacket->Endpoint]; |
| 280 | pPacket->Completion = NULL; |
| 281 | |
| 282 | /* get completion status */ |
| 283 | status = pPacket->Status; |
| 284 | |
| 285 | do { |
| 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)); |
| 289 | break; |
| 290 | } |
| 291 | /* process the header for any trailer data */ |
| 292 | status = HTCProcessRecvHeader(target,pPacket,&nextLookAhead); |
| 293 | |
| 294 | if (A_FAILED(status)) { |
| 295 | break; |
| 296 | } |
| 297 | /* was there a lookahead for the next packet? */ |
| 298 | if (nextLookAhead != 0) { |
| 299 | A_STATUS nextStatus; |
| 300 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, |
| 301 | ("HTCRecvCompleteHandler - next look ahead was non-zero : 0x%X \n", |
| 302 | nextLookAhead)); |
| 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, |
| 310 | 4, |
| 311 | "BAD lookahead from lookahead report"); |
| 312 | } |
| 313 | } else { |
| 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); |
| 321 | } |
| 322 | |
| 323 | HTC_RX_STAT_PROFILE(target,pEndpoint,nextLookAhead); |
| 324 | DO_RCV_COMPLETION(target,pPacket,pEndpoint); |
| 325 | |
| 326 | } while (FALSE); |
| 327 | |
| 328 | if (A_FAILED(status)) { |
| 329 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| 330 | ("HTCRecvCompleteHandler , message fetch failed (status = %d) \n", |
| 331 | status)); |
| 332 | /* recyle this packet */ |
| 333 | HTC_RECYCLE_RX_PKT(target, pPacket); |
| 334 | } |
| 335 | |
| 336 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCRecvCompleteHandler\n")); |
| 337 | } |
| 338 | |
| 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) |
| 343 | { |
| 344 | A_STATUS status; |
| 345 | A_UINT32 lookAhead; |
| 346 | HTC_PACKET *pPacket = NULL; |
| 347 | HTC_FRAME_HDR *pHdr; |
| 348 | |
| 349 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCWaitforControlMessage \n")); |
| 350 | |
| 351 | do { |
| 352 | |
| 353 | *ppControlPacket = NULL; |
| 354 | |
| 355 | /* call the polling function to see if we have a message */ |
| 356 | status = DevPollMboxMsgRecv(&target->Device, |
| 357 | &lookAhead, |
| 358 | HTC_TARGET_RESPONSE_TIMEOUT); |
| 359 | |
| 360 | if (A_FAILED(status)) { |
| 361 | break; |
| 362 | } |
| 363 | |
| 364 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, |
| 365 | ("HTCWaitforControlMessage : lookAhead : 0x%X \n", lookAhead)); |
| 366 | |
| 367 | /* check the lookahead */ |
| 368 | pHdr = (HTC_FRAME_HDR *)&lookAhead; |
| 369 | |
| 370 | if (pHdr->EndpointID != ENDPOINT_0) { |
| 371 | /* unexpected endpoint number, should be zero */ |
| 372 | AR_DEBUG_ASSERT(FALSE); |
| 373 | status = A_EPROTO; |
| 374 | break; |
| 375 | } |
| 376 | |
| 377 | if (A_FAILED(status)) { |
| 378 | /* bad message */ |
| 379 | AR_DEBUG_ASSERT(FALSE); |
| 380 | status = A_EPROTO; |
| 381 | break; |
| 382 | } |
| 383 | |
| 384 | pPacket = HTC_ALLOC_CONTROL_RX(target); |
| 385 | |
| 386 | if (pPacket == NULL) { |
| 387 | AR_DEBUG_ASSERT(FALSE); |
| 388 | status = A_NO_MEMORY; |
| 389 | break; |
| 390 | } |
| 391 | |
| 392 | pPacket->HTCReserved = lookAhead; |
| 393 | pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH; |
| 394 | |
| 395 | if (pPacket->ActualLength > pPacket->BufferLength) { |
| 396 | AR_DEBUG_ASSERT(FALSE); |
| 397 | status = A_EPROTO; |
| 398 | break; |
| 399 | } |
| 400 | |
| 401 | /* we want synchronous operation */ |
| 402 | pPacket->Completion = NULL; |
| 403 | |
| 404 | /* get the message from the device, this will block */ |
| 405 | status = HTCIssueRecv(target, pPacket); |
| 406 | |
| 407 | if (A_FAILED(status)) { |
| 408 | break; |
| 409 | } |
| 410 | |
| 411 | /* process receive header */ |
| 412 | status = HTCProcessRecvHeader(target,pPacket,NULL); |
| 413 | |
| 414 | pPacket->Status = status; |
| 415 | |
| 416 | if (A_FAILED(status)) { |
| 417 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| 418 | ("HTCWaitforControlMessage, HTCProcessRecvHeader failed (status = %d) \n", |
| 419 | status)); |
| 420 | break; |
| 421 | } |
| 422 | |
| 423 | /* give the caller this control message packet, they are responsible to free */ |
| 424 | *ppControlPacket = pPacket; |
| 425 | |
| 426 | } while (FALSE); |
| 427 | |
| 428 | if (A_FAILED(status)) { |
| 429 | if (pPacket != NULL) { |
| 430 | /* cleanup buffer on error */ |
| 431 | HTC_FREE_CONTROL_RX(target,pPacket); |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCWaitforControlMessage \n")); |
| 436 | |
| 437 | return status; |
| 438 | } |
| 439 | |
| 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) |
| 442 | { |
| 443 | HTC_TARGET *target = (HTC_TARGET *)Context; |
| 444 | A_STATUS status = A_OK; |
| 445 | HTC_PACKET *pPacket = NULL; |
| 446 | HTC_FRAME_HDR *pHdr; |
| 447 | HTC_ENDPOINT *pEndpoint; |
| 448 | A_BOOL asyncProc = FALSE; |
| 449 | |
| 450 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+HTCRecvMessagePendingHandler LookAhead:0x%X \n",LookAhead)); |
| 451 | |
| 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 */ |
| 456 | asyncProc = TRUE; |
| 457 | } |
| 458 | |
| 459 | if (pAsyncProc != NULL) { |
| 460 | /* indicate to caller how we decided to process this */ |
| 461 | *pAsyncProc = asyncProc; |
| 462 | } |
| 463 | |
| 464 | while (TRUE) { |
| 465 | |
| 466 | pHdr = (HTC_FRAME_HDR *)&LookAhead; |
| 467 | |
| 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 */ |
| 471 | status = A_EPROTO; |
| 472 | break; |
| 473 | } |
| 474 | |
| 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)); |
| 478 | status = A_EPROTO; |
| 479 | break; |
| 480 | } |
| 481 | |
| 482 | pEndpoint = &target->EndPoint[pHdr->EndpointID]; |
| 483 | |
| 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 */ |
| 487 | status = A_EPROTO; |
| 488 | break; |
| 489 | } |
| 490 | |
| 491 | /* lock RX to get a buffer */ |
| 492 | LOCK_HTC_RX(target); |
| 493 | |
| 494 | /* get a packet from the endpoint recv queue */ |
| 495 | pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers); |
| 496 | |
| 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, |
| 503 | pHdr->EndpointID); |
| 504 | LOCK_HTC_RX(target); |
| 505 | /* check if we have more buffers */ |
| 506 | pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers); |
| 507 | /* fall through */ |
| 508 | } |
| 509 | } |
| 510 | |
| 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; |
| 516 | } |
| 517 | |
| 518 | UNLOCK_HTC_RX(target); |
| 519 | |
| 520 | if (A_FAILED(status)) { |
| 521 | /* no buffers */ |
| 522 | break; |
| 523 | } |
| 524 | |
| 525 | AR_DEBUG_ASSERT(pPacket->Endpoint == pHdr->EndpointID); |
| 526 | |
| 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)); |
| 532 | status = A_EPROTO; |
| 533 | break; |
| 534 | } |
| 535 | |
| 536 | pPacket->HTCReserved = LookAhead; /* set expected look ahead */ |
| 537 | /* set the amount of data to fetch */ |
| 538 | pPacket->ActualLength = pHdr->PayloadLen + HTC_HDR_LENGTH; |
| 539 | |
| 540 | if (asyncProc) { |
| 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; |
| 545 | } else { |
| 546 | /* fully synchronous */ |
| 547 | pPacket->Completion = NULL; |
| 548 | } |
| 549 | |
| 550 | /* go fetch the packet */ |
| 551 | status = HTCIssueRecv(target, pPacket); |
| 552 | |
| 553 | if (A_FAILED(status)) { |
| 554 | break; |
| 555 | } |
| 556 | |
| 557 | if (asyncProc) { |
| 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 */ |
| 561 | break; |
| 562 | } |
| 563 | |
| 564 | /* in the sync case, we process the packet, check lookaheads and then repeat */ |
| 565 | |
| 566 | LookAhead = 0; |
| 567 | status = HTCProcessRecvHeader(target,pPacket,&LookAhead); |
| 568 | |
| 569 | if (A_FAILED(status)) { |
| 570 | break; |
| 571 | } |
| 572 | |
| 573 | HTC_RX_STAT_PROFILE(target,pEndpoint,LookAhead); |
| 574 | DO_RCV_COMPLETION(target,pPacket,pEndpoint); |
| 575 | |
| 576 | pPacket = NULL; |
| 577 | |
| 578 | if (0 == LookAhead) { |
| 579 | break; |
| 580 | } |
| 581 | |
| 582 | } |
| 583 | |
| 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", |
| 587 | pHdr->EndpointID)); |
| 588 | /* try to stop receive at the device layer */ |
| 589 | DevStopRecv(&target->Device, asyncProc ? DEV_STOP_RECV_ASYNC : DEV_STOP_RECV_SYNC); |
| 590 | status = A_OK; |
| 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", |
| 594 | LookAhead, status)); |
| 595 | if (pPacket != NULL) { |
| 596 | /* clean up packet on error */ |
| 597 | HTC_RECYCLE_RX_PKT(target, pPacket); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-HTCRecvMessagePendingHandler \n")); |
| 602 | |
| 603 | return status; |
| 604 | } |
| 605 | |
| 606 | /* Makes a buffer available to the HTC module */ |
| 607 | A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) |
| 608 | { |
| 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; |
| 613 | |
| 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)); |
| 617 | |
| 618 | do { |
| 619 | |
| 620 | if (HTC_STOPPING(target)) { |
| 621 | status = A_ECANCELED; |
| 622 | break; |
| 623 | } |
| 624 | |
| 625 | AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX); |
| 626 | |
| 627 | pEndpoint = &target->EndPoint[pPacket->Endpoint]; |
| 628 | |
| 629 | LOCK_HTC_RX(target); |
| 630 | |
| 631 | /* store receive packet */ |
| 632 | HTC_PACKET_ENQUEUE(&pEndpoint->RxBuffers, pPacket); |
| 633 | |
| 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; |
| 641 | unblockRecv = TRUE; |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | UNLOCK_HTC_RX(target); |
| 646 | |
| 647 | if (unblockRecv && !HTC_STOPPING(target)) { |
| 648 | /* TODO : implement a buffer threshold count? */ |
| 649 | DevEnableRecv(&target->Device,DEV_ENABLE_RECV_SYNC); |
| 650 | } |
| 651 | |
| 652 | } while (FALSE); |
| 653 | |
| 654 | return status; |
| 655 | } |
| 656 | |
| 657 | static void HTCFlushEndpointRX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint) |
| 658 | { |
| 659 | HTC_PACKET *pPacket; |
| 660 | |
| 661 | LOCK_HTC_RX(target); |
| 662 | |
| 663 | while (1) { |
| 664 | pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBuffers); |
| 665 | if (NULL == pPacket) { |
| 666 | break; |
| 667 | } |
| 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, |
| 675 | pPacket); |
| 676 | LOCK_HTC_RX(target); |
| 677 | } |
| 678 | |
| 679 | UNLOCK_HTC_RX(target); |
| 680 | |
| 681 | |
| 682 | } |
| 683 | |
| 684 | void HTCFlushRecvBuffers(HTC_TARGET *target) |
| 685 | { |
| 686 | HTC_ENDPOINT *pEndpoint; |
| 687 | int i; |
| 688 | |
| 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) { |
| 694 | /* not in use.. */ |
| 695 | continue; |
| 696 | } |
| 697 | HTCFlushEndpointRX(target,pEndpoint); |
| 698 | } |
| 699 | |
| 700 | |
| 701 | } |
| 702 | |
| 703 | |
| 704 | |