blob: 8529941858c5e194e703a9fb811c2fa0eb40a8b1 [file] [log] [blame]
/*
* 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;
}