|
@@ -1,1538 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
|
|
|
- *
|
|
|
- * 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.
|
|
|
- */
|
|
|
-
|
|
|
-#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
|
|
|
-/* high nibble */
|
|
|
-#define BUNDLE_COUNT_HIGH(f) ((f & 0x0C) << 2)
|
|
|
-/* low nibble */
|
|
|
-#define BUNDLE_COUNT_LOW(f) ((f & 0xF0) >> 4)
|
|
|
-#define GET_RECV_BUNDLE_COUNT(f) (BUNDLE_COUNT_HIGH(f) + BUNDLE_COUNT_LOW(f))
|
|
|
-
|
|
|
-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 = GET_RECV_BUNDLE_COUNT(hdr->Flags);
|
|
|
- /* 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;
|
|
|
- if ((j == (num_messages-1))
|
|
|
- && ((hdr->Flags) & HTC_FLAGS_RECV_1MORE_BLOCK))
|
|
|
- packet->PktInfo.AsRx.HTCRxFlags |=
|
|
|
- HTC_RX_PKT_LAST_BUNDLED_PKT_HAS_ADDTIONAL_BLOCK;
|
|
|
- packet->Endpoint = hdr->EndpointID;
|
|
|
- packet->Completion = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- UNLOCK_HIF_DEV_RX(pdev);
|
|
|
-
|
|
|
- /* for NO RESOURCE error, no need to flush data queue */
|
|
|
- if (QDF_IS_STATUS_ERROR(status)
|
|
|
- && (status != QDF_STATUS_E_RESOURCES)) {
|
|
|
- while (!HTC_QUEUE_EMPTY(queue)) {
|
|
|
- qdf_nbuf_t netbuf;
|
|
|
- packet = htc_packet_dequeue(queue);
|
|
|
- if (packet == NULL)
|
|
|
- break;
|
|
|
- netbuf = (qdf_nbuf_t) packet->pNetBufContext;
|
|
|
- if (netbuf)
|
|
|
- qdf_nbuf_free(netbuf);
|
|
|
- }
|
|
|
- }
|
|
|
- if (status == QDF_STATUS_E_RESOURCES)
|
|
|
- status = QDF_STATUS_SUCCESS;
|
|
|
- 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_RX) {
|
|
|
- /* 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_RX) > 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_RX));
|
|
|
- }
|
|
|
-
|
|
|
- bundleSpaceRemaining =
|
|
|
- HTC_MAX_MSG_PER_BUNDLE_RX * 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_RX;
|
|
|
- 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 (packet->PktInfo.AsRx.HTCRxFlags &
|
|
|
- HTC_RX_PKT_LAST_BUNDLED_PKT_HAS_ADDTIONAL_BLOCK)
|
|
|
- padded_length += HIF_MBOX_BLOCK_SIZE;
|
|
|
- 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;
|
|
|
- }
|
|
|
-#if 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);
|
|
|
- if (packet->PktInfo.AsRx.HTCRxFlags &
|
|
|
- HTC_RX_PKT_LAST_BUNDLED_PKT_HAS_ADDTIONAL_BLOCK)
|
|
|
- padded_length +=
|
|
|
- HIF_MBOX_BLOCK_SIZE;
|
|
|
- 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_RX];
|
|
|
- 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_RX) {
|
|
|
- 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_RX) {
|
|
|
- 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)) {
|
|
|
- while (!HTC_QUEUE_EMPTY(&recv_pkt_queue)) {
|
|
|
- qdf_nbuf_t netbuf;
|
|
|
- packet =
|
|
|
- htc_packet_dequeue(&recv_pkt_queue);
|
|
|
- if (packet == NULL)
|
|
|
- break;
|
|
|
- netbuf =
|
|
|
- (qdf_nbuf_t) packet->pNetBufContext;
|
|
|
- if (netbuf)
|
|
|
- qdf_nbuf_free(netbuf);
|
|
|
- }
|
|
|
- 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)) {
|
|
|
- HTC_PACKET_ENQUEUE_TO_HEAD(&sync_completed_pkts_queue,
|
|
|
- packet);
|
|
|
- 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)) {
|
|
|
- if (!HTC_QUEUE_EMPTY(&sync_completed_pkts_queue))
|
|
|
- hif_dev_free_recv_pkt_queue(
|
|
|
- &sync_completed_pkts_queue);
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|