blob: 416c7ef1249b50c3bf1d473f414a5a521912916b [file] [log] [blame]
/*
* Copyright (c) 2014-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.
*/
#define ATH_MODULE_NAME hif
#include <qdf_types.h>
#include <qdf_status.h>
#include <qdf_timer.h>
#include <qdf_time.h>
#include <qdf_lock.h>
#include <qdf_mem.h>
#include <qdf_util.h>
#include <qdf_defer.h>
#include <qdf_atomic.h>
#include <qdf_nbuf.h>
#include <qdf_threads.h>
#include <athdefs.h>
#include <qdf_net_types.h>
#include <a_types.h>
#include <athdefs.h>
#include <a_osapi.h>
#include <hif.h>
#include <htc_services.h>
#include "hif_sdio_internal.h"
#include <htc_internal.h>
#include "regtable_sdio.h"
#include "if_sdio.h"
#define NBUF_ALLOC_FAIL_WAIT_TIME 100
static void hif_dev_dump_registers(struct hif_sdio_device *pdev,
struct MBOX_IRQ_PROC_REGISTERS *irq_proc_regs,
struct MBOX_IRQ_ENABLE_REGISTERS *
irq_enable_regs,
struct MBOX_COUNTER_REGISTERS *
mailbox_counter_registers)
{
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("RegTable->"));
if (irq_proc_regs != NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("HostIntStatus: 0x%x ",
irq_proc_regs->host_int_status));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("CPUIntStatus: 0x%x ",
irq_proc_regs->cpu_int_status));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("ErrorIntStatus: 0x%x ",
irq_proc_regs->error_int_status));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("CounterIntStatus: 0x%x ",
irq_proc_regs->counter_int_status));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("MboxFrame: 0x%x ",
irq_proc_regs->mbox_frame));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("\nRegTable->"));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("RxLKAValid: 0x%x ",
irq_proc_regs->rx_lookahead_valid));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("RxLKA0: 0x%x",
irq_proc_regs->rx_lookahead[0]));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("RxLKA1: 0x%x ",
irq_proc_regs->rx_lookahead[1]));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("RxLKA2: 0x%x ",
irq_proc_regs->rx_lookahead[2]));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("RxLKA3: 0x%x",
irq_proc_regs->rx_lookahead[3]));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("\nRegTable->"));
if (pdev->MailBoxInfo.gmbox_address != 0) {
/* if the target supports GMBOX hardware,
* dump some additional state
*/
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("GMBOX-HostIntStatus2: 0x%x ",
irq_proc_regs->host_int_status2));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("GMBOX-RX-Avail: 0x%x ",
irq_proc_regs->gmbox_rx_avail));
}
}
if (irq_enable_regs != NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("Int Status Enable: 0x%x\n",
irq_enable_regs->int_status_enable));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("Counter Int Status Enable: 0x%x\n",
irq_enable_regs->counter_int_status_enable));
}
if (mailbox_counter_registers != NULL) {
int i;
for (i = 0; i < 4; i++) {
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("Counter[%d]: 0x%x\n", i,
mailbox_counter_registers->
counter[i]));
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("<------------------------------->\n"));
}
static
QDF_STATUS hif_dev_alloc_and_prepare_rx_packets(struct hif_sdio_device *pdev,
uint32_t look_aheads[],
int messages,
HTC_PACKET_QUEUE *queue)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HTC_PACKET *packet;
HTC_FRAME_HDR *hdr;
int i, j;
int num_messages;
int full_length;
bool no_recycle;
/* lock RX while we assemble the packet buffers */
LOCK_HIF_DEV_RX(pdev);
for (i = 0; i < messages; i++) {
hdr = (HTC_FRAME_HDR *) &look_aheads[i];
if (hdr->EndpointID >= ENDPOINT_MAX) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid Endpoint in look-ahead: %d\n",
hdr->EndpointID));
/* invalid endpoint */
status = QDF_STATUS_E_PROTO;
break;
}
if (hdr->PayloadLen > HTC_MAX_PAYLOAD_LENGTH) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Payload length %d exceeds max HTC : %d !\n",
hdr->PayloadLen,
(uint32_t) HTC_MAX_PAYLOAD_LENGTH));
status = QDF_STATUS_E_PROTO;
break;
}
if ((hdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK) == 0) {
/* HTC header only indicates 1 message to fetch */
num_messages = 1;
} else {
/* HTC header indicates that every packet to follow
* has the same padded length so that it can
* be optimally fetched as a full bundle
*/
num_messages =
(hdr->Flags & HTC_FLAGS_RECV_BUNDLE_CNT_MASK)
>> HTC_FLAGS_RECV_BUNDLE_CNT_SHIFT;
/* the count doesn't include the starter frame, just
* a count of frames to follow
*/
num_messages++;
/* A_ASSERT(numMessages <= target->MaxMsgPerBundle); */
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("HTC header indicates :%d messages can be fetched as a bundle\n",
num_messages));
}
full_length =
DEV_CALC_RECV_PADDED_LEN(pdev,
hdr->PayloadLen +
sizeof(HTC_FRAME_HDR));
/* get packet buffers for each message, if there was a
* bundle detected in the header,
* use pHdr as a template to fetch all packets in the bundle
*/
for (j = 0; j < num_messages; j++) {
/* reset flag, any packets allocated using the
* RecvAlloc() API cannot be recycled on cleanup,
* they must be explicitly returned
*/
no_recycle = false;
packet = hif_dev_alloc_rx_buffer(pdev);
if (packet == NULL) {
/* No error, simply need to mark that
* we are waiting for buffers.
*/
pdev->RecvStateFlags |= HTC_RECV_WAIT_BUFFERS;
/* pDev->EpWaitingForBuffers = pEndpoint->Id; */
status = QDF_STATUS_E_RESOURCES;
break;
}
/* AR_DEBUG_ASSERT(pPacket->Endpoint == pEndpoint->Id);
*/
/* clear flags */
packet->PktInfo.AsRx.HTCRxFlags = 0;
packet->PktInfo.AsRx.IndicationFlags = 0;
packet->Status = QDF_STATUS_SUCCESS;
if (no_recycle)
/* flag that these packets cannot be recycled,
* they have to be returned to the user
*/
packet->PktInfo.AsRx.HTCRxFlags |=
HTC_RX_PKT_NO_RECYCLE;
/* add packet to queue (also incase we need to
* cleanup down below)
*/
HTC_PACKET_ENQUEUE(queue, packet);
/* if (HTC_STOPPING(target)) {
* status = QDF_STATUS_E_CANCELED;
* break;
* }
*/
/* make sure message can fit in the endpoint buffer */
if ((uint32_t) full_length > packet->BufferLength) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Payload Length Error : header reports payload of: %d (%d) endpoint buffer size: %d\n",
hdr->PayloadLen, full_length,
packet->BufferLength));
status = QDF_STATUS_E_PROTO;
break;
}
if (j > 0) {
/* for messages fetched in a bundle the expected
* lookahead is unknown as we are only using the
* lookahead of the first packet as a template
* of what to expect for lengths
*/
packet->PktInfo.AsRx.HTCRxFlags |=
HTC_RX_PKT_REFRESH_HDR;
/* set it to something invalid */
packet->PktInfo.AsRx.ExpectedHdr = 0xFFFFFFFF;
} else {
packet->PktInfo.AsRx.ExpectedHdr =
look_aheads[i];
}
/* set the amount of data to fetch */
packet->ActualLength =
hdr->PayloadLen + HTC_HDR_LENGTH;
packet->Endpoint = hdr->EndpointID;
packet->Completion = NULL;
}
if (QDF_IS_STATUS_ERROR(status)) {
if (QDF_STATUS_E_RESOURCES == status) {
/* this is actually okay */
status = QDF_STATUS_SUCCESS;
}
break;
}
}
UNLOCK_HIF_DEV_RX(pdev);
if (QDF_IS_STATUS_ERROR(status)) {
while (!HTC_QUEUE_EMPTY(queue))
packet = htc_packet_dequeue(queue);
}
return status;
}
static inline QDF_STATUS hif_dev_recv_packet(struct hif_sdio_device *pdev,
HTC_PACKET *packet,
uint32_t recv_length, uint8_t mbox_index)
{
uint32_t padded_length;
QDF_STATUS status;
bool sync = (packet->Completion == NULL) ? true : false;
/* adjust the length to be a multiple of block size if appropriate */
padded_length = DEV_CALC_RECV_PADDED_LEN(pdev, recv_length);
if (padded_length > packet->BufferLength) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("DevRecvPacket, Not enough space for padlen:%d recvlen:%d bufferlen:%d\n",
padded_length, recv_length,
packet->BufferLength));
if (packet->Completion != NULL) {
COMPLETE_HTC_PACKET(packet, QDF_STATUS_E_INVAL);
return QDF_STATUS_SUCCESS;
}
return QDF_STATUS_E_INVAL;
}
/* mailbox index is saved in Endpoint member */
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("hif_dev_recv_packet (0x%lX : hdr:0x%X) Len:%d, Padded Length: %d Mbox:0x%X\n",
(unsigned long)packet,
packet->PktInfo.AsRx.ExpectedHdr, recv_length,
padded_length,
pdev->MailBoxInfo.mbox_addresses[mbox_index]));
status = hif_read_write(pdev->HIFDevice,
pdev->MailBoxInfo.mbox_addresses[mbox_index],
packet->pBuffer, padded_length,
(sync ? HIF_RD_SYNC_BLOCK_FIX :
HIF_RD_ASYNC_BLOCK_FIX),
sync ? NULL : packet);
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("EP%d, Seq:%d\n",
((HTC_FRAME_HDR *) packet->pBuffer)->
EndpointID,
((HTC_FRAME_HDR *) packet->pBuffer)->
ControlBytes1));
if (status != QDF_STATUS_SUCCESS) {
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("hif_dev_recv_packet (0x%lX : hdr:0x%X) Failed\n",
(unsigned long)packet,
packet->PktInfo.AsRx.ExpectedHdr));
}
if (sync) {
packet->Status = status;
if (status == QDF_STATUS_SUCCESS) {
HTC_FRAME_HDR *hdr =
(HTC_FRAME_HDR *) packet->pBuffer;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("hif_dev_recv_packet EP:%d,Len:%d,Flag:%d,CB:0x%02X,0x%02X\n",
hdr->EndpointID, hdr->PayloadLen,
hdr->Flags, hdr->ControlBytes0,
hdr->ControlBytes1));
}
}
return status;
}
static inline QDF_STATUS hif_dev_process_trailer(struct hif_sdio_device *pdev,
uint8_t *buffer, int length,
uint32_t *next_look_aheads,
int *num_look_aheads,
HTC_ENDPOINT_ID from_endpoint)
{
HTC_RECORD_HDR *record;
uint8_t *record_buf;
HTC_LOOKAHEAD_REPORT *look_ahead;
uint8_t *orig_buffer;
int orig_length;
QDF_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(buffer, length, "Recv Trailer");
orig_buffer = buffer;
orig_length = length;
status = QDF_STATUS_SUCCESS;
while (length > 0) {
if (length < sizeof(HTC_RECORD_HDR)) {
status = QDF_STATUS_E_PROTO;
break;
}
/* these are byte aligned structs */
record = (HTC_RECORD_HDR *) buffer;
length -= sizeof(HTC_RECORD_HDR);
buffer += sizeof(HTC_RECORD_HDR);
if (record->Length > length) {
/* no room left in buffer for record */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
(" invalid record len: %d (id:%d) buffer has:%d bytes left\n",
record->Length, record->RecordID,
length));
status = QDF_STATUS_E_PROTO;
break;
}
/* start of record follows the header */
record_buf = buffer;
switch (record->RecordID) {
case HTC_RECORD_CREDITS:
/* Process in HTC, ignore here */
break;
case HTC_RECORD_LOOKAHEAD:
AR_DEBUG_ASSERT(record->Length >=
sizeof(HTC_LOOKAHEAD_REPORT));
look_ahead = (HTC_LOOKAHEAD_REPORT *) record_buf;
if ((look_ahead->PreValid ==
((~look_ahead->PostValid) & 0xFF))
&& (next_look_aheads != NULL)) {
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
(" look_ahead Report (pre valid:0x%X, post valid:0x%X) %d %d\n",
look_ahead->PreValid,
look_ahead->PostValid,
from_endpoint,
look_ahead->LookAhead0));
/* look ahead bytes are valid, copy them over */
((uint8_t *) (&next_look_aheads[0]))[0] =
look_ahead->LookAhead0;
((uint8_t *) (&next_look_aheads[0]))[1] =
look_ahead->LookAhead1;
((uint8_t *) (&next_look_aheads[0]))[2] =
look_ahead->LookAhead2;
((uint8_t *) (&next_look_aheads[0]))[3] =
look_ahead->LookAhead3;
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
debug_dump_bytes((uint8_t *)
next_look_aheads, 4,
"Next Look Ahead");
}
/* just one normal lookahead */
if (num_look_aheads != NULL)
*num_look_aheads = 1;
}
break;
case HTC_RECORD_LOOKAHEAD_BUNDLE:
AR_DEBUG_ASSERT(record->Length >=
sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT));
if (record->Length >=
sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT)
&& (next_look_aheads != NULL)) {
HTC_BUNDLED_LOOKAHEAD_REPORT
*pBundledLookAheadRpt;
int i;
pBundledLookAheadRpt =
(HTC_BUNDLED_LOOKAHEAD_REPORT *) record_buf;
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
debug_dump_bytes(record_buf,
record->Length,
"Bundle look_ahead");
}
if ((record->Length /
(sizeof(HTC_BUNDLED_LOOKAHEAD_REPORT)))
> HTC_MAX_MSG_PER_BUNDLE) {
/* this should never happen, the target
* restricts the number of messages per
* bundle configured by the host
*/
A_ASSERT(false);
status = QDF_STATUS_E_PROTO;
break;
}
for (i = 0;
i <
(int)(record->Length /
(sizeof
(HTC_BUNDLED_LOOKAHEAD_REPORT)));
i++) {
((uint8_t *)(&next_look_aheads[i]))[0] =
pBundledLookAheadRpt->LookAhead0;
((uint8_t *)(&next_look_aheads[i]))[1] =
pBundledLookAheadRpt->LookAhead1;
((uint8_t *)(&next_look_aheads[i]))[2] =
pBundledLookAheadRpt->LookAhead2;
((uint8_t *)(&next_look_aheads[i]))[3] =
pBundledLookAheadRpt->LookAhead3;
pBundledLookAheadRpt++;
}
if (num_look_aheads) {
*num_look_aheads = i;
}
}
break;
default:
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
(" HIF unhandled record: id:%d length:%d\n",
record->RecordID, record->Length));
break;
}
if (QDF_IS_STATUS_ERROR(status))
break;
/* advance buffer past this record for next time around */
buffer += record->Length;
length -= record->Length;
}
if (QDF_IS_STATUS_ERROR(status))
debug_dump_bytes(orig_buffer, orig_length,
"BAD Recv Trailer");
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-htc_process_trailer\n"));
return status;
}
/* process a received message (i.e. strip off header,
* process any trailer data).
* note : locks must be released when this function is called
*/
static QDF_STATUS hif_dev_process_recv_header(struct hif_sdio_device *pdev,
HTC_PACKET *packet,
uint32_t *next_look_aheads,
int *num_look_aheads)
{
uint8_t temp;
uint8_t *buf;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint16_t payloadLen;
uint32_t look_ahead, actual_length;
buf = packet->pBuffer;
actual_length = packet->ActualLength;
if (num_look_aheads != NULL)
*num_look_aheads = 0;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("+HTCProcessRecvHeader\n"));
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV))
AR_DEBUG_PRINTBUF(buf, packet->ActualLength, "HTC Recv PKT");
do {
/* note, we cannot assume the alignment of pBuffer,
* so we use the safe macros to
* retrieve 16 bit fields
*/
payloadLen = HTC_GET_FIELD(buf, HTC_FRAME_HDR,
PAYLOADLEN);
((uint8_t *) &look_ahead)[0] = buf[0];
((uint8_t *) &look_ahead)[1] = buf[1];
((uint8_t *) &look_ahead)[2] = buf[2];
((uint8_t *) &look_ahead)[3] = buf[3];
if (packet->PktInfo.AsRx.HTCRxFlags & HTC_RX_PKT_REFRESH_HDR) {
/* refresh expected hdr, since this was unknown
* at the time we grabbed the packets
* as part of a bundle
*/
packet->PktInfo.AsRx.ExpectedHdr = look_ahead;
/* refresh actual length since we now have the
* real header
*/
packet->ActualLength = payloadLen + HTC_HDR_LENGTH;
/* validate the actual header that was refreshed */
if (packet->ActualLength > packet->BufferLength) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid HDR payload length (%d) in bundled RECV (hdr: 0x%X)\n",
payloadLen, look_ahead));
/* limit this to max buffer just to print out
* some of the buffer
*/
packet->ActualLength =
min(packet->ActualLength,
packet->BufferLength);
status = QDF_STATUS_E_PROTO;
break;
}
if (packet->Endpoint
!= HTC_GET_FIELD(buf, HTC_FRAME_HDR, ENDPOINTID)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Refreshed HDR endpoint (%d) does not match expected endpoint (%d)\n",
HTC_GET_FIELD(buf,
HTC_FRAME_HDR,
ENDPOINTID),
packet->Endpoint));
status = QDF_STATUS_E_PROTO;
break;
}
}
if (look_ahead != packet->PktInfo.AsRx.ExpectedHdr) {
/* somehow the lookahead that gave us the full read
* length did not reflect the actual header
* in the pending message
*/
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("hif_dev_process_recv_header, lookahead mismatch! (pPkt:0x%lX flags:0x%X), 0x%08X != 0x%08X\n",
(unsigned long)packet,
packet->PktInfo.AsRx.HTCRxFlags,
look_ahead,
packet->PktInfo.AsRx.ExpectedHdr));
#ifdef ATH_DEBUG_MODULE
debug_dump_bytes((uint8_t *) &packet->PktInfo.AsRx.
ExpectedHdr, 4,
"Expected Message look_ahead");
debug_dump_bytes(buf, sizeof(HTC_FRAME_HDR),
"Current Frame Header");
#ifdef HTC_CAPTURE_LAST_FRAME
debug_dump_bytes((uint8_t *) &target->LastFrameHdr,
sizeof(HTC_FRAME_HDR),
"Last Frame Header");
if (target->LastTrailerLength != 0)
debug_dump_bytes(target->LastTrailer,
target->LastTrailerLength,
"Last trailer");
#endif
#endif
status = QDF_STATUS_E_PROTO;
break;
}
/* get flags */
temp = HTC_GET_FIELD(buf, HTC_FRAME_HDR, FLAGS);
if (temp & HTC_FLAGS_RECV_TRAILER) {
/* this packet has a trailer */
/* extract the trailer length in control byte 0 */
temp =
HTC_GET_FIELD(buf, HTC_FRAME_HDR,
CONTROLBYTES0);
if ((temp < sizeof(HTC_RECORD_HDR))
|| (temp > payloadLen)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("hif_dev_process_recv_header, invalid header(payloadlength should be :%d, CB[0] is:%d)\n",
payloadLen, temp));
status = QDF_STATUS_E_PROTO;
break;
}
if (packet->PktInfo.AsRx.
HTCRxFlags & HTC_RX_PKT_IGNORE_LOOKAHEAD) {
/* this packet was fetched as part of an HTC
* bundle as the lookahead is not valid.
* Next packet may have already been fetched as
* part of the bundle
*/
next_look_aheads = NULL;
num_look_aheads = NULL;
}
/* process trailer data that follows HDR and
* application payload
*/
status = hif_dev_process_trailer(pdev,
(buf + HTC_HDR_LENGTH +
payloadLen - temp), temp,
next_look_aheads,
num_look_aheads,
packet->Endpoint);
if (QDF_IS_STATUS_ERROR(status))
break;
}
} while (false);
if (QDF_IS_STATUS_ERROR(status)) {
/* dump the whole packet */
debug_dump_bytes(buf, packet->ActualLength,
"BAD HTC Recv PKT");
} else {
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
if (packet->ActualLength > 0) {
AR_DEBUG_PRINTBUF(packet->pBuffer,
packet->ActualLength,
"HTC - Application Msg");
}
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("-hif_dev_process_recv_header\n"));
return status;
}
static QDF_STATUS hif_dev_issue_recv_packet_bundle(struct hif_sdio_device *pdev,
HTC_PACKET_QUEUE *recv_pkt_queue,
HTC_PACKET_QUEUE *
sync_completion_queue,
uint8_t mail_box_index,
int *num_packets_fetched,
bool partial_bundle)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
int i, total_length = 0;
unsigned char *bundle_buffer = NULL;
HTC_PACKET *packet, *packet_rx_bundle;
HTC_TARGET *target = NULL;
uint32_t padded_length;
int bundleSpaceRemaining = 0;
target = (HTC_TARGET *) pdev->pTarget;
if ((HTC_PACKET_QUEUE_DEPTH(recv_pkt_queue) - HTC_MAX_MSG_PER_BUNDLE) >
0) {
partial_bundle = true;
AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
("%s, partial bundle detected num: %d, %d\n",
__func__,
HTC_PACKET_QUEUE_DEPTH(recv_pkt_queue),
HTC_MAX_MSG_PER_BUNDLE));
}
bundleSpaceRemaining =
HTC_MAX_MSG_PER_BUNDLE * target->TargetCreditSize;
packet_rx_bundle = allocate_htc_bundle_packet(target);
if (!packet_rx_bundle) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("%s: packet_rx_bundle is NULL\n", __func__));
qdf_sleep(NBUF_ALLOC_FAIL_WAIT_TIME); /* 100 msec sleep */
return QDF_STATUS_E_NOMEM;
}
bundle_buffer = packet_rx_bundle->pBuffer;
for (i = 0;
!HTC_QUEUE_EMPTY(recv_pkt_queue) && i < HTC_MAX_MSG_PER_BUNDLE;
i++) {
packet = htc_packet_dequeue(recv_pkt_queue);
A_ASSERT(packet != NULL);
if (!packet) {
break;
}
padded_length =
DEV_CALC_RECV_PADDED_LEN(pdev, packet->ActualLength);
if ((bundleSpaceRemaining - padded_length) < 0) {
/* exceeds what we can transfer, put the packet back */
HTC_PACKET_ENQUEUE_TO_HEAD(recv_pkt_queue, packet);
break;
}
bundleSpaceRemaining -= padded_length;
if (partial_bundle ||
HTC_PACKET_QUEUE_DEPTH(recv_pkt_queue) > 0) {
packet->PktInfo.AsRx.HTCRxFlags |=
HTC_RX_PKT_IGNORE_LOOKAHEAD;
}
packet->PktInfo.AsRx.HTCRxFlags |= HTC_RX_PKT_PART_OF_BUNDLE;
if (sync_completion_queue) {
HTC_PACKET_ENQUEUE(sync_completion_queue, packet);
}
total_length += padded_length;
}
#ifdef DEBUG_BUNDLE
qdf_print("Recv bundle count %d, length %d.\n",
sync_completion_queue ?
HTC_PACKET_QUEUE_DEPTH(sync_completion_queue) : 0,
total_length);
#endif
status = hif_read_write(pdev->HIFDevice,
pdev->MailBoxInfo.
mbox_addresses[(int)mail_box_index],
bundle_buffer, total_length,
HIF_RD_SYNC_BLOCK_FIX, NULL);
if (status != QDF_STATUS_SUCCESS) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("%s, hif_send Failed status:%d\n",
__func__, status));
} else {
unsigned char *buffer = bundle_buffer;
*num_packets_fetched = i;
if (sync_completion_queue) {
HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(
sync_completion_queue, packet) {
padded_length =
DEV_CALC_RECV_PADDED_LEN(pdev,
packet->ActualLength);
A_MEMCPY(packet->pBuffer,
buffer, padded_length);
buffer += padded_length;
} HTC_PACKET_QUEUE_ITERATE_END;
}
}
/* free bundle space under Sync mode */
free_htc_bundle_packet(target, packet_rx_bundle);
return status;
}
static inline void hif_dev_free_recv_pkt_queue(HTC_PACKET_QUEUE *recv_pkt_queue)
{
HTC_PACKET *packet;
qdf_nbuf_t netbuf;
while (!HTC_QUEUE_EMPTY(recv_pkt_queue)) {
packet = htc_packet_dequeue(recv_pkt_queue);
if (packet == NULL)
break;
netbuf = (qdf_nbuf_t) packet->
pNetBufContext;
if (netbuf)
qdf_nbuf_free(netbuf);
}
}
static
QDF_STATUS hif_dev_recv_message_pending_handler(struct hif_sdio_device *pdev,
uint8_t mail_box_index,
uint32_t msg_look_aheads[],
int num_look_aheads,
bool *async_proc,
int *num_pkts_fetched)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
HTC_PACKET *packet;
bool asyncProc = false;
uint32_t look_aheads[HTC_MAX_MSG_PER_BUNDLE];
int pkts_fetched;
HTC_PACKET_QUEUE recv_pkt_queue, sync_completed_pkts_queue;
bool partial_bundle;
HTC_ENDPOINT_ID id;
int total_fetched = 0;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
("+HTCRecvMessagePendingHandler NumLookAheads: %d\n",
num_look_aheads));
if (num_pkts_fetched != NULL)
*num_pkts_fetched = 0;
if (IS_DEV_IRQ_PROCESSING_ASYNC_ALLOWED(pdev)) {
/* We use async mode to get the packets if the
* device layer supports it. The device layer
* interfaces with HIF in which HIF may have
* restrictions on how interrupts are processed
*/
asyncProc = true;
}
if (async_proc != NULL)
/* indicate to caller how we decided to process this */
*async_proc = asyncProc;
if (num_look_aheads > HTC_MAX_MSG_PER_BUNDLE) {
A_ASSERT(false);
return QDF_STATUS_E_PROTO;
}
A_MEMCPY(look_aheads, msg_look_aheads,
(sizeof(uint32_t)) * num_look_aheads);
while (true) {
/* reset packets queues */
INIT_HTC_PACKET_QUEUE(&recv_pkt_queue);
INIT_HTC_PACKET_QUEUE(&sync_completed_pkts_queue);
if (num_look_aheads > HTC_MAX_MSG_PER_BUNDLE) {
status = QDF_STATUS_E_PROTO;
A_ASSERT(false);
break;
}
/* first lookahead sets the expected endpoint IDs for
* all packets in a bundle
*/
id = ((HTC_FRAME_HDR *) &look_aheads[0])->EndpointID;
if (id >= ENDPOINT_MAX) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("MsgPend, Invalid Endpoint in lookahead: %d\n",
id));
status = QDF_STATUS_E_PROTO;
break;
}
/* try to allocate as many HTC RX packets indicated
* by the lookaheads these packets are stored
* in the recvPkt queue
*/
status = hif_dev_alloc_and_prepare_rx_packets(pdev,
look_aheads,
num_look_aheads,
&recv_pkt_queue);
if (QDF_IS_STATUS_ERROR(status))
break;
total_fetched += HTC_PACKET_QUEUE_DEPTH(&recv_pkt_queue);
/* we've got packet buffers for all we can currently fetch,
* this count is not valid anymore
*/
num_look_aheads = 0;
partial_bundle = false;
/* now go fetch the list of HTC packets */
while (!HTC_QUEUE_EMPTY(&recv_pkt_queue)) {
pkts_fetched = 0;
if ((HTC_PACKET_QUEUE_DEPTH(&recv_pkt_queue) > 1)) {
/* there are enough packets to attempt a bundle
* transfer and recv bundling is allowed
*/
status = hif_dev_issue_recv_packet_bundle(pdev,
&recv_pkt_queue,
asyncProc ?
NULL :
&sync_completed_pkts_queue,
mail_box_index,
&pkts_fetched,
partial_bundle);
if (QDF_IS_STATUS_ERROR(status)) {
hif_dev_free_recv_pkt_queue(
&recv_pkt_queue);
break;
}
if (HTC_PACKET_QUEUE_DEPTH(&recv_pkt_queue) !=
0) {
/* we couldn't fetch all packets at one,
* time this creates a broken
* bundle
*/
partial_bundle = true;
}
}
/* see if the previous operation fetched any
* packets using bundling
*/
if (0 == pkts_fetched) {
/* dequeue one packet */
packet = htc_packet_dequeue(&recv_pkt_queue);
A_ASSERT(packet != NULL);
if (!packet) {
break;
}
packet->Completion = NULL;
if (HTC_PACKET_QUEUE_DEPTH(&recv_pkt_queue) >
0) {
/* lookaheads in all packets except the
* last one in must be ignored
*/
packet->PktInfo.AsRx.HTCRxFlags |=
HTC_RX_PKT_IGNORE_LOOKAHEAD;
}
/* go fetch the packet */
status =
hif_dev_recv_packet(pdev, packet,
packet->ActualLength,
mail_box_index);
if (QDF_IS_STATUS_ERROR(status))
break;
/* sent synchronously, queue this packet for
* synchronous completion
*/
HTC_PACKET_ENQUEUE(&sync_completed_pkts_queue,
packet);
}
}
/* synchronous handling */
if (pdev->DSRCanYield) {
/* for the SYNC case, increment count that tracks
* when the DSR should yield
*/
pdev->CurrentDSRRecvCount++;
}
/* in the sync case, all packet buffers are now filled,
* we can process each packet, check lookahead , then repeat
*/
/* unload sync completion queue */
while (!HTC_QUEUE_EMPTY(&sync_completed_pkts_queue)) {
uint8_t pipeid;
qdf_nbuf_t netbuf;
packet = htc_packet_dequeue(&sync_completed_pkts_queue);
A_ASSERT(packet != NULL);
if (!packet) {
break;
}
num_look_aheads = 0;
status =
hif_dev_process_recv_header(pdev, packet,
look_aheads,
&num_look_aheads);
if (QDF_IS_STATUS_ERROR(status))
break;
netbuf = (qdf_nbuf_t) packet->pNetBufContext;
/* set data length */
qdf_nbuf_put_tail(netbuf, packet->ActualLength);
if (pdev->hif_callbacks.rxCompletionHandler) {
pipeid =
hif_dev_map_mail_box_to_pipe(pdev,
mail_box_index,
true);
pdev->hif_callbacks.rxCompletionHandler(pdev->
hif_callbacks.
Context,
netbuf,
pipeid);
}
}
if (QDF_IS_STATUS_ERROR(status))
break;
if (num_look_aheads == 0) {
/* no more look aheads */
break;
}
/* check whether other OS contexts have queued any WMI
* command/data for WLAN. This check is needed only if WLAN
* Tx and Rx happens in same thread context
*/
/* A_CHECK_DRV_TX(); */
}
if (num_pkts_fetched != NULL)
*num_pkts_fetched = total_fetched;
AR_DEBUG_PRINTF(ATH_DEBUG_RECV, ("-HTCRecvMessagePendingHandler\n"));
return status;
}
/**
* hif_dev_service_cpu_interrupt() - service fatal interrupts
* synchronously
*
* @pDev: hif sdio device context
*
* Return: QDF_STATUS_SUCCESS for success
*/
static QDF_STATUS hif_dev_service_cpu_interrupt(struct hif_sdio_device *pdev)
{
QDF_STATUS status;
uint8_t cpu_int_status;
uint8_t reg_buffer[4];
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("CPU Interrupt\n"));
cpu_int_status = pdev->IrqProcRegisters.cpu_int_status
& pdev->IrqEnableRegisters.cpu_int_status_enable;
A_ASSERT(cpu_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("Valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
cpu_int_status));
/* Clear the interrupt */
pdev->IrqProcRegisters.cpu_int_status &= ~cpu_int_status;
/*set up the register transfer buffer to hit the register
* 4 times , this is done to make the access 4-byte aligned
* to mitigate issues with host bus interconnects that
* restrict bus transfer lengths to be a multiple of 4-bytes
* set W1C value to clear the interrupt, this hits the register
* first
*/
reg_buffer[0] = cpu_int_status;
/* the remaining 4 values are set to zero which have no-effect */
reg_buffer[1] = 0;
reg_buffer[2] = 0;
reg_buffer[3] = 0;
status = hif_read_write(pdev->HIFDevice,
CPU_INT_STATUS_ADDRESS,
reg_buffer, 4, HIF_WR_SYNC_BYTE_FIX, NULL);
A_ASSERT(status == QDF_STATUS_SUCCESS);
/* The Interrupt sent to the Host is generated via bit0
* of CPU INT register
*/
if (cpu_int_status & 0x1) {
if (pdev->hif_callbacks.fwEventHandler)
/* It calls into HTC which propagates this
* to ol_target_failure()
*/
pdev->hif_callbacks.fwEventHandler(pdev->hif_callbacks.
Context, QDF_STATUS_E_FAILURE);
} else
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
("%s: Unable to call fwEventHandler, invalid input arguments\n",
__func__));
return status;
}
/**
* hif_dev_service_error_interrupt() - service error interrupts
* synchronously
*
* @pDev: hif sdio device context
*
* Return: QDF_STATUS_SUCCESS for success
*/
static QDF_STATUS hif_dev_service_error_interrupt(struct hif_sdio_device *pdev)
{
QDF_STATUS status;
uint8_t error_int_status;
uint8_t reg_buffer[4];
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error Interrupt\n"));
error_int_status = pdev->IrqProcRegisters.error_int_status & 0x0F;
A_ASSERT(error_int_status);
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("Valid interrupt source in ERROR_INT_STATUS: 0x%x\n",
error_int_status));
if (ERROR_INT_STATUS_WAKEUP_GET(error_int_status)) {
/* Wakeup */
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error : Wakeup\n"));
}
if (ERROR_INT_STATUS_RX_UNDERFLOW_GET(error_int_status)) {
/* Rx Underflow */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Rx Underflow\n"));
}
if (ERROR_INT_STATUS_TX_OVERFLOW_GET(error_int_status)) {
/* Tx Overflow */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Tx Overflow\n"));
}
/* Clear the interrupt */
pdev->IrqProcRegisters.error_int_status &= ~error_int_status;
/* set up the register transfer buffer to hit the register
* 4 times , this is done to make the access 4-byte
* aligned to mitigate issues with host bus interconnects that
* restrict bus transfer lengths to be a multiple of 4-bytes
*/
/* set W1C value to clear the interrupt */
reg_buffer[0] = error_int_status;
/* the remaining 4 values are set to zero which have no-effect */
reg_buffer[1] = 0;
reg_buffer[2] = 0;
reg_buffer[3] = 0;
status = hif_read_write(pdev->HIFDevice,
ERROR_INT_STATUS_ADDRESS,
reg_buffer, 4, HIF_WR_SYNC_BYTE_FIX, NULL);
A_ASSERT(status == QDF_STATUS_SUCCESS);
return status;
}
/**
* hif_dev_service_debug_interrupt() - service debug interrupts
* synchronously
*
* @pDev: hif sdio device context
*
* Return: QDF_STATUS_SUCCESS for success
*/
static QDF_STATUS hif_dev_service_debug_interrupt(struct hif_sdio_device *pdev)
{
uint32_t dummy;
QDF_STATUS status;
/* Send a target failure event to the application */
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Target debug interrupt\n"));
/* clear the interrupt , the debug error interrupt is counter 0
* read counter to clear interrupt
*/
status = hif_read_write(pdev->HIFDevice,
COUNT_DEC_ADDRESS,
(uint8_t *) &dummy,
4, HIF_RD_SYNC_BYTE_INC, NULL);
A_ASSERT(status == QDF_STATUS_SUCCESS);
return status;
}
/**
* hif_dev_service_counter_interrupt() - service counter interrupts
* synchronously
*
* @pDev: hif sdio device context
*
* Return: QDF_STATUS_SUCCESS for success
*/
static
QDF_STATUS hif_dev_service_counter_interrupt(struct hif_sdio_device *pdev)
{
uint8_t counter_int_status;
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Counter Interrupt\n"));
counter_int_status = pdev->IrqProcRegisters.counter_int_status &
pdev->IrqEnableRegisters.counter_int_status_enable;
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("Valid interrupt source in COUNTER_INT_STATUS: 0x%x\n",
counter_int_status));
/* Check if the debug interrupt is pending
* NOTE: other modules like GMBOX may use the counter interrupt
* for credit flow control on other counters, we only need to
* check for the debug assertion counter interrupt
*/
if (counter_int_status & AR6K_TARGET_DEBUG_INTR_MASK)
return hif_dev_service_debug_interrupt(pdev);
return QDF_STATUS_SUCCESS;
}
/**
* hif_dev_process_pending_irqs() - process pending interrupts
* synchronously
*
* @pDev: hif sdio device context
* @pDone: pending irq completion status
* @pASyncProcessing: sync/async processing flag
* Return: QDF_STATUS_SUCCESS for success
*/
static QDF_STATUS hif_dev_process_pending_irqs(struct hif_sdio_device *pdev,
bool *done,
bool *async_processing)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t host_int_status = 0;
uint32_t look_ahead[MAILBOX_USED_COUNT];
int i;
qdf_mem_zero(&look_ahead, sizeof(look_ahead));
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("+ProcessPendingIRQs: (dev: 0x%lX)\n",
(unsigned long)pdev));
/* NOTE: the HIF implementation guarantees that the context
* of this call allows us to perform SYNCHRONOUS I/O,
* that is we can block, sleep or call any API that
* can block or switch thread/task ontexts.
* This is a fully schedulable context.
*/
do {
if (pdev->IrqEnableRegisters.int_status_enable == 0) {
/* interrupt enables have been cleared, do not try
* to process any pending interrupts that
* may result in more bus transactions.
* The target may be unresponsive at this point.
*/
break;
}
status = hif_read_write(pdev->HIFDevice,
HOST_INT_STATUS_ADDRESS,
(uint8_t *) &pdev->IrqProcRegisters,
sizeof(pdev->IrqProcRegisters),
HIF_RD_SYNC_BYTE_INC, NULL);
if (QDF_IS_STATUS_ERROR(status))
break;
if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) {
hif_dev_dump_registers(pdev,
&pdev->IrqProcRegisters,
&pdev->IrqEnableRegisters,
&pdev->MailBoxCounterRegisters);
}
/* Update only those registers that are enabled */
host_int_status = pdev->IrqProcRegisters.host_int_status
& pdev->IrqEnableRegisters.int_status_enable;
/* only look at mailbox status if the HIF layer did not
* provide this function, on some HIF interfaces reading
* the RX lookahead is not valid to do
*/
for (i = 0; i < MAILBOX_USED_COUNT; i++) {
look_ahead[i] = 0;
if (host_int_status & (1 << i)) {
/* mask out pending mailbox value, we use
* "lookAhead" as the real flag for
* mailbox processing below
*/
host_int_status &= ~(1 << i);
if (pdev->IrqProcRegisters.
rx_lookahead_valid & (1 << i)) {
/* mailbox has a message and the
* look ahead is valid
*/
look_ahead[i] =
pdev->
IrqProcRegisters.rx_lookahead[
MAILBOX_LOOKAHEAD_SIZE_IN_WORD *
i];
}
}
} /*end of for loop */
} while (false);
do {
bool bLookAheadValid = false;
/* did the interrupt status fetches succeed? */
if (QDF_IS_STATUS_ERROR(status))
break;
for (i = 0; i < MAILBOX_USED_COUNT; i++) {
if (look_ahead[i] != 0) {
bLookAheadValid = true;
break;
}
}
if ((0 == host_int_status) && !bLookAheadValid) {
/* nothing to process, the caller can use this
* to break out of a loop
*/
*done = true;
break;
}
if (bLookAheadValid) {
for (i = 0; i < MAILBOX_USED_COUNT; i++) {
int fetched = 0;
if (look_ahead[i] == 0)
continue;
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("Pending mailbox[%d] message, look_ahead: 0x%X\n",
i, look_ahead[i]));
/* Mailbox Interrupt, the HTC layer may issue
* async requests to empty the mailbox...
* When emptying the recv mailbox we use the
* async handler from the completion routine of
* routine of the callers read request.
* This can improve performance by reducing
* the context switching when we rapidly
* pull packets
*/
status = hif_dev_recv_message_pending_handler(
pdev, i,
&look_ahead
[i], 1,
async_processing,
&fetched);
if (QDF_IS_STATUS_ERROR(status))
break;
if (!fetched) {
/* HTC could not pull any messages out
* due to lack of resources force DSR
* handle to ack the interrupt
*/
*async_processing = false;
pdev->RecheckIRQStatusCnt = 0;
}
}
}
/* now handle the rest of them */
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
(" Valid interrupt source for OTHER interrupts: 0x%x\n",
host_int_status));
if (HOST_INT_STATUS_CPU_GET(host_int_status)) {
/* CPU Interrupt */
status = hif_dev_service_cpu_interrupt(pdev);
if (QDF_IS_STATUS_ERROR(status))
break;
}
if (HOST_INT_STATUS_ERROR_GET(host_int_status)) {
/* Error Interrupt */
status = hif_dev_service_error_interrupt(pdev);
if (QDF_IS_STATUS_ERROR(status))
break;
}
if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) {
/* Counter Interrupt */
status = hif_dev_service_counter_interrupt(pdev);
if (QDF_IS_STATUS_ERROR(status))
break;
}
} while (false);
/* an optimization to bypass reading the IRQ status registers
* unecessarily which can re-wake the target, if upper layers
* determine that we are in a low-throughput mode, we can
* rely on taking another interrupt rather than re-checking
* the status registers which can re-wake the target.
*
* NOTE : for host interfaces that use the special
* GetPendingEventsFunc, this optimization cannot be used due to
* possible side-effects. For example, SPI requires the host
* to drain all messages from the mailbox before exiting
* the ISR routine.
*/
if (!(*async_processing) && (pdev->RecheckIRQStatusCnt == 0)) {
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("Bypassing IRQ Status re-check, forcing done\n"));
*done = true;
}
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("-ProcessPendingIRQs: (done:%d, async:%d) status=%d\n",
*done, *async_processing, status));
return status;
}
#define DEV_CHECK_RECV_YIELD(pdev) \
((pdev)->CurrentDSRRecvCount >= \
(pdev)->HifIRQYieldParams.recv_packet_yield_count)
/**
* hif_dev_dsr_handler() - Synchronous interrupt handler
*
* @context: hif send context
*
* Return: 0 for success and non-zero for failure
*/
QDF_STATUS hif_dev_dsr_handler(void *context)
{
struct hif_sdio_device *pdev = (struct hif_sdio_device *) context;
QDF_STATUS status = QDF_STATUS_SUCCESS;
bool done = false;
bool async_proc = false;
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
("+DevDsrHandler: (dev: 0x%lX)\n",
(unsigned long)pdev));
/* reset the recv counter that tracks when we need
* to yield from the DSR
*/
pdev->CurrentDSRRecvCount = 0;
/* reset counter used to flag a re-scan of IRQ
* status registers on the target
*/
pdev->RecheckIRQStatusCnt = 0;
while (!done) {
status = hif_dev_process_pending_irqs(pdev, &done, &async_proc);
if (QDF_IS_STATUS_ERROR(status))
break;
if (HIF_DEVICE_IRQ_SYNC_ONLY == pdev->HifIRQProcessingMode) {
/* the HIF layer does not allow async IRQ processing,
* override the asyncProc flag
*/
async_proc = false;
/* this will cause us to re-enter ProcessPendingIRQ()
* and re-read interrupt status registers.
* This has a nice side effect of blocking us until all
* async read requests are completed. This behavior is
* required as we do not allow ASYNC processing
* in interrupt handlers (like Windows CE)
*/
if (pdev->DSRCanYield && DEV_CHECK_RECV_YIELD(pdev))
/* ProcessPendingIRQs() pulled enough recv
* messages to satisfy the yield count, stop
* checking for more messages and return
*/
break;
}
if (async_proc) {
/* the function does some async I/O for performance,
* we need to exit the ISR immediately, the check below
* will prevent the interrupt from being
* Ack'd while we handle it asynchronously
*/
break;
}
}
if (QDF_IS_STATUS_SUCCESS(status) && !async_proc) {
/* Ack the interrupt only if :
* 1. we did not get any errors in processing interrupts
* 2. there are no outstanding async processing requests
*/
if (pdev->DSRCanYield) {
/* if the DSR can yield do not ACK the interrupt, there
* could be more pending messages. The HIF layer
* must ACK the interrupt on behalf of HTC
*/
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
(" Yield in effect (cur RX count: %d)\n",
pdev->CurrentDSRRecvCount));
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
(" Acking interrupt from DevDsrHandler\n"));
hif_ack_interrupt(pdev->HIFDevice);
}
}
AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("-DevDsrHandler\n"));
return status;
}