Files
android_kernel_samsung_sm86…/htc/htc_recv.c
Mukul Sharma 514201712a qcacmn: Add callback context for wake up / suspend ack callback's
Currently, Host initial wake up, suspend ack callback do not
register callback context with host-to-target communication layer.
As now PMO is handling now these callback, objmgr psoc is required
as callback context.

Hence as part of the fix, Add supports for callback context for
these API's.

Change-Id: I83d513764792b800a8540cdbe847aad552df0ab1
CRs-Fixed: 2015229
2017-03-08 13:00:40 -08:00

773 line
19 KiB
C

/*
* 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 <qdf_nbuf.h> /* qdf_nbuf_t */
/* HTC Control message receive timeout msec */
#define HTC_CONTROL_RX_TIMEOUT 3000
#if defined(WLAN_DEBUG) || defined(DEBUG)
void debug_dump_bytes(uint8_t *buffer, uint16_t length, char *pDescription)
{
int8_t stream[60];
int8_t byteOffsetStr[10];
uint32_t i;
uint16_t 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);
qdf_mem_zero(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 debug_dump_bytes(uint8_t *buffer, uint16_t length, char *pDescription)
{
}
#endif
static A_STATUS htc_process_trailer(HTC_TARGET *target,
uint8_t *pBuffer,
int Length, HTC_ENDPOINT_ID FromEndpoint);
static void do_recv_completion(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);
if (pEndpoint->EpCallBacks.EpRecv == NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("HTC ep %d has NULL recv callback on packet %p\n",
pEndpoint->Id,
pPacket));
continue;
}
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("HTC calling ep %d recv callback on packet %p\n",
pEndpoint->Id, pPacket));
pEndpoint->EpCallBacks.EpRecv(pEndpoint->
EpCallBacks.
pContext,
pPacket);
}
}
} while (false);
}
static void recv_packet_completion(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint,
HTC_PACKET *pPacket)
{
HTC_PACKET_QUEUE container;
INIT_HTC_PACKET_QUEUE_AND_ADD(&container, pPacket);
/* do completion */
do_recv_completion(pEndpoint, &container);
}
void htc_control_rx_complete(void *Context, HTC_PACKET *pPacket)
{
/* TODO, can't really receive HTC control messages yet.... */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid call to htc_control_rx_complete\n"));
}
void htc_unblock_recv(HTC_HANDLE HTCHandle)
{
/* TODO find the Need in new model */
}
void htc_enable_recv(HTC_HANDLE HTCHandle)
{
/* TODO find the Need in new model */
}
void htc_disable_recv(HTC_HANDLE HTCHandle)
{
/* TODO find the Need in new model */
}
int htc_get_num_recv_buffers(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 *allocate_htc_packet_container(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 free_htc_packet_container(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
qdf_nbuf_t rx_sg_to_single_netbuf(HTC_TARGET *target)
{
qdf_nbuf_t skb;
uint8_t *anbdata;
uint8_t *anbdata_new;
uint32_t anblen;
qdf_nbuf_t new_skb = NULL;
uint32_t sg_queue_len;
qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue;
sg_queue_len = qdf_nbuf_queue_len(rx_sg_queue);
if (sg_queue_len <= 1) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("rx_sg_to_single_netbuf: invalid sg queue len %u\n"));
goto _failed;
}
new_skb = qdf_nbuf_alloc(target->ExpRxSgTotalLen, 0, 4, false);
if (new_skb == NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("rx_sg_to_single_netbuf: can't allocate %u size netbuf\n",
target->ExpRxSgTotalLen));
goto _failed;
}
qdf_nbuf_peek_header(new_skb, &anbdata_new, &anblen);
skb = qdf_nbuf_queue_remove(rx_sg_queue);
do {
qdf_nbuf_peek_header(skb, &anbdata, &anblen);
qdf_mem_copy(anbdata_new, anbdata, qdf_nbuf_len(skb));
qdf_nbuf_put_tail(new_skb, qdf_nbuf_len(skb));
anbdata_new += qdf_nbuf_len(skb);
qdf_nbuf_free(skb);
skb = qdf_nbuf_queue_remove(rx_sg_queue);
} while (skb != NULL);
RESET_RX_SG_CONFIG(target);
return new_skb;
_failed:
while ((skb = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL)
qdf_nbuf_free(skb);
RESET_RX_SG_CONFIG(target);
return NULL;
}
#endif
#ifdef CONFIG_WIN
#define HTC_MSG_NACK_SUSPEND 7
#endif
QDF_STATUS htc_rx_completion_handler(void *Context, qdf_nbuf_t netbuf,
uint8_t pipeID)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HTC_FRAME_HDR *HtcHdr;
HTC_TARGET *target = (HTC_TARGET *) Context;
uint8_t *netdata;
uint32_t netlen;
HTC_ENDPOINT *pEndpoint;
HTC_PACKET *pPacket;
uint16_t payloadLen;
uint32_t trailerlen = 0;
uint8_t htc_ep_id;
HTC_INIT_INFO *info;
#ifdef RX_SG_SUPPORT
LOCK_HTC_RX(target);
if (target->IsRxSgInprogress) {
target->CurRxSgTotalLen += qdf_nbuf_len(netbuf);
qdf_nbuf_queue_add(&target->RxSgQueue, netbuf);
if (target->CurRxSgTotalLen == target->ExpRxSgTotalLen) {
netbuf = rx_sg_to_single_netbuf(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 = qdf_nbuf_data(netbuf);
netlen = qdf_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));
debug_dump_bytes((uint8_t *) HtcHdr,
sizeof(HTC_FRAME_HDR), "BAD HTC Header");
status = QDF_STATUS_E_FAILURE;
QDF_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) {
htc_send_complete_check(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;
qdf_nbuf_queue_init(&target->RxSgQueue);
qdf_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));
debug_dump_bytes((uint8_t *) HtcHdr,
sizeof(HTC_FRAME_HDR),
"BAD RX packet length");
status = QDF_STATUS_E_FAILURE;
QDF_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)) { */
{
uint8_t temp;
A_STATUS temp_status;
/* 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,
("htc_rx_completion_handler, invalid header (payloadlength should be :%d, CB[0] is:%d)\n",
payloadLen, temp));
status = QDF_STATUS_E_INVAL;
break;
}
trailerlen = temp;
/* process trailer data that follows HDR + application payload */
temp_status = htc_process_trailer(target,
((uint8_t *) HtcHdr +
HTC_HDR_LENGTH +
payloadLen - temp),
temp, htc_ep_id);
if (A_FAILED(temp_status)) {
status = QDF_STATUS_E_FAILURE;
break;
}
}
}
if (((int)payloadLen - (int)trailerlen) <= 0) {
/* zero length packet with trailer data, just drop these */
break;
}
if (htc_ep_id == ENDPOINT_0) {
uint16_t message_id;
HTC_UNKNOWN_MSG *htc_msg;
bool wow_nack;
/* remove HTC header */
qdf_nbuf_pull_head(netbuf, HTC_HDR_LENGTH);
netdata = qdf_nbuf_data(netbuf);
netlen = qdf_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 = QDF_STATUS_E_FAILURE;
QDF_BUG(false);
break;
}
LOCK_HTC_RX(target);
target->CtrlResponseLength =
min((int)netlen,
HTC_MAX_CONTROL_MESSAGE_LENGTH);
qdf_mem_copy(target->CtrlResponseBuffer,
netdata,
target->CtrlResponseLength);
/* Requester will clear this flag */
target->CtrlResponseProcessing = true;
UNLOCK_HTC_RX(target);
qdf_event_set(&target->ctrl_response_valid);
break;
#ifdef HTC_MSG_WAKEUP_FROM_SUSPEND_ID
case HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("Received initial wake up"));
LOCK_HTC_CREDIT(target);
htc_credit_record(HTC_INITIAL_WAKE_UP,
pEndpoint->TxCredits,
HTC_PACKET_QUEUE_DEPTH(
&pEndpoint->TxQueue));
UNLOCK_HTC_CREDIT(target);
info = &target->HTCInitInfo;
if (info && info->target_initial_wakeup_cb)
info->target_initial_wakeup_cb(
info->target_psoc);
else
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("No initial wake up cb"));
break;
#endif
case HTC_MSG_SEND_SUSPEND_COMPLETE:
wow_nack = false;
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(
target->HTCInitInfo.target_psoc,
wow_nack);
break;
case HTC_MSG_NACK_SUSPEND:
wow_nack = true;
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(
target->HTCInitInfo.target_psoc,
wow_nack);
break;
}
qdf_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 = allocate_htc_packet_container(target);
if (NULL == pPacket) {
status = QDF_STATUS_E_RESOURCES;
break;
}
pPacket->Status = QDF_STATUS_SUCCESS;
pPacket->Endpoint = htc_ep_id;
pPacket->pPktContext = netbuf;
pPacket->pBuffer = qdf_nbuf_data(netbuf) + HTC_HDR_LENGTH;
pPacket->ActualLength = netlen - HTC_HEADER_LEN - trailerlen;
qdf_nbuf_pull_head(netbuf, HTC_HEADER_LEN);
qdf_nbuf_set_pktlen(netbuf, pPacket->ActualLength);
recv_packet_completion(target, pEndpoint, pPacket);
/* recover the packet container */
free_htc_packet_container(target, pPacket);
netbuf = NULL;
} while (false);
#ifdef RX_SG_SUPPORT
_out:
#endif
if (netbuf != NULL) {
qdf_nbuf_free(netbuf);
}
return status;
}
A_STATUS htc_add_receive_pkt_multiple(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,
("+- htc_add_receive_pkt_multiple : 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;
do_recv_completion(pEndpoint, pPktQueue);
}
return status;
}
A_STATUS htc_add_receive_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
HTC_PACKET_QUEUE queue;
INIT_HTC_PACKET_QUEUE_AND_ADD(&queue, pPacket);
return htc_add_receive_pkt_multiple(HTCHandle, &queue);
}
void htc_flush_rx_hold_queue(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:%p, length:%d, ep:%d \n",
pPacket, pPacket->BufferLength,
pPacket->Endpoint));
INIT_HTC_PACKET_QUEUE_AND_ADD(&container, pPacket);
/* give the packet back */
do_recv_completion(pEndpoint, &container);
LOCK_HTC_RX(target);
}
UNLOCK_HTC_RX(target);
}
void htc_recv_init(HTC_TARGET *target)
{
/* Initialize ctrl_response_valid to block */
qdf_event_create(&target->ctrl_response_valid);
}
/* polling routine to wait for a control packet to be received */
A_STATUS htc_wait_recv_ctrl_message(HTC_TARGET *target)
{
/* int count = HTC_TARGET_MAX_RESPONSE_POLL; */
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCWaitCtrlMessageRecv\n"));
/* Wait for BMI request/response transaction to complete */
if (qdf_wait_single_event(&target->ctrl_response_valid,
HTC_CONTROL_RX_TIMEOUT)) {
QDF_BUG(0);
return A_ERROR;
}
LOCK_HTC_RX(target);
/* caller will clear this flag */
target->CtrlResponseProcessing = true;
UNLOCK_HTC_RX(target);
#if 0
while (count > 0) {
LOCK_HTC_RX(target);
if (target->CtrlResponseValid) {
target->CtrlResponseValid = false;
/* caller will clear this flag */
target->CtrlResponseProcessing = true;
UNLOCK_HTC_RX(target);
break;
}
UNLOCK_HTC_RX(target);
count--;
A_MSLEEP(HTC_TARGET_RESPONSE_POLL_MS);
}
if (count <= 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("-HTCWaitCtrlMessageRecv: Timeout!\n"));
return A_ECOMM;
}
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCWaitCtrlMessageRecv success\n"));
return A_OK;
}
static A_STATUS htc_process_trailer(HTC_TARGET *target,
uint8_t *pBuffer,
int Length, HTC_ENDPOINT_ID FromEndpoint)
{
HTC_RECORD_HDR *pRecord;
uint8_t htc_rec_id;
uint8_t htc_rec_len;
uint8_t *pRecordBuf;
uint8_t *pOrigBuffer;
int origLength;
A_STATUS status;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("+htc_process_trailer (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));
htc_process_credit_rpt(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)) {
debug_dump_bytes(pOrigBuffer, origLength, "BAD Recv Trailer");
}
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-htc_process_trailer \n"));
return status;
}