| /* |
| * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| #include "htc_debug.h" |
| #include "htc_internal.h" |
| #include "vos_api.h" |
| #include <adf_nbuf.h> /* adf_nbuf_t */ |
| #include <vos_getBin.h> |
| #include "epping_main.h" |
| |
| /* HTC Control message receive timeout msec */ |
| #define HTC_CONTROL_RX_TIMEOUT 5000 |
| |
| #ifdef WLAN_DEBUG |
| void DebugDumpBytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription) |
| { |
| A_CHAR stream[60]; |
| A_CHAR byteOffsetStr[10]; |
| A_UINT32 i; |
| A_UINT16 offset, count, byteOffset; |
| |
| A_PRINTF("<---------Dumping %d Bytes : %s ------>\n", length, pDescription); |
| |
| count = 0; |
| offset = 0; |
| byteOffset = 0; |
| for(i = 0; i < length; i++) { |
| A_SNPRINTF(stream + offset, (sizeof(stream) - offset), |
| "%02X ", buffer[i]); |
| count ++; |
| offset += 3; |
| |
| if(count == 16) { |
| count = 0; |
| offset = 0; |
| A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X",byteOffset); |
| A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); |
| A_MEMZERO(stream, 60); |
| byteOffset += 16; |
| } |
| } |
| |
| if(offset != 0) { |
| A_SNPRINTF(byteOffsetStr, sizeof(byteOffset), "%4.4X",byteOffset); |
| A_PRINTF("[%s]: %s\n", byteOffsetStr, stream); |
| } |
| |
| A_PRINTF("<------------------------------------------------->\n"); |
| } |
| #else |
| void DebugDumpBytes(A_UCHAR *buffer, A_UINT16 length, char *pDescription) |
| {} |
| #endif |
| |
| static A_STATUS HTCProcessTrailer(HTC_TARGET *target, |
| A_UINT8 *pBuffer, |
| int Length, |
| HTC_ENDPOINT_ID FromEndpoint); |
| |
| static void DoRecvCompletion(HTC_ENDPOINT *pEndpoint, |
| HTC_PACKET_QUEUE *pQueueToIndicate) |
| { |
| |
| do { |
| |
| if (HTC_QUEUE_EMPTY(pQueueToIndicate)) { |
| /* nothing to indicate */ |
| break; |
| } |
| |
| if (pEndpoint->EpCallBacks.EpRecvPktMultiple != NULL) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" HTC calling ep %d, recv multiple callback (%d pkts) \n", |
| pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate))); |
| /* a recv multiple handler is being used, pass the queue to the handler */ |
| pEndpoint->EpCallBacks.EpRecvPktMultiple(pEndpoint->EpCallBacks.pContext, |
| pQueueToIndicate); |
| INIT_HTC_PACKET_QUEUE(pQueueToIndicate); |
| } else { |
| HTC_PACKET *pPacket; |
| /* using legacy EpRecv */ |
| while (!HTC_QUEUE_EMPTY(pQueueToIndicate)) { |
| pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate); |
| LOCK_HTC_ENDPOINT_RX(pEndpoint); |
| if (pEndpoint->EpCallBacks.EpRecv == NULL) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HTC ep %d has NULL recv callback on packet %pK\n", |
| pEndpoint->Id, pPacket)); |
| UNLOCK_HTC_ENDPOINT_RX(pEndpoint); |
| continue; |
| } |
| AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("HTC calling ep %d recv callback on packet %pK\n", |
| pEndpoint->Id, pPacket)); |
| pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext, pPacket); |
| UNLOCK_HTC_ENDPOINT_RX(pEndpoint); |
| } |
| } |
| |
| } while (FALSE); |
| |
| } |
| |
| static void RecvPacketCompletion(HTC_TARGET *target,HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket) |
| { |
| HTC_PACKET_QUEUE container; |
| INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket); |
| /* do completion */ |
| DoRecvCompletion(pEndpoint,&container); |
| } |
| |
| |
| void HTCControlRxComplete(void *Context, HTC_PACKET *pPacket) |
| { |
| /* TODO, can't really receive HTC control messages yet.... */ |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Invalid call to HTCControlRxComplete\n")); |
| } |
| |
| void HTCUnblockRecv(HTC_HANDLE HTCHandle) |
| { |
| /* TODO find the Need in new model*/ |
| } |
| |
| |
| void HTCEnableRecv(HTC_HANDLE HTCHandle) |
| { |
| |
| /* TODO find the Need in new model*/ |
| } |
| |
| void HTCDisableRecv(HTC_HANDLE HTCHandle) |
| { |
| |
| /* TODO find the Need in new model*/ |
| } |
| |
| int HTCGetNumRecvBuffers(HTC_HANDLE HTCHandle, |
| HTC_ENDPOINT_ID Endpoint) |
| { |
| HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; |
| return HTC_PACKET_QUEUE_DEPTH(&pEndpoint->RxBufferHoldQueue); |
| } |
| |
| HTC_PACKET *AllocateHTCPacketContainer(HTC_TARGET *target) |
| { |
| HTC_PACKET *pPacket; |
| |
| LOCK_HTC_RX(target); |
| |
| if (NULL == target->pHTCPacketStructPool) { |
| UNLOCK_HTC_RX(target); |
| return NULL; |
| } |
| |
| pPacket = target->pHTCPacketStructPool; |
| target->pHTCPacketStructPool = (HTC_PACKET *)pPacket->ListLink.pNext; |
| |
| UNLOCK_HTC_RX(target); |
| |
| pPacket->ListLink.pNext = NULL; |
| return pPacket; |
| } |
| |
| void FreeHTCPacketContainer(HTC_TARGET *target, HTC_PACKET *pPacket) |
| { |
| LOCK_HTC_RX(target); |
| |
| if (NULL == target->pHTCPacketStructPool) { |
| target->pHTCPacketStructPool = pPacket; |
| pPacket->ListLink.pNext = NULL; |
| } else { |
| pPacket->ListLink.pNext = (DL_LIST *)target->pHTCPacketStructPool; |
| target->pHTCPacketStructPool = pPacket; |
| } |
| |
| UNLOCK_HTC_RX(target); |
| } |
| |
| #ifdef RX_SG_SUPPORT |
| adf_nbuf_t RxSgToSingleNetbuf(HTC_TARGET *target) |
| { |
| adf_nbuf_t skb; |
| a_uint8_t *anbdata; |
| a_uint8_t *anbdata_new; |
| a_uint32_t anblen; |
| adf_nbuf_t new_skb = NULL; |
| a_uint32_t sg_queue_len; |
| adf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue; |
| |
| sg_queue_len = adf_nbuf_queue_len(rx_sg_queue); |
| |
| if (sg_queue_len <= 1) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| ("RxSgToSingleNetbuf: invalid sg queue len %u\n")); |
| goto _failed; |
| } |
| |
| new_skb = adf_nbuf_alloc(target->ExpRxSgTotalLen, 0, 4, FALSE); |
| if (new_skb == NULL) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| ("RxSgToSingleNetbuf: can't allocate %u size netbuf\n", |
| target->ExpRxSgTotalLen)); |
| goto _failed; |
| } |
| |
| adf_nbuf_peek_header(new_skb, &anbdata_new, &anblen); |
| |
| skb = adf_nbuf_queue_remove(rx_sg_queue); |
| do { |
| adf_nbuf_peek_header(skb, &anbdata, &anblen); |
| adf_os_mem_copy(anbdata_new, anbdata, adf_nbuf_len(skb)); |
| adf_nbuf_put_tail(new_skb, adf_nbuf_len(skb)); |
| anbdata_new += adf_nbuf_len(skb); |
| adf_nbuf_free(skb); |
| skb = adf_nbuf_queue_remove(rx_sg_queue); |
| } while(skb != NULL); |
| |
| RESET_RX_SG_CONFIG(target); |
| return new_skb; |
| |
| _failed: |
| |
| while ((skb = adf_nbuf_queue_remove(rx_sg_queue)) != NULL) { |
| adf_nbuf_free(skb); |
| } |
| |
| RESET_RX_SG_CONFIG(target); |
| return NULL; |
| } |
| #endif |
| |
| static void HTCsuspendwow(HTC_TARGET *target) |
| { |
| HIFsuspendwow(target->hif_dev); |
| return; |
| } |
| |
| A_STATUS HTCRxCompletionHandler( |
| void *Context, adf_nbuf_t netbuf, a_uint8_t pipeID) |
| { |
| A_STATUS status = A_OK; |
| HTC_FRAME_HDR *HtcHdr; |
| HTC_TARGET *target = (HTC_TARGET *)Context; |
| a_uint8_t *netdata; |
| a_uint32_t netlen; |
| HTC_ENDPOINT *pEndpoint; |
| HTC_PACKET *pPacket; |
| A_UINT16 payloadLen; |
| a_uint32_t trailerlen = 0; |
| A_UINT8 htc_ep_id; |
| |
| #ifdef RX_SG_SUPPORT |
| LOCK_HTC_RX(target); |
| if (target->IsRxSgInprogress) { |
| target->CurRxSgTotalLen += adf_nbuf_len(netbuf); |
| adf_nbuf_queue_add(&target->RxSgQueue, netbuf); |
| if (target->CurRxSgTotalLen == target->ExpRxSgTotalLen) { |
| netbuf = RxSgToSingleNetbuf(target); |
| if (netbuf == NULL) { |
| UNLOCK_HTC_RX(target); |
| goto _out; |
| } |
| } |
| else { |
| netbuf = NULL; |
| UNLOCK_HTC_RX(target); |
| goto _out; |
| } |
| } |
| UNLOCK_HTC_RX(target); |
| #endif |
| |
| netdata = adf_nbuf_data(netbuf); |
| netlen = adf_nbuf_len(netbuf); |
| |
| HtcHdr = (HTC_FRAME_HDR *)netdata; |
| |
| do { |
| |
| htc_ep_id = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, ENDPOINTID); |
| |
| if (htc_ep_id >= ENDPOINT_MAX) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTC Rx: invalid EndpointID=%d\n",htc_ep_id)); |
| DebugDumpBytes((A_UINT8 *)HtcHdr,sizeof(HTC_FRAME_HDR),"BAD HTC Header"); |
| status = A_ERROR; |
| VOS_BUG(0); |
| break; |
| } |
| |
| pEndpoint = &target->EndPoint[htc_ep_id]; |
| |
| /* |
| * If this endpoint that received a message from the target has |
| * a to-target HIF pipe whose send completions are polled rather |
| * than interrupt-driven, this is a good point to ask HIF to check |
| * whether it has any completed sends to handle. |
| */ |
| if (pEndpoint->ul_is_polled) { |
| HTCSendCompleteCheck(pEndpoint, 1); |
| } |
| |
| payloadLen = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, PAYLOADLEN); |
| |
| if (netlen < (payloadLen + HTC_HDR_LENGTH)) { |
| #ifdef RX_SG_SUPPORT |
| LOCK_HTC_RX(target); |
| target->IsRxSgInprogress = TRUE; |
| adf_nbuf_queue_init(&target->RxSgQueue); |
| adf_nbuf_queue_add(&target->RxSgQueue, netbuf); |
| target->ExpRxSgTotalLen = (payloadLen + HTC_HDR_LENGTH); |
| target->CurRxSgTotalLen += netlen; |
| UNLOCK_HTC_RX(target); |
| netbuf = NULL; |
| break; |
| #else |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTC Rx: insufficient length, got:%d expected =%zu\n", |
| netlen, payloadLen + HTC_HDR_LENGTH)); |
| DebugDumpBytes((A_UINT8 *)HtcHdr,sizeof(HTC_FRAME_HDR),"BAD RX packet length"); |
| status = A_ERROR; |
| VOS_BUG(0); |
| break; |
| #endif |
| } |
| |
| #ifdef HTC_EP_STAT_PROFILING |
| LOCK_HTC_RX(target); |
| INC_HTC_EP_STAT(pEndpoint,RxReceived,1); |
| UNLOCK_HTC_RX(target); |
| #endif |
| |
| //if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) { |
| { |
| A_UINT8 temp; |
| /* get flags to check for trailer */ |
| temp = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, FLAGS); |
| if (temp & HTC_FLAGS_RECV_TRAILER) { |
| /* extract the trailer length */ |
| temp = HTC_GET_FIELD(HtcHdr, HTC_FRAME_HDR, CONTROLBYTES0); |
| if ((temp < sizeof(HTC_RECORD_HDR)) || (temp > payloadLen)) { |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| ("HTCRxCompletionHandler, invalid header (payloadlength should be :%d, CB[0] is:%d) \n", |
| payloadLen, temp)); |
| status = A_EPROTO; |
| break; |
| } |
| |
| trailerlen = temp; |
| /* process trailer data that follows HDR + application payload */ |
| status = HTCProcessTrailer(target, |
| ((A_UINT8 *)HtcHdr + HTC_HDR_LENGTH + payloadLen - temp), |
| temp, htc_ep_id); |
| if (A_FAILED(status)) { |
| break; |
| } |
| |
| } |
| } |
| |
| if (((int)payloadLen - (int)trailerlen) <= 0) { |
| /* zero length packet with trailer data, just drop these */ |
| break; |
| } |
| |
| |
| if (htc_ep_id == ENDPOINT_0) { |
| A_UINT16 message_id; |
| HTC_UNKNOWN_MSG *htc_msg; |
| int wow_nack = 0; |
| |
| /* remove HTC header */ |
| adf_nbuf_pull_head(netbuf, HTC_HDR_LENGTH); |
| netdata = adf_nbuf_data(netbuf); |
| netlen = adf_nbuf_len(netbuf); |
| |
| htc_msg = (HTC_UNKNOWN_MSG*)netdata; |
| message_id = HTC_GET_FIELD(htc_msg, HTC_UNKNOWN_MSG, MESSAGEID); |
| |
| switch (message_id) { |
| default: |
| /* handle HTC control message */ |
| if (target->CtrlResponseProcessing) { |
| /* this is a fatal error, target should not be sending unsolicited messages |
| * on the endpoint 0 */ |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HTC Rx Ctrl still processing\n")); |
| status = A_ERROR; |
| VOS_BUG(FALSE); |
| break; |
| } |
| |
| LOCK_HTC_RX(target); |
| target->CtrlResponseLength = min((int)netlen,HTC_MAX_CONTROL_MESSAGE_LENGTH); |
| A_MEMCPY(target->CtrlResponseBuffer,netdata,target->CtrlResponseLength); |
| |
| /* Requester will clear this flag */ |
| target->CtrlResponseProcessing = TRUE; |
| UNLOCK_HTC_RX(target); |
| |
| adf_os_complete(&target->CtrlResponseValid); |
| break; |
| case HTC_MSG_SEND_SUSPEND_COMPLETE: |
| wow_nack = 0; |
| LOCK_HTC_CREDIT(target); |
| htc_credit_record(HTC_SUSPEND_ACK, pEndpoint->TxCredits, |
| HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)); |
| UNLOCK_HTC_CREDIT(target); |
| target->HTCInitInfo.TargetSendSuspendComplete((void *)&wow_nack); |
| HTCsuspendwow(target); |
| break; |
| case HTC_MSG_NACK_SUSPEND: |
| wow_nack = 1; |
| LOCK_HTC_CREDIT(target); |
| htc_credit_record(HTC_SUSPEND_NACK, pEndpoint->TxCredits, |
| HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)); |
| UNLOCK_HTC_CREDIT(target); |
| target->HTCInitInfo.TargetSendSuspendComplete((void *)&wow_nack); |
| break; |
| } |
| |
| adf_nbuf_free(netbuf); |
| netbuf = NULL; |
| break; |
| } |
| |
| /* the current message based HIF architecture allocates net bufs for recv packets |
| * since this layer bridges that HIF to upper layers , which expects HTC packets, |
| * we form the packets here |
| * TODO_FIXME */ |
| pPacket = AllocateHTCPacketContainer(target); |
| if (NULL == pPacket) { |
| status = A_NO_RESOURCE; |
| break; |
| } |
| pPacket->Status = A_OK; |
| pPacket->Endpoint = htc_ep_id; |
| pPacket->pPktContext = netbuf; |
| pPacket->pBuffer = adf_nbuf_data(netbuf) + HTC_HDR_LENGTH; |
| pPacket->ActualLength = netlen - HTC_HEADER_LEN - trailerlen; |
| |
| adf_nbuf_pull_head(netbuf, HTC_HEADER_LEN); |
| adf_nbuf_set_pktlen(netbuf, pPacket->ActualLength); |
| |
| RecvPacketCompletion(target,pEndpoint,pPacket); |
| /* recover the packet container */ |
| FreeHTCPacketContainer(target,pPacket); |
| netbuf = NULL; |
| |
| } while(FALSE); |
| |
| #ifdef RX_SG_SUPPORT |
| _out: |
| #endif |
| |
| if (netbuf != NULL) { |
| adf_nbuf_free(netbuf); |
| } |
| |
| return status; |
| |
| } |
| |
| A_STATUS HTCAddReceivePktMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue) |
| { |
| HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| HTC_ENDPOINT *pEndpoint; |
| HTC_PACKET *pFirstPacket; |
| A_STATUS status = A_OK; |
| HTC_PACKET *pPacket; |
| |
| pFirstPacket = HTC_GET_PKT_AT_HEAD(pPktQueue); |
| |
| if (NULL == pFirstPacket) { |
| A_ASSERT(FALSE); |
| return A_EINVAL; |
| } |
| |
| AR_DEBUG_ASSERT(pFirstPacket->Endpoint < ENDPOINT_MAX); |
| |
| AR_DEBUG_PRINTF(ATH_DEBUG_RECV, |
| ("+- HTCAddReceivePktMultiple : endPointId: %d, cnt:%d, length: %d\n", |
| pFirstPacket->Endpoint, |
| HTC_PACKET_QUEUE_DEPTH(pPktQueue), |
| pFirstPacket->BufferLength)); |
| |
| pEndpoint = &target->EndPoint[pFirstPacket->Endpoint]; |
| |
| LOCK_HTC_RX(target); |
| |
| do { |
| |
| if (HTC_STOPPING(target)) { |
| status = A_ERROR; |
| break; |
| } |
| |
| /* store receive packets */ |
| HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->RxBufferHoldQueue,pPktQueue); |
| |
| } while (FALSE); |
| |
| UNLOCK_HTC_RX(target); |
| |
| if (A_FAILED(status)) { |
| /* walk through queue and mark each one canceled */ |
| HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) { |
| pPacket->Status = A_ECANCELED; |
| } HTC_PACKET_QUEUE_ITERATE_END; |
| |
| DoRecvCompletion(pEndpoint,pPktQueue); |
| } |
| |
| return status; |
| } |
| |
| |
| A_STATUS HTCAddReceivePkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) |
| { |
| HTC_PACKET_QUEUE queue; |
| INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket); |
| return HTCAddReceivePktMultiple(HTCHandle, &queue); |
| } |
| |
| void HTCFlushRxHoldQueue(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint) |
| { |
| HTC_PACKET *pPacket; |
| HTC_PACKET_QUEUE container; |
| |
| LOCK_HTC_RX(target); |
| |
| while (1) { |
| pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->RxBufferHoldQueue); |
| if (NULL == pPacket) { |
| break; |
| } |
| UNLOCK_HTC_RX(target); |
| pPacket->Status = A_ECANCELED; |
| pPacket->ActualLength = 0; |
| AR_DEBUG_PRINTF(ATH_DEBUG_RECV, (" Flushing RX packet:%pK, length:%d, ep:%d \n", |
| pPacket, pPacket->BufferLength, pPacket->Endpoint)); |
| INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket); |
| /* give the packet back */ |
| DoRecvCompletion(pEndpoint,&container); |
| LOCK_HTC_RX(target); |
| } |
| |
| UNLOCK_HTC_RX(target); |
| } |
| |
| void HTCRecvInit(HTC_TARGET *target) |
| { |
| /* Initialize CtrlResponseValid to block */ |
| adf_os_init_completion(&target->CtrlResponseValid); |
| } |
| |
| |
| /* polling routine to wait for a control packet to be received */ |
| A_STATUS HTCWaitRecvCtrlMessage(HTC_TARGET *target) |
| { |
| // int count = HTC_TARGET_MAX_RESPONSE_POLL; |
| |
| AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("+HTCWaitCtrlMessageRecv\n")); |
| |
| adf_os_re_init_completion(target->CtrlResponseValid); |
| /* Wait for BMI request/response transaction to complete */ |
| if(!adf_os_wait_for_completion_timeout(&target->CtrlResponseValid, |
| adf_os_msecs_to_ticks(HTC_CONTROL_RX_TIMEOUT))) { |
| if(hif_set_target_reset(target->hif_dev)) |
| VOS_BUG(0); |
| return A_ERROR; |
| } |
| |
| AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("-HTCWaitCtrlMessageRecv success\n")); |
| return A_OK; |
| } |
| |
| static A_STATUS HTCProcessTrailer(HTC_TARGET *target, |
| A_UINT8 *pBuffer, |
| int Length, |
| HTC_ENDPOINT_ID FromEndpoint) |
| { |
| HTC_RECORD_HDR *pRecord; |
| A_UINT8 htc_rec_id; |
| A_UINT8 htc_rec_len; |
| A_UINT8 *pRecordBuf; |
| 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); |
| |
| htc_rec_len = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, LENGTH); |
| htc_rec_id = HTC_GET_FIELD(pRecord, HTC_RECORD_HDR, RECORDID); |
| |
| if (htc_rec_len > 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", |
| htc_rec_len, htc_rec_id, Length)); |
| status = A_EPROTO; |
| break; |
| } |
| /* start of record follows the header */ |
| pRecordBuf = pBuffer; |
| |
| switch (htc_rec_id) { |
| case HTC_RECORD_CREDITS: |
| AR_DEBUG_ASSERT(htc_rec_len >= sizeof(HTC_CREDIT_REPORT)); |
| HTCProcessCreditRpt(target, |
| (HTC_CREDIT_REPORT *)pRecordBuf, |
| htc_rec_len / (sizeof(HTC_CREDIT_REPORT)), |
| FromEndpoint); |
| break; |
| |
| #ifdef HIF_SDIO |
| case HTC_RECORD_LOOKAHEAD: |
| /* Process in HIF layer */ |
| break; |
| |
| case HTC_RECORD_LOOKAHEAD_BUNDLE: |
| /* Process in HIF layer */ |
| break; |
| #endif /* HIF_SDIO */ |
| |
| default: |
| AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" HTC unhandled record: id:%d length:%d \n", |
| htc_rec_id, htc_rec_len)); |
| break; |
| } |
| |
| if (A_FAILED(status)) { |
| break; |
| } |
| |
| /* advance buffer past this record for next time around */ |
| pBuffer += htc_rec_len; |
| Length -= htc_rec_len; |
| } |
| |
| if (A_FAILED(status)) { |
| DebugDumpBytes(pOrigBuffer,origLength,"BAD Recv Trailer"); |
| } |
| |
| AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCProcessTrailer \n")); |
| return status; |
| |
| } |