/*
 * Copyright (c) 2013-2018 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 "htc_credit_history.h"
#include <qdf_mem.h>            /* qdf_mem_malloc */
#include <qdf_nbuf.h>           /* qdf_nbuf_t */
#include "qdf_module.h"

/* #define USB_HIF_SINGLE_PIPE_DATA_SCHED */
/* #ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED */
#define DATA_EP_SIZE 4
/* #endif */
#define HTC_DATA_RESOURCE_THRS 256
#define HTC_DATA_MINDESC_PERPACKET 2

enum HTC_SEND_QUEUE_RESULT {
	HTC_SEND_QUEUE_OK = 0,  /* packet was queued */
	HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */
};

#ifndef DEBUG_CREDIT
#define DEBUG_CREDIT 0
#endif

#if DEBUG_CREDIT
/* bit mask to enable debug certain endpoint */
static unsigned int ep_debug_mask =
	(1 << ENDPOINT_0) | (1 << ENDPOINT_1) | (1 << ENDPOINT_2);
#endif

#ifdef QCA_WIFI_NAPIER_EMULATION
#define HTC_EMULATION_DELAY_IN_MS 20
/**
 * htc_add_delay(): Adds a delay in before proceeding, only for emulation
 *
 * Return: None
 */
static inline void htc_add_emulation_delay(void)
{
	qdf_mdelay(HTC_EMULATION_DELAY_IN_MS);
}
#else
static inline void htc_add_emulation_delay(void)
{
}
#endif

void htc_dump_counter_info(HTC_HANDLE HTCHandle)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);

	AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
			("\n%s: ce_send_cnt = %d, TX_comp_cnt = %d\n",
			 __func__, target->ce_send_cnt, target->TX_comp_cnt));
}

int htc_get_tx_queue_depth(HTC_HANDLE *htc_handle, HTC_ENDPOINT_ID endpoint_id)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
	HTC_ENDPOINT *endpoint = &target->endpoint[endpoint_id];

	return HTC_PACKET_QUEUE_DEPTH(&endpoint->TxQueue);
}
qdf_export_symbol(htc_get_tx_queue_depth);

void htc_get_control_endpoint_tx_host_credits(HTC_HANDLE HTCHandle,
					      int *credits)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint;
	int i;

	if (!credits || !target) {
		AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: invalid args", __func__));
		return;
	}

	*credits = 0;
	LOCK_HTC_TX(target);
	for (i = 0; i < ENDPOINT_MAX; i++) {
		pEndpoint = &target->endpoint[i];
		if (pEndpoint->service_id == WMI_CONTROL_SVC) {
			*credits = pEndpoint->TxCredits;
			break;
		}
	}
	UNLOCK_HTC_TX(target);
}

static inline void restore_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket)
{
	qdf_nbuf_t netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);

	if (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) {
		qdf_nbuf_unmap(target->osdev, netbuf, QDF_DMA_TO_DEVICE);
		pPacket->PktInfo.AsTx.Flags &= ~HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
	}

	qdf_nbuf_pull_head(netbuf, sizeof(HTC_FRAME_HDR));
}

static void send_packet_completion(HTC_TARGET *target, HTC_PACKET *pPacket)
{
	HTC_ENDPOINT *pEndpoint = &target->endpoint[pPacket->Endpoint];
	HTC_EP_SEND_PKT_COMPLETE EpTxComplete;

	restore_tx_packet(target, pPacket);

	/* do completion */
	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("HTC calling ep %d send complete callback on packet %pK\n",
			 pEndpoint->Id, pPacket));

	EpTxComplete = pEndpoint->EpCallBacks.EpTxComplete;
	if (EpTxComplete != NULL)
		EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);
	else
		qdf_nbuf_free(pPacket->pPktContext);


}

void htc_send_complete_check_cleanup(void *context)
{
	HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *) context;

	htc_send_complete_check(pEndpoint, 1);
}

HTC_PACKET *allocate_htc_bundle_packet(HTC_TARGET *target)
{
	HTC_PACKET *pPacket;
	HTC_PACKET_QUEUE *pQueueSave;
	qdf_nbuf_t netbuf;

	LOCK_HTC_TX(target);
	if (NULL == target->pBundleFreeList) {
		UNLOCK_HTC_TX(target);
		netbuf = qdf_nbuf_alloc(NULL,
					target->MaxMsgsPerHTCBundle *
					target->TargetCreditSize, 0, 4, false);
		AR_DEBUG_ASSERT(netbuf);
		if (!netbuf)
			return NULL;
		pPacket = qdf_mem_malloc(sizeof(HTC_PACKET));
		AR_DEBUG_ASSERT(pPacket);
		if (!pPacket) {
			qdf_nbuf_free(netbuf);
			return NULL;
		}
		pQueueSave = qdf_mem_malloc(sizeof(HTC_PACKET_QUEUE));
		AR_DEBUG_ASSERT(pQueueSave);
		if (!pQueueSave) {
			qdf_nbuf_free(netbuf);
			qdf_mem_free(pPacket);
			return NULL;
		}
		INIT_HTC_PACKET_QUEUE(pQueueSave);
		pPacket->pContext = pQueueSave;
		SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
		pPacket->pBuffer = qdf_nbuf_data(netbuf);
		pPacket->BufferLength = qdf_nbuf_len(netbuf);

		/* store the original head room so that we can restore this
		 * when we "free" the packet.
		 * free packet puts the packet back on the free list
		 */
		pPacket->netbufOrigHeadRoom = qdf_nbuf_headroom(netbuf);
		return pPacket;
	}
	/* already done malloc - restore from free list */
	pPacket = target->pBundleFreeList;
	AR_DEBUG_ASSERT(pPacket);
	if (!pPacket) {
		UNLOCK_HTC_TX(target);
		return NULL;
	}
	target->pBundleFreeList = (HTC_PACKET *) pPacket->ListLink.pNext;
	UNLOCK_HTC_TX(target);
	pPacket->ListLink.pNext = NULL;

	return pPacket;
}

void free_htc_bundle_packet(HTC_TARGET *target, HTC_PACKET *pPacket)
{
	uint32_t curentHeadRoom;
	qdf_nbuf_t netbuf;
	HTC_PACKET_QUEUE *pQueueSave;

	netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
	AR_DEBUG_ASSERT(netbuf);
	if (!netbuf) {
		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
				("\n%s: Invalid netbuf in HTC Packet\n",
				__func__));
		return;
	}
	/* HIF adds data to the headroom section of the nbuf, restore thei
	 * original size. If this is not done, headroom keeps shrinking with
	 * every HIF send and eventually HIF ends up doing another malloc big
	 * enough to store the data + its header
	 */

	curentHeadRoom = qdf_nbuf_headroom(netbuf);
	qdf_nbuf_pull_head(netbuf,
			   pPacket->netbufOrigHeadRoom - curentHeadRoom);
	qdf_nbuf_trim_tail(netbuf, qdf_nbuf_len(netbuf));

	/* restore the pBuffer pointer. HIF changes this */
	pPacket->pBuffer = qdf_nbuf_data(netbuf);
	pPacket->BufferLength = qdf_nbuf_len(netbuf);

	/* restore queue */
	pQueueSave = (HTC_PACKET_QUEUE *) pPacket->pContext;
	AR_DEBUG_ASSERT(pQueueSave);

	INIT_HTC_PACKET_QUEUE(pQueueSave);

	LOCK_HTC_TX(target);
	if (target->pBundleFreeList == NULL) {
		target->pBundleFreeList = pPacket;
		pPacket->ListLink.pNext = NULL;
	} else {
		pPacket->ListLink.pNext = (DL_LIST *) target->pBundleFreeList;
		target->pBundleFreeList = pPacket;
	}
	UNLOCK_HTC_TX(target);
}

#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)

/**
 * htc_send_update_tx_bundle_stats() - update tx bundle stats depends
 *				on max bundle size
 * @target: hif context
 * @data_len: tx data len
 * @TxCreditSize: endpoint tx credit size
 *
 * Return: None
 */
static inline void
htc_send_update_tx_bundle_stats(HTC_TARGET *target,
				qdf_size_t data_len,
				int TxCreditSize)
{
	if ((data_len / TxCreditSize) <= HTC_MAX_MSG_PER_BUNDLE_TX)
		target->tx_bundle_stats[(data_len / TxCreditSize) - 1]++;
}

/**
 * htc_issue_tx_bundle_stats_inc() - increment in tx bundle stats
 *				on max bundle size
 * @target: hif context
 *
 * Return: None
 */
static inline void
htc_issue_tx_bundle_stats_inc(HTC_TARGET *target)
{
	target->tx_bundle_stats[0]++;
}
#else

static inline void
htc_send_update_tx_bundle_stats(HTC_TARGET *target,
				qdf_size_t data_len,
				int TxCreditSize)
{
}

static inline void
htc_issue_tx_bundle_stats_inc(HTC_TARGET *target)
{
}
#endif

#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
static QDF_STATUS htc_send_bundled_netbuf(HTC_TARGET *target,
					HTC_ENDPOINT *pEndpoint,
					unsigned char *pBundleBuffer,
					HTC_PACKET *pPacketTx)
{
	qdf_size_t data_len;
	QDF_STATUS status;
	qdf_nbuf_t bundleBuf;
	uint32_t data_attr = 0;

	bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
	data_len = pBundleBuffer - qdf_nbuf_data(bundleBuf);
	qdf_nbuf_put_tail(bundleBuf, data_len);
	SET_HTC_PACKET_INFO_TX(pPacketTx,
			       target,
			       pBundleBuffer,
			       data_len,
			       pEndpoint->Id, HTC_TX_PACKET_TAG_BUNDLED);
	LOCK_HTC_TX(target);
	HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacketTx);
	pEndpoint->ul_outstanding_cnt++;
	UNLOCK_HTC_TX(target);
#if DEBUG_BUNDLE
	qdf_print(" Send bundle EP%d buffer size:0x%x, total:0x%x, count:%d.\n",
		  pEndpoint->Id,
		  pEndpoint->TxCreditSize,
		  data_len, data_len / pEndpoint->TxCreditSize);
#endif

	htc_send_update_tx_bundle_stats(target, data_len,
					pEndpoint->TxCreditSize);

	status = hif_send_head(target->hif_dev,
			       pEndpoint->UL_PipeID,
			       pEndpoint->Id, data_len,
			       bundleBuf, data_attr);
	if (status != QDF_STATUS_SUCCESS) {
		qdf_print("%s:hif_send_head failed(len=%zu).\n", __func__,
			  data_len);
	}
	return status;
}

/**
 * htc_issue_packets_bundle() - HTC function to send bundle packets from a queue
 * @target: HTC target on which packets need to be sent
 * @pEndpoint: logical endpoint on which packets needs to be sent
 * @pPktQueue: HTC packet queue containing the list of packets to be sent
 *
 * Return: void
 */
static void htc_issue_packets_bundle(HTC_TARGET *target,
				     HTC_ENDPOINT *pEndpoint,
				     HTC_PACKET_QUEUE *pPktQueue)
{
	int i, frag_count, nbytes;
	qdf_nbuf_t netbuf, bundleBuf;
	unsigned char *pBundleBuffer = NULL;
	HTC_PACKET *pPacket = NULL, *pPacketTx = NULL;
	HTC_FRAME_HDR *pHtcHdr;
	int last_credit_pad = 0;
	int creditPad, creditRemainder, transferLength, bundlesSpaceRemaining =
		0;
	HTC_PACKET_QUEUE *pQueueSave = NULL;

	bundlesSpaceRemaining =
		target->MaxMsgsPerHTCBundle * pEndpoint->TxCreditSize;
	pPacketTx = allocate_htc_bundle_packet(target);
	if (!pPacketTx) {
		/* good time to panic */
		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
				("allocate_htc_bundle_packet failed\n"));
		AR_DEBUG_ASSERT(false);
		return;
	}
	bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
	pBundleBuffer = qdf_nbuf_data(bundleBuf);
	pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext;
	while (1) {
		pPacket = htc_packet_dequeue(pPktQueue);
		if (pPacket == NULL)
			break;
		creditPad = 0;
		transferLength = pPacket->ActualLength + HTC_HDR_LENGTH;
		creditRemainder = transferLength % pEndpoint->TxCreditSize;
		if (creditRemainder != 0) {
			if (transferLength < pEndpoint->TxCreditSize) {
				creditPad = pEndpoint->TxCreditSize -
					    transferLength;
			} else {
				creditPad = creditRemainder;
			}
			transferLength += creditPad;
		}

		if (bundlesSpaceRemaining < transferLength) {
			/* send out previous buffer */
			htc_send_bundled_netbuf(target, pEndpoint,
						pBundleBuffer - last_credit_pad,
						pPacketTx);
			/* One packet has been dequeued from sending queue when enter
			 * this loop, so need to add 1 back for this checking.
			 */
			if ((HTC_PACKET_QUEUE_DEPTH(pPktQueue) + 1) <
			    HTC_MIN_MSG_PER_BUNDLE) {
				HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
				return;
			}
			bundlesSpaceRemaining =
				target->MaxMsgsPerHTCBundle *
				pEndpoint->TxCreditSize;
			pPacketTx = allocate_htc_bundle_packet(target);
			if (!pPacketTx) {
				HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
				/* good time to panic */
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						("allocate_htc_bundle_packet failed\n"));
				AR_DEBUG_ASSERT(false);
				return;
			}
			bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
			pBundleBuffer = qdf_nbuf_data(bundleBuf);
			pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext;
		}

		bundlesSpaceRemaining -= transferLength;
		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);

		if (hif_get_bus_type(target->hif_dev) != QDF_BUS_TYPE_USB) {
			pHtcHdr = (HTC_FRAME_HDR *)qdf_nbuf_get_frag_vaddr(
								netbuf, 0);
			HTC_WRITE32(pHtcHdr,
				SM(pPacket->ActualLength,
				HTC_FRAME_HDR_PAYLOADLEN) |
				SM(pPacket->PktInfo.AsTx.SendFlags |
				HTC_FLAGS_SEND_BUNDLE,
				HTC_FRAME_HDR_FLAGS) |
				SM(pPacket->Endpoint,
				HTC_FRAME_HDR_ENDPOINTID));
			HTC_WRITE32((uint32_t *) pHtcHdr + 1,
				SM(pPacket->PktInfo.AsTx.SeqNo,
				HTC_FRAME_HDR_CONTROLBYTES1) | SM(creditPad,
				HTC_FRAME_HDR_RESERVED));
			pHtcHdr->reserved = creditPad;
		}
		frag_count = qdf_nbuf_get_num_frags(netbuf);
		nbytes = pPacket->ActualLength + HTC_HDR_LENGTH;
		for (i = 0; i < frag_count && nbytes > 0; i++) {
			int frag_len = qdf_nbuf_get_frag_len(netbuf, i);
			unsigned char *frag_addr =
				qdf_nbuf_get_frag_vaddr(netbuf, i);
			if (frag_len > nbytes)
				frag_len = nbytes;
			qdf_mem_copy(pBundleBuffer, frag_addr, frag_len);
			nbytes -= frag_len;
			pBundleBuffer += frag_len;
		}
		HTC_PACKET_ENQUEUE(pQueueSave, pPacket);
		pBundleBuffer += creditPad;

		/* last one can't be packed. */
		if (hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_USB)
			last_credit_pad = creditPad;
	}
	/* send out remaining buffer */
	if (pBundleBuffer != qdf_nbuf_data(bundleBuf))
		htc_send_bundled_netbuf(target, pEndpoint,
					pBundleBuffer - last_credit_pad,
					pPacketTx);
	else
		free_htc_bundle_packet(target, pPacketTx);
}
#endif /* ENABLE_BUNDLE_TX */
#else
static void htc_issue_packets_bundle(HTC_TARGET *target,
				     HTC_ENDPOINT *pEndpoint,
				     HTC_PACKET_QUEUE *pPktQueue)
{
}
#endif

/**
 * htc_issue_packets() - HTC function to send packets from a queue
 * @target: HTC target on which packets need to be sent
 * @pEndpoint: logical endpoint on which packets needs to be sent
 * @pPktQueue: HTC packet queue containing the list of packets to be sent
 *
 * Return: QDF_STATUS_SUCCESS on success and error QDF status on failure
 */
static QDF_STATUS htc_issue_packets(HTC_TARGET *target,
				  HTC_ENDPOINT *pEndpoint,
				  HTC_PACKET_QUEUE *pPktQueue)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	qdf_nbuf_t netbuf;
	HTC_PACKET *pPacket = NULL;
	uint16_t payloadLen;
	HTC_FRAME_HDR *pHtcHdr;
	uint32_t data_attr = 0;
	enum qdf_bus_type bus_type;
	QDF_STATUS ret;
	bool rt_put = false;

	bus_type = hif_get_bus_type(target->hif_dev);

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+htc_issue_packets: Queue: %pK, Pkts %d\n", pPktQueue,
			 HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
	while (true) {
		if (HTC_TX_BUNDLE_ENABLED(target) &&
		    HTC_PACKET_QUEUE_DEPTH(pPktQueue) >=
		    HTC_MIN_MSG_PER_BUNDLE) {
			switch (bus_type) {
			case QDF_BUS_TYPE_SDIO:
				if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint))
					break;
			case QDF_BUS_TYPE_USB:
				htc_issue_packets_bundle(target,
							pEndpoint,
							pPktQueue);
				break;
			default:
				break;
			}
		}
		/* if not bundling or there was a packet that could not be
		 * placed in a bundle, and send it by normal way
		 */
		pPacket = htc_packet_dequeue(pPktQueue);
		if (NULL == pPacket) {
			/* local queue is fully drained */
			break;
		}

		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
		AR_DEBUG_ASSERT(netbuf);
		/* Non-credit enabled endpoints have been mapped and setup by
		 * now, so no need to revisit the HTC headers
		 */
		if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {

			payloadLen = pPacket->ActualLength;
			/* setup HTC frame header */

			pHtcHdr = (HTC_FRAME_HDR *)
				qdf_nbuf_get_frag_vaddr(netbuf, 0);
			AR_DEBUG_ASSERT(pHtcHdr);

			HTC_WRITE32(pHtcHdr,
					SM(payloadLen,
						HTC_FRAME_HDR_PAYLOADLEN) |
					SM(pPacket->PktInfo.AsTx.SendFlags,
						HTC_FRAME_HDR_FLAGS) |
					SM(pPacket->Endpoint,
						HTC_FRAME_HDR_ENDPOINTID));
			HTC_WRITE32(((uint32_t *) pHtcHdr) + 1,
				    SM(pPacket->PktInfo.AsTx.SeqNo,
				       HTC_FRAME_HDR_CONTROLBYTES1));

			/*
			 * Now that the HTC frame header has been added, the
			 * netbuf can be mapped.  This only applies to non-data
			 * frames, since data frames were already mapped as they
			 * entered into the driver.
			 */
			pPacket->PktInfo.AsTx.Flags |=
				HTC_TX_PACKET_FLAG_FIXUP_NETBUF;

			ret = qdf_nbuf_map(target->osdev,
				GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
				QDF_DMA_TO_DEVICE);
			if (ret != QDF_STATUS_SUCCESS) {
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
					("%s nbuf Map Fail Endpnt %pK\n",
					__func__, pEndpoint));
				HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
				status = QDF_STATUS_E_FAILURE;
				break;
			}
		}

		if (!pEndpoint->async_update) {
			LOCK_HTC_TX(target);
		}
		/* store in look up queue to match completions */
		HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacket);
		INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
		pEndpoint->ul_outstanding_cnt++;
		if (!pEndpoint->async_update) {
			UNLOCK_HTC_TX(target);
			hif_send_complete_check(target->hif_dev,
					pEndpoint->UL_PipeID, false);
		}

		htc_packet_set_magic_cookie(pPacket, HTC_PACKET_MAGIC_COOKIE);
		/*
		 * For HTT messages without a response from fw,
		 *   do the runtime put here.
		 * otherwise runtime put will be done when the fw response comes
		 */
		if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_RUNTIME_PUT)
			rt_put = true;
#if DEBUG_BUNDLE
		qdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
			  pEndpoint->Id,
			  pEndpoint->TxCreditSize,
			  HTC_HDR_LENGTH + pPacket->ActualLength);
#endif
		status = hif_send_head(target->hif_dev,
				       pEndpoint->UL_PipeID, pEndpoint->Id,
				       HTC_HDR_LENGTH + pPacket->ActualLength,
				       netbuf, data_attr);

		htc_issue_tx_bundle_stats_inc(target);

		target->ce_send_cnt++;

		if (qdf_unlikely(QDF_IS_STATUS_ERROR(status))) {
			if (status != QDF_STATUS_E_RESOURCES) {
				/* TODO : if more than 1 endpoint maps to the
				 * same PipeID it is possible to run out of
				 * resources in the HIF layer. Don't emit the
				 * error
				 */
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						("hif_send Failed status:%d\n",
						 status));
			}
			qdf_nbuf_unmap(target->osdev,
				GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
				QDF_DMA_TO_DEVICE);
			if (!pEndpoint->async_update) {
				LOCK_HTC_TX(target);
			}
			target->ce_send_cnt--;
			pEndpoint->ul_outstanding_cnt--;
			HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacket);
			/* reclaim credits */
			pEndpoint->TxCredits +=
				pPacket->PktInfo.AsTx.CreditsUsed;
			htc_packet_set_magic_cookie(pPacket, 0);
			/* put it back into the callers queue */
			HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
			if (!pEndpoint->async_update) {
				UNLOCK_HTC_TX(target);
			}
			break;
		}
		if (rt_put) {
			hif_pm_runtime_put(target->hif_dev);
			rt_put = false;
		}
	}
	if (qdf_unlikely(QDF_IS_STATUS_ERROR(status))) {
		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
			("htc_issue_packets, failed pkt:0x%pK status:%d",
			 pPacket, status));
	}

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_issue_packets\n"));

	return status;
}

#ifdef FEATURE_RUNTIME_PM
/**
 * extract_htc_pm_packtes(): move pm packets from endpoint into queue
 * @endpoint: which enpoint to extract packets from
 * @queue: a queue to store extracted packets in.
 *
 * remove pm packets from the endpoint's tx queue.
 * queue them into a queue
 */
static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint,
				HTC_PACKET_QUEUE *queue)
{
	HTC_PACKET *packet;

	/* only WMI endpoint has power management packets */
	if (endpoint->service_id != WMI_CONTROL_SVC)
		return;

	ITERATE_OVER_LIST_ALLOW_REMOVE(&endpoint->TxQueue.QueueHead, packet,
			HTC_PACKET, ListLink) {
		if (packet->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_AUTO_PM) {
			HTC_PACKET_REMOVE(&endpoint->TxQueue, packet);
			HTC_PACKET_ENQUEUE(queue, packet);
		}
	} ITERATE_END
}

/**
 * queue_htc_pm_packets(): queue pm packets with priority
 * @endpoint: enpoint to queue packets to
 * @queue: queue of pm packets to enque
 *
 * suspend resume packets get special treatment & priority.
 * need to queue them at the front of the queue.
 */
static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint,
				 HTC_PACKET_QUEUE *queue)
{
	if (endpoint->service_id != WMI_CONTROL_SVC)
		return;

	HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&endpoint->TxQueue, queue);
}
#else
static void extract_htc_pm_packets(HTC_ENDPOINT *endpoint,
		HTC_PACKET_QUEUE *queue)
{}

static void queue_htc_pm_packets(HTC_ENDPOINT *endpoint,
		HTC_PACKET_QUEUE *queue)
{}
#endif

/**
 * get_htc_send_packets_credit_based() - get packets based on available credits
 * @target: HTC target on which packets need to be sent
 * @pEndpoint: logical endpoint on which packets needs to be sent
 * @pQueue: HTC packet queue containing the list of packets to be sent
 *
 * Get HTC send packets from TX queue on an endpoint based on available credits.
 * The function moves the packets from TX queue of the endpoint to pQueue.
 *
 * Return: None
 */
static void get_htc_send_packets_credit_based(HTC_TARGET *target,
					      HTC_ENDPOINT *pEndpoint,
					      HTC_PACKET_QUEUE *pQueue)
{
	int creditsRequired;
	int remainder;
	uint8_t sendFlags;
	HTC_PACKET *pPacket;
	unsigned int transferLength;
	HTC_PACKET_QUEUE *tx_queue;
	HTC_PACKET_QUEUE pm_queue;
	bool do_pm_get = false;

	/*** NOTE : the TX lock is held when this function is called ***/
	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+get_htc_send_packets_credit_based\n"));

	INIT_HTC_PACKET_QUEUE(&pm_queue);
	extract_htc_pm_packets(pEndpoint, &pm_queue);
	if (HTC_QUEUE_EMPTY(&pm_queue)) {
		tx_queue = &pEndpoint->TxQueue;
		do_pm_get = true;
	} else {
		tx_queue = &pm_queue;
	}

	/* loop until we can grab as many packets out of the queue as we can */
	while (true) {
		if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) {
			/* bus suspended, runtime resume issued */
			QDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
			break;
		}

		sendFlags = 0;
		/* get packet at head, but don't remove it */
		pPacket = htc_get_pkt_at_head(tx_queue);
		if (pPacket == NULL) {
			if (do_pm_get)
				hif_pm_runtime_put(target->hif_dev);
			break;
		}

		AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
				(" Got head packet:%pK , Queue Depth: %d\n",
				 pPacket,
				 HTC_PACKET_QUEUE_DEPTH(tx_queue)));

		transferLength = pPacket->ActualLength + HTC_HDR_LENGTH;

		if (transferLength <= pEndpoint->TxCreditSize) {
			creditsRequired = 1;
		} else {
			/* figure out how many credits this message requires */
			creditsRequired =
				transferLength / pEndpoint->TxCreditSize;
			remainder = transferLength % pEndpoint->TxCreditSize;

			if (remainder)
				creditsRequired++;
		}

		AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
				(" Credits Required:%d   Got:%d\n",
				 creditsRequired, pEndpoint->TxCredits));

		if (pEndpoint->Id == ENDPOINT_0) {
			/*
			 * endpoint 0 is special, it always has a credit and
			 * does not require credit based flow control
			 */
			creditsRequired = 0;
		} else {

			if (pEndpoint->TxCredits < creditsRequired) {
#if DEBUG_CREDIT
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						("EP%d,No Credit now.%d < %d\n",
						 pEndpoint->Id,
						 pEndpoint->TxCredits,
						 creditsRequired));
#endif
				if (do_pm_get)
					hif_pm_runtime_put(target->hif_dev);
				break;
			}

			pEndpoint->TxCredits -= creditsRequired;
			INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed,
					creditsRequired);

			/* check if we need credits back from the target */
			if (pEndpoint->TxCredits <=
			    pEndpoint->TxCreditsPerMaxMsg) {
				/* tell the target we need credits ASAP! */
				sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
				if (pEndpoint->service_id == WMI_CONTROL_SVC) {
					htc_credit_record(HTC_REQUEST_CREDIT,
							  pEndpoint->TxCredits,
							  HTC_PACKET_QUEUE_DEPTH
							  (tx_queue));
				}
				INC_HTC_EP_STAT(pEndpoint,
						TxCreditLowIndications, 1);
#if DEBUG_CREDIT
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						(" EP%d Needs Credits\n",
						 pEndpoint->Id));
#endif
			}
		}

		/* now we can fully dequeue */
		pPacket = htc_packet_dequeue(tx_queue);
		if (pPacket) {
			/* save the number of credits this packet consumed */
			pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired;
			/* save send flags */
			pPacket->PktInfo.AsTx.SendFlags = sendFlags;

			/* queue this packet into the caller's queue */
			HTC_PACKET_ENQUEUE(pQueue, pPacket);
		}
	}

	if (!HTC_QUEUE_EMPTY(&pm_queue))
		queue_htc_pm_packets(pEndpoint, &pm_queue);

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("-get_htc_send_packets_credit_based\n"));

}

static void get_htc_send_packets(HTC_TARGET *target,
				 HTC_ENDPOINT *pEndpoint,
				 HTC_PACKET_QUEUE *pQueue, int Resources)
{

	HTC_PACKET *pPacket;
	HTC_PACKET_QUEUE *tx_queue;
	HTC_PACKET_QUEUE pm_queue;
	bool do_pm_get = false;

	/*** NOTE : the TX lock is held when this function is called ***/
	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+get_htc_send_packets %d resources\n", Resources));

	INIT_HTC_PACKET_QUEUE(&pm_queue);
	extract_htc_pm_packets(pEndpoint, &pm_queue);
	if (HTC_QUEUE_EMPTY(&pm_queue)) {
		tx_queue = &pEndpoint->TxQueue;
		do_pm_get = true;
	} else {
		tx_queue = &pm_queue;
	}

	/* loop until we can grab as many packets out of the queue as we can */
	while (Resources > 0) {
		int num_frags;

		if (do_pm_get && hif_pm_runtime_get(target->hif_dev)) {
			/* bus suspended, runtime resume issued */
			QDF_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
			break;
		}

		pPacket = htc_packet_dequeue(tx_queue);
		if (pPacket == NULL) {
			if (do_pm_get)
				hif_pm_runtime_put(target->hif_dev);
			break;
		}
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
				(" Got packet:%pK , New Queue Depth: %d\n",
				 pPacket,
				 HTC_PACKET_QUEUE_DEPTH(tx_queue)));
		/* For non-credit path the sequence number is already embedded
		 * in the constructed HTC header
		 */
		pPacket->PktInfo.AsTx.SendFlags = 0;
		pPacket->PktInfo.AsTx.CreditsUsed = 0;
		/* queue this packet into the caller's queue */
		HTC_PACKET_ENQUEUE(pQueue, pPacket);

		/*
		 * FIX THIS:
		 * For now, avoid calling qdf_nbuf_get_num_frags before calling
		 * qdf_nbuf_map, because the MacOS version of qdf_nbuf_t doesn't
		 * support qdf_nbuf_get_num_frags until after qdf_nbuf_map has
		 * been done.
		 * Assume that the non-data netbufs, i.e. WMI message netbufs,
		 * consist of a single fragment.
		 */
		/* WMI messages are in a single-fragment network buf */
		num_frags =
			(pPacket->PktInfo.AsTx.
			 Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) ? 1 :
			qdf_nbuf_get_num_frags(GET_HTC_PACKET_NET_BUF_CONTEXT
						       (pPacket));
		Resources -= num_frags;
	}

	if (!HTC_QUEUE_EMPTY(&pm_queue))
		queue_htc_pm_packets(pEndpoint, &pm_queue);

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-get_htc_send_packets\n"));

}

/**
 * htc_try_send() - Send packets in a queue on an endpoint
 * @target: HTC target on which packets need to be sent
 * @pEndpoint: logical endpoint on which packets needs to be sent
 * @pCallersSendQueue: packet queue containing the list of packets to be sent
 *
 * Return: enum HTC_SEND_QUEUE_RESULT indicates whether the packet was queued to
 *         be sent or the packet should be dropped by the upper layer
 */
static enum HTC_SEND_QUEUE_RESULT htc_try_send(HTC_TARGET *target,
					  HTC_ENDPOINT *pEndpoint,
					  HTC_PACKET_QUEUE *pCallersSendQueue)
{
	/* temp queue to hold packets at various stages */
	HTC_PACKET_QUEUE sendQueue;
	HTC_PACKET *pPacket;
	int tx_resources;
	int overflow;
	enum HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+htc_try_send (Queue:%pK Depth:%d)\n",
					 pCallersSendQueue,
					 (pCallersSendQueue ==
					  NULL) ? 0 :
					 HTC_PACKET_QUEUE_DEPTH
						 (pCallersSendQueue)));

	/* init the local send queue */
	INIT_HTC_PACKET_QUEUE(&sendQueue);

	do {

		/* caller didn't provide a queue, just wants us to check
		 * queues and send
		 */
		if (pCallersSendQueue == NULL)
			break;

		if (HTC_QUEUE_EMPTY(pCallersSendQueue)) {
			/* empty queue */
			OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,
							HTC_PKT_Q_EMPTY);
			result = HTC_SEND_QUEUE_DROP;
			break;
		}

		if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >=
		    pEndpoint->MaxTxQueueDepth) {
			/* we've already overflowed */
			overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
		} else {
			/* figure out how much we will overflow by */
			overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
			overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
			/* get how much we will overflow the TX queue by */
			overflow -= pEndpoint->MaxTxQueueDepth;
		}

		/* if overflow is negative or zero, we are okay */
		if (overflow > 0) {
			AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
					("Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d\n",
					 pEndpoint->Id, overflow,
					 HTC_PACKET_QUEUE_DEPTH(&pEndpoint->
								TxQueue),
					 pEndpoint->MaxTxQueueDepth));
		}
		if ((overflow <= 0)
		    || (pEndpoint->EpCallBacks.EpSendFull == NULL)) {
			/* all packets will fit or caller did not provide send
			 * full indication handler
			 * just move all of them to local sendQueue object
			 */
			HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue,
							  pCallersSendQueue);
		} else {
			int i;
			int goodPkts =
				HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) -
				overflow;

			A_ASSERT(goodPkts >= 0);
			/* we have overflowed and callback is provided. Dequeue
			 * all non-overflow packets into the sendqueue
			 */
			for (i = 0; i < goodPkts; i++) {
				/* pop off caller's queue */
				pPacket = htc_packet_dequeue(pCallersSendQueue);
				A_ASSERT(pPacket != NULL);
				/* insert into local queue */
				HTC_PACKET_ENQUEUE(&sendQueue, pPacket);
			}

			/* the caller's queue has all the packets that won't fit
			 * walk through the caller's queue and indicate each one
			 * to the send full handler
			 */
			ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue->
						       QueueHead, pPacket,
						       HTC_PACKET, ListLink) {

				AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
						("Indicating overflowed TX packet: %pK\n",
						 pPacket));
				/*
				 * Remove headroom reserved for HTC_FRAME_HDR
				 * before giving the packet back to the user via
				 * the EpSendFull callback.
				 */
				restore_tx_packet(target, pPacket);

				if (pEndpoint->EpCallBacks.
				    EpSendFull(pEndpoint->EpCallBacks.pContext,
					       pPacket) == HTC_SEND_FULL_DROP) {
					/* callback wants the packet dropped */
					INC_HTC_EP_STAT(pEndpoint, TxDropped,
							1);
					/* leave this one in the caller's queue
					 * for cleanup
					 */
				} else {
					/* callback wants to keep this packet,
					 * remove from caller's queue
					 */
					HTC_PACKET_REMOVE(pCallersSendQueue,
							  pPacket);
					/* put it in the send queue
					 * add HTC_FRAME_HDR space reservation
					 * again
					 */
					qdf_nbuf_push_head
						(GET_HTC_PACKET_NET_BUF_CONTEXT
							(pPacket),
						sizeof(HTC_FRAME_HDR));

					HTC_PACKET_ENQUEUE(&sendQueue, pPacket);
				}

			}
			ITERATE_END;

			if (HTC_QUEUE_EMPTY(&sendQueue)) {
				/* no packets made it in, caller will cleanup */
				OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,
							HTC_SEND_Q_EMPTY);
				result = HTC_SEND_QUEUE_DROP;
				break;
			}
		}

	} while (false);

	if (result != HTC_SEND_QUEUE_OK) {
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send: %d\n",
			result));
		return result;
	}

	if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
		tx_resources =
			hif_get_free_queue_number(target->hif_dev,
						  pEndpoint->UL_PipeID);
	} else {
		tx_resources = 0;
	}

	LOCK_HTC_TX(target);

	if (!HTC_QUEUE_EMPTY(&sendQueue)) {
		if (target->is_nodrop_pkt) {
			/*
			 * nodrop pkts have higher priority than normal pkts,
			 * insert nodrop pkt to head for proper
			 * start/termination of test.
			 */
			HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,
					&sendQueue);
			target->is_nodrop_pkt = false;
		} else {
			/* transfer packets to tail */
			HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue,
					&sendQueue);
			A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue));
			INIT_HTC_PACKET_QUEUE(&sendQueue);
		}
	}

	/* increment tx processing count on entry */
	if (qdf_atomic_inc_return(&pEndpoint->TxProcessCount) > 1) {
		/* another thread or task is draining the TX queues on this
		 * endpoint that thread will reset the tx processing count when
		 * the queue is drained
		 */
		qdf_atomic_dec(&pEndpoint->TxProcessCount);
		UNLOCK_HTC_TX(target);
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send (busy)\n"));
		return HTC_SEND_QUEUE_OK;
	}

	/***** beyond this point only 1 thread may enter ******/

	/* now drain the endpoint TX queue for transmission as long as we have
	 * enough transmit resources
	 */
	while (true) {

		if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0)
			break;

		if (pEndpoint->async_update &&
			(!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) &&
			(!tx_resources))
			break;

		if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
#if DEBUG_CREDIT
			int cred = pEndpoint->TxCredits;
#endif
			/* credit based mechanism provides flow control based on
			 * target transmit resource availability, we assume that
			 * the HIF layer will always have bus resources greater
			 * than target transmit resources
			 */
			get_htc_send_packets_credit_based(target, pEndpoint,
							  &sendQueue);
#if DEBUG_CREDIT
			if (ep_debug_mask & (1 << pEndpoint->Id)) {
				if (cred - pEndpoint->TxCredits > 0) {
					AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						(" <HTC> Decrease EP%d %d - %d = %d credits.\n",
							 pEndpoint->Id, cred,
							 cred -
							 pEndpoint->TxCredits,
							 pEndpoint->TxCredits));
				}
			}
#endif
		} else {

		/*
		* Header and payload belongs to the different fragments and
		* consume 2 resource for one HTC package but USB combine into
		* one transfer.And one WMI message only consumes one single
		* resource.
		*/
			if (HTC_TX_BUNDLE_ENABLED(target) && tx_resources &&
				hif_get_bus_type(target->hif_dev) ==
						QDF_BUS_TYPE_USB) {
				if (pEndpoint->service_id ==
					WMI_CONTROL_SVC)
					tx_resources =
					    HTC_MAX_MSG_PER_BUNDLE_TX;
				else
					tx_resources =
					    (HTC_MAX_MSG_PER_BUNDLE_TX * 2);
			}
			/* get all the packets for this endpoint that we can for
			 * this pass
			 */
			get_htc_send_packets(target, pEndpoint, &sendQueue,
					     tx_resources);
		}

		if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) {
			/* didn't get any packets due to a lack of resources or
			 * TX queue was drained
			 */
			break;
		}

		if (!pEndpoint->async_update)
			UNLOCK_HTC_TX(target);

		/* send what we can */
		if (htc_issue_packets(target, pEndpoint, &sendQueue)) {
			int i;

			result = HTC_SEND_QUEUE_DROP;
			AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
				("htc_issue_packets, failed status:%d put it back to head of callersSendQueue",
				 result));

			for (i = HTC_PACKET_QUEUE_DEPTH(&sendQueue); i > 0; i--)
				hif_pm_runtime_put(target->hif_dev);
			if (!pEndpoint->async_update) {
				LOCK_HTC_TX(target);
			}
			HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,
							  &sendQueue);
			break;
		}

		if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
			tx_resources =
				hif_get_free_queue_number(target->hif_dev,
							  pEndpoint->UL_PipeID);
		}

		if (!pEndpoint->async_update) {
			LOCK_HTC_TX(target);
		}

	}

	/* done with this endpoint, we can clear the count */
	qdf_atomic_init(&pEndpoint->TxProcessCount);

	UNLOCK_HTC_TX(target);

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_try_send:\n"));

	return HTC_SEND_QUEUE_OK;
}

#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED
static uint16_t htc_send_pkts_sched_check(HTC_HANDLE HTCHandle,
					  HTC_ENDPOINT_ID id)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint;
	HTC_ENDPOINT_ID eid;
	HTC_PACKET_QUEUE *pTxQueue;
	uint16_t resources;
	uint16_t acQueueStatus[DATA_EP_SIZE] = { 0, 0, 0, 0 };

	if (id < ENDPOINT_2 || id > ENDPOINT_5)
		return 1;

	for (eid = ENDPOINT_2; eid <= ENDPOINT_5; eid++) {
		pEndpoint = &target->endpoint[eid];
		pTxQueue = &pEndpoint->TxQueue;

		if (HTC_QUEUE_EMPTY(pTxQueue))
			acQueueStatus[eid - 2] = 1;
	}

	switch (id) {
	case ENDPOINT_2:        /* BE */
		return acQueueStatus[0] && acQueueStatus[2]
			&& acQueueStatus[3];
	case ENDPOINT_3:        /* BK */
		return acQueueStatus[0] && acQueueStatus[1] && acQueueStatus[2]
			&& acQueueStatus[3];
	case ENDPOINT_4:        /* VI */
		return acQueueStatus[2] && acQueueStatus[3];
	case ENDPOINT_5:        /* VO */
		return acQueueStatus[3];
	default:
		return 0;
	}

}

static A_STATUS htc_send_pkts_sched_queue(HTC_TARGET *target,
					  HTC_PACKET_QUEUE *pPktQueue,
					  HTC_ENDPOINT_ID eid)
{
	HTC_ENDPOINT *pEndpoint;
	HTC_PACKET_QUEUE *pTxQueue;
	HTC_PACKET *pPacket;
	int goodPkts;

	pEndpoint = &target->endpoint[eid];
	pTxQueue = &pEndpoint->TxQueue;

	LOCK_HTC_TX(target);

	goodPkts =
		pEndpoint->MaxTxQueueDepth -
		HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);

	if (goodPkts > 0) {
		while (!HTC_QUEUE_EMPTY(pPktQueue)) {
			pPacket = htc_packet_dequeue(pPktQueue);
			HTC_PACKET_ENQUEUE(pTxQueue, pPacket);
			goodPkts--;

			if (goodPkts <= 0)
				break;
		}
	}

	if (HTC_PACKET_QUEUE_DEPTH(pPktQueue)) {
		ITERATE_OVER_LIST_ALLOW_REMOVE(&pPktQueue->QueueHead, pPacket,
					       HTC_PACKET, ListLink) {

			if (pEndpoint->EpCallBacks.
			    EpSendFull(pEndpoint->EpCallBacks.pContext,
				       pPacket) == HTC_SEND_FULL_DROP) {
				INC_HTC_EP_STAT(pEndpoint, TxDropped, 1);
			} else {
				HTC_PACKET_REMOVE(pPktQueue, pPacket);
				HTC_PACKET_ENQUEUE(pTxQueue, pPacket);
			}
		}
		ITERATE_END;
	}

	UNLOCK_HTC_TX(target);

	return A_OK;
}

#endif

static inline QDF_STATUS __htc_send_pkt(HTC_HANDLE HTCHandle,
				HTC_PACKET *pPacket)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint;
	HTC_PACKET_QUEUE pPktQueue;
	qdf_nbuf_t netbuf;
	HTC_FRAME_HDR *pHtcHdr;
	QDF_STATUS status;

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+__htc_send_pkt\n"));

	/* get packet at head to figure out which endpoint these packets will
	 * go into
	 */
	if (NULL == pPacket) {
		OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target, GET_HTC_PKT_Q_FAIL);
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-__htc_send_pkt\n"));
		return QDF_STATUS_E_INVAL;
	}

	if ((pPacket->Endpoint >= ENDPOINT_MAX) ||
	    (pPacket->Endpoint <= ENDPOINT_UNUSED)) {
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("%s endpoint is invalid\n", __func__));
		AR_DEBUG_ASSERT(0);
		return QDF_STATUS_E_INVAL;
	}
	pEndpoint = &target->endpoint[pPacket->Endpoint];

	if (!pEndpoint->service_id) {
		AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("%s service_id is invalid\n",
								__func__));
		return QDF_STATUS_E_INVAL;
	}

#ifdef HTC_EP_STAT_PROFILING
	LOCK_HTC_TX(target);
	INC_HTC_EP_STAT(pEndpoint, TxPosted, 1);
	UNLOCK_HTC_TX(target);
#endif

	/* provide room in each packet's netbuf for the HTC frame header */
	netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
	AR_DEBUG_ASSERT(netbuf);

	qdf_nbuf_push_head(netbuf, sizeof(HTC_FRAME_HDR));
	/* setup HTC frame header */
	pHtcHdr = (HTC_FRAME_HDR *) qdf_nbuf_get_frag_vaddr(netbuf, 0);
	AR_DEBUG_ASSERT(pHtcHdr);
	HTC_WRITE32(pHtcHdr,
		    SM(pPacket->ActualLength,
		       HTC_FRAME_HDR_PAYLOADLEN) |
		    SM(pPacket->Endpoint,
		       HTC_FRAME_HDR_ENDPOINTID));
	LOCK_HTC_TX(target);

	pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
	pEndpoint->SeqNo++;

	HTC_WRITE32(((uint32_t *) pHtcHdr) + 1,
		    SM(pPacket->PktInfo.AsTx.SeqNo,
		       HTC_FRAME_HDR_CONTROLBYTES1));

	UNLOCK_HTC_TX(target);

	/*
	 * For flow control enabled endpoints mapping is done in
	 * htc_issue_packets and for non flow control enabled endpoints
	 * its done here.
	 */
	if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
		pPacket->PktInfo.AsTx.Flags |= HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
		status = qdf_nbuf_map(target->osdev,
				      GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
				      QDF_DMA_TO_DEVICE);
		if (status != QDF_STATUS_SUCCESS) {
			AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
					("%s: nbuf map failed, endpoint %pK, seq_no. %d\n",
					 __func__, pEndpoint, pEndpoint->SeqNo));
			return status;
		}
	}

	INIT_HTC_PACKET_QUEUE_AND_ADD(&pPktQueue, pPacket);
#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED
	if (!htc_send_pkts_sched_check(HTCHandle, pEndpoint->Id))
		htc_send_pkts_sched_queue(HTCHandle, &pPktQueue, pEndpoint->Id);
	else
		htc_try_send(target, pEndpoint, &pPktQueue);
#else
	htc_try_send(target, pEndpoint, &pPktQueue);
#endif

	/* do completion on any packets that couldn't get in */
	while (!HTC_QUEUE_EMPTY(&pPktQueue)) {
		pPacket = htc_packet_dequeue(&pPktQueue);

		if (HTC_STOPPING(target))
			pPacket->Status = QDF_STATUS_E_CANCELED;
		else
			pPacket->Status = QDF_STATUS_E_RESOURCES;

		send_packet_completion(target, pPacket);
	}

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-__htc_send_pkt\n"));

	return QDF_STATUS_SUCCESS;
}

/* HTC API - htc_send_pkt */
QDF_STATUS htc_send_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
	if (HTCHandle == NULL || pPacket == NULL)
		return QDF_STATUS_E_FAILURE;

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+-htc_send_pkt: Enter endPointId: %d, buffer: %pK, length: %d\n",
			 pPacket->Endpoint, pPacket->pBuffer,
			 pPacket->ActualLength));
	return __htc_send_pkt(HTCHandle, pPacket);
}
qdf_export_symbol(htc_send_pkt);

#ifdef ATH_11AC_TXCOMPACT
/**
 * htc_send_data_pkt() - send single data packet on an endpoint
 * @HTCHandle: pointer to HTC handle
 * @netbuf: network buffer containing the data to be sent
 * @ActualLength: length of data that needs to be transmitted
 *
 * Return: QDF_STATUS_SUCCESS for success or an appropriate QDF_STATUS error
 */
QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, qdf_nbuf_t netbuf, int Epid,
			   int ActualLength)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint;
	HTC_FRAME_HDR *pHtcHdr;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	int tx_resources;
	uint32_t data_attr = 0;

	pEndpoint = &target->endpoint[Epid];

	tx_resources = hif_get_free_queue_number(target->hif_dev,
						 pEndpoint->UL_PipeID);

	if (tx_resources < HTC_DATA_RESOURCE_THRS) {
		if (pEndpoint->ul_is_polled) {
			hif_send_complete_check(pEndpoint->target->hif_dev,
						pEndpoint->UL_PipeID, 1);
			tx_resources =
				hif_get_free_queue_number(target->hif_dev,
							  pEndpoint->UL_PipeID);
		}
		if (tx_resources < HTC_DATA_MINDESC_PERPACKET)
			return QDF_STATUS_E_FAILURE;
	}

	if (hif_pm_runtime_get(target->hif_dev))
		return QDF_STATUS_E_FAILURE;

	pHtcHdr = (HTC_FRAME_HDR *) qdf_nbuf_get_frag_vaddr(netbuf, 0);
	AR_DEBUG_ASSERT(pHtcHdr);

	data_attr = qdf_nbuf_data_attr_get(netbuf);

	HTC_WRITE32(pHtcHdr, SM(ActualLength, HTC_FRAME_HDR_PAYLOADLEN) |
		    SM(Epid, HTC_FRAME_HDR_ENDPOINTID));
	/*
	 * If the HIF pipe for the data endpoint is polled rather than
	 * interrupt-driven, this is a good point to check whether any
	 * data previously sent through the HIF pipe have finished being
	 * sent.
	 * Since this may result in callbacks to htc_tx_completion_handler,
	 * which can take the HTC tx lock, make the hif_send_complete_check
	 * call before acquiring the HTC tx lock.
	 * Call hif_send_complete_check directly, rather than calling
	 * htc_send_complete_check, and call the PollTimerStart separately
	 * after calling hif_send_head, so the timer will be started to
	 * check for completion of the new outstanding download (in the
	 * unexpected event that other polling calls don't catch it).
	 */

	LOCK_HTC_TX(target);

	HTC_WRITE32(((uint32_t *) pHtcHdr) + 1,
		    SM(pEndpoint->SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));

	pEndpoint->SeqNo++;

	QDF_NBUF_UPDATE_TX_PKT_COUNT(netbuf, QDF_NBUF_TX_PKT_HTC);
	DPTRACE(qdf_dp_trace(netbuf, QDF_DP_TRACE_HTC_PACKET_PTR_RECORD,
		QDF_TRACE_DEFAULT_PDEV_ID, qdf_nbuf_data_addr(netbuf),
		sizeof(qdf_nbuf_data(netbuf)), QDF_TX));
	status = hif_send_head(target->hif_dev,
			       pEndpoint->UL_PipeID,
			       pEndpoint->Id, ActualLength, netbuf, data_attr);

	UNLOCK_HTC_TX(target);
	return status;
}
#else                           /*ATH_11AC_TXCOMPACT */

/**
 * htc_send_data_pkt() - htc_send_data_pkt
 * @HTCHandle: pointer to HTC handle
 * @pPacket: pointer to HTC_PACKET
 * @more_data: indicates whether more data is to follow
 *
 * Return: QDF_STATUS_SUCCESS for success or an appropriate QDF_STATUS error
 */
QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
			   uint8_t more_data)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint;
	HTC_FRAME_HDR *pHtcHdr;
	HTC_PACKET_QUEUE sendQueue;
	qdf_nbuf_t netbuf = NULL;
	int tx_resources;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	uint32_t data_attr = 0;

	if (pPacket) {
		if ((pPacket->Endpoint >= ENDPOINT_MAX) ||
		   (pPacket->Endpoint <= ENDPOINT_UNUSED)) {
			AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
				("%s endpoint is invalid\n", __func__));
			AR_DEBUG_ASSERT(0);
			return QDF_STATUS_E_INVAL;
		}
		pEndpoint = &target->endpoint[pPacket->Endpoint];

		/* add HTC_FRAME_HDR in the initial fragment */
		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
		pHtcHdr = (HTC_FRAME_HDR *) qdf_nbuf_get_frag_vaddr(netbuf, 0);
		AR_DEBUG_ASSERT(pHtcHdr);

		HTC_WRITE32(pHtcHdr,
				SM(pPacket->ActualLength,
				       HTC_FRAME_HDR_PAYLOADLEN) |
				SM(pPacket->PktInfo.AsTx.SendFlags,
					HTC_FRAME_HDR_FLAGS) |
				SM(pPacket->Endpoint,
					HTC_FRAME_HDR_ENDPOINTID));
		/*
		 * If the HIF pipe for the data endpoint is polled rather than
		 * interrupt-driven, this is a good point to check whether any
		 * data previously sent through the HIF pipe have finished being
		 * sent. Since this may result in callbacks to
		 * htc_tx_completion_handler, which can take the HTC tx lock,
		 * make the hif_send_complete_check call before acquiring the
		 * HTC tx lock.
		 * Call hif_send_complete_check directly, rather than calling
		 * htc_send_complete_check, and call the PollTimerStart
		 * separately after calling hif_send_head, so the timer will be
		 * started to check for completion of the new outstanding
		 * download (in the unexpected event that other polling calls
		 * don't catch it).
		 */
		if (pEndpoint->ul_is_polled) {
			htc_send_complete_poll_timer_stop(pEndpoint);
			hif_send_complete_check(pEndpoint->target->hif_dev,
						pEndpoint->UL_PipeID, 0);
		}

		LOCK_HTC_TX(target);

		pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
		pEndpoint->SeqNo++;

		HTC_WRITE32(((uint32_t *) pHtcHdr) + 1,
			    SM(pPacket->PktInfo.AsTx.SeqNo,
			       HTC_FRAME_HDR_CONTROLBYTES1));

		/* append new packet to pEndpoint->TxQueue */
		HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue, pPacket);
		if (HTC_TX_BUNDLE_ENABLED(target) && (more_data)) {
			UNLOCK_HTC_TX(target);
			return QDF_STATUS_SUCCESS;
		}

		QDF_NBUF_UPDATE_TX_PKT_COUNT(netbuf, QDF_NBUF_TX_PKT_HTC);
		DPTRACE(qdf_dp_trace(netbuf, QDF_DP_TRACE_HTC_PACKET_PTR_RECORD,
			  QDF_TRACE_DEFAULT_PDEV_ID, qdf_nbuf_data_addr(netbuf),
				sizeof(qdf_nbuf_data(netbuf)), QDF_TX));
	} else {
		LOCK_HTC_TX(target);
		pEndpoint = &target->endpoint[1];
	}

	/* increment tx processing count on entry */
	qdf_atomic_inc(&pEndpoint->TxProcessCount);
	if (qdf_atomic_read(&pEndpoint->TxProcessCount) > 1) {
		/*
		 * Another thread or task is draining the TX queues on this
		 * endpoint. That thread will reset the tx processing count when
		 * the queue is drained.
		 */
		qdf_atomic_dec(&pEndpoint->TxProcessCount);
		UNLOCK_HTC_TX(target);
		return QDF_STATUS_SUCCESS;
	}

	/***** beyond this point only 1 thread may enter ******/

	INIT_HTC_PACKET_QUEUE(&sendQueue);
	if (IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
#if DEBUG_CREDIT
		int cred = pEndpoint->TxCredits;
#endif
		get_htc_send_packets_credit_based(target, pEndpoint,
						 &sendQueue);
#if DEBUG_CREDIT
		if (ep_debug_mask & (1 << pEndpoint->Id)) {
			if (cred - pEndpoint->TxCredits > 0) {
				AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
						(" <HTC> Decrease EP%d %d - %d = %d credits.\n",
						 pEndpoint->Id, cred,
						 cred - pEndpoint->TxCredits,
						 pEndpoint->TxCredits));
			}
		}
#endif
		UNLOCK_HTC_TX(target);
	}

	else if (HTC_TX_BUNDLE_ENABLED(target)) {

		if ((hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_USB) &&
			hif_get_free_queue_number(target->hif_dev,
						pEndpoint->UL_PipeID)) {
			/*
			 * Header and payload belongs to the different fragments
			 * and consume 2 resource for one HTC package but USB
			 * combine into one transfer.
			 */
			get_htc_send_packets(target, pEndpoint, &sendQueue,
				(HTC_MAX_MSG_PER_BUNDLE_TX * 2));
		} else {
		/* Dequeue max packets from endpoint tx queue */
		get_htc_send_packets(target, pEndpoint, &sendQueue,
				     HTC_MAX_TX_BUNDLE_SEND_LIMIT);
		}

		UNLOCK_HTC_TX(target);
	} else {
		/*
		 * Now drain the endpoint TX queue for transmission as long as
		 * we have enough transmit resources
		 */
		tx_resources =
			hif_get_free_queue_number(target->hif_dev,
						  pEndpoint->UL_PipeID);
		get_htc_send_packets(target, pEndpoint, &sendQueue,
				     tx_resources);
		UNLOCK_HTC_TX(target);
	}

	/* send what we can */
	while (true) {
		if (HTC_TX_BUNDLE_ENABLED(target) &&
		    (HTC_PACKET_QUEUE_DEPTH(&sendQueue) >=
		     HTC_MIN_MSG_PER_BUNDLE) &&
		    (hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_SDIO ||
		     hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_USB)) {
			htc_issue_packets_bundle(target, pEndpoint, &sendQueue);
		}
		pPacket = htc_packet_dequeue(&sendQueue);
		if (pPacket == NULL)
			break;
		netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);

		LOCK_HTC_TX(target);
		/* store in look up queue to match completions */
		HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue, pPacket);
		INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
		pEndpoint->ul_outstanding_cnt++;
		UNLOCK_HTC_TX(target);

		status = hif_send_head(target->hif_dev,
				       pEndpoint->UL_PipeID,
				       pEndpoint->Id,
				       HTC_HDR_LENGTH + pPacket->ActualLength,
				       netbuf, data_attr);
#if DEBUG_BUNDLE
		qdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
			  pEndpoint->Id,
			  pEndpoint->TxCreditSize,
			  HTC_HDR_LENGTH + pPacket->ActualLength);
#endif

		htc_issue_tx_bundle_stats_inc(target);

		if (qdf_unlikely(QDF_IS_STATUS_ERROR(status))) {
			LOCK_HTC_TX(target);
			pEndpoint->ul_outstanding_cnt--;
			/* remove this packet from the tx completion queue */
			HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacket);

			/*
			 * Don't bother reclaiming credits - HTC flow control
			 * is not applicable to tx data.
			 * In LL systems, there is no download flow control,
			 * since there's virtually no download delay.
			 * In HL systems, the txrx SW explicitly performs the
			 * tx flow control.
			 */
			/* pEndpoint->TxCredits +=
			 * pPacket->PktInfo.AsTx.CreditsUsed;
			 */

			/* put this frame back at the front of the sendQueue */
			HTC_PACKET_ENQUEUE_TO_HEAD(&sendQueue, pPacket);

			/* put the sendQueue back at the front of
			 * pEndpoint->TxQueue
			 */
			HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,
							  &sendQueue);
			UNLOCK_HTC_TX(target);
			break;  /* still need to reset TxProcessCount */
		}
	}
	/* done with this endpoint, we can clear the count */
	qdf_atomic_init(&pEndpoint->TxProcessCount);

	if (pEndpoint->ul_is_polled) {
		/*
		 * Start a cleanup timer to poll for download completion.
		 * The download completion should be noticed promptly from
		 * other polling calls, but the timer provides a safety net
		 * in case other polling calls don't occur as expected.
		 */
		htc_send_complete_poll_timer_start(pEndpoint);
	}

	return status;
}
#endif /*ATH_11AC_TXCOMPACT */
qdf_export_symbol(htc_send_data_pkt);

/*
 * In the adapted HIF layer, qdf_nbuf_t are passed between HIF and HTC,
 * since upper layers expects HTC_PACKET containers we use the completed netbuf
 * and lookup its corresponding HTC packet buffer from a lookup list.
 * This is extra overhead that can be fixed by re-aligning HIF interfaces
 * with HTC.
 *
 */
static HTC_PACKET *htc_lookup_tx_packet(HTC_TARGET *target,
					HTC_ENDPOINT *pEndpoint,
					qdf_nbuf_t netbuf)
{
	HTC_PACKET *pPacket = NULL;
	HTC_PACKET *pFoundPacket = NULL;
	HTC_PACKET_QUEUE lookupQueue;

	INIT_HTC_PACKET_QUEUE(&lookupQueue);
	LOCK_HTC_EP_TX_LOOKUP(pEndpoint);

	LOCK_HTC_TX(target);

	/* mark that HIF has indicated the send complete for another packet */
	pEndpoint->ul_outstanding_cnt--;

	/* Dequeue first packet directly because of in-order completion */
	pPacket = htc_packet_dequeue(&pEndpoint->TxLookupQueue);
	if (qdf_unlikely(!pPacket)) {
		UNLOCK_HTC_TX(target);
		UNLOCK_HTC_EP_TX_LOOKUP(pEndpoint);
		return NULL;
	}
	if (netbuf == (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) {
		UNLOCK_HTC_TX(target);
		UNLOCK_HTC_EP_TX_LOOKUP(pEndpoint);
		return pPacket;
	}
	HTC_PACKET_ENQUEUE(&lookupQueue, pPacket);

	/*
	 * Move TX lookup queue to temp queue because most of packets that are
	 * not index 0 are not top 10 packets.
	 */
	HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&lookupQueue,
					  &pEndpoint->TxLookupQueue);
	UNLOCK_HTC_TX(target);

	ITERATE_OVER_LIST_ALLOW_REMOVE(&lookupQueue.QueueHead, pPacket,
				       HTC_PACKET, ListLink) {

		if (NULL == pPacket) {
			pFoundPacket = pPacket;
			break;
		}
		/* check for removal */
		if (netbuf ==
		    (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) {
			/* found it */
			HTC_PACKET_REMOVE(&lookupQueue, pPacket);
			pFoundPacket = pPacket;
			break;
		}

	}
	ITERATE_END;

	LOCK_HTC_TX(target);
	HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxLookupQueue,
					  &lookupQueue);
	UNLOCK_HTC_TX(target);
	UNLOCK_HTC_EP_TX_LOOKUP(pEndpoint);

	return pFoundPacket;
}

/**
 * htc_tx_completion_handler() - htc tx completion handler
 * @Context: pointer to HTC_TARGET structure
 * @netbuf: pointer to netbuf for which completion handler is being called
 * @EpID: end point Id on which the packet was sent
 * @toeplitz_hash_result: toeplitz hash result
 *
 * Return: QDF_STATUS_SUCCESS for success or an appropriate QDF_STATUS error
 */
QDF_STATUS htc_tx_completion_handler(void *Context,
				   qdf_nbuf_t netbuf, unsigned int EpID,
				   uint32_t toeplitz_hash_result)
{
	HTC_TARGET *target = (HTC_TARGET *) Context;
	HTC_ENDPOINT *pEndpoint;
	HTC_PACKET *pPacket;
#ifdef USB_HIF_SINGLE_PIPE_DATA_SCHED
	HTC_ENDPOINT_ID eid[DATA_EP_SIZE] = { ENDPOINT_5, ENDPOINT_4,
		ENDPOINT_2, ENDPOINT_3 };
	int epidIdx;
	uint16_t resourcesThresh[DATA_EP_SIZE]; /* urb resources */
	uint16_t resources;
	uint16_t resourcesMax;
#endif

	pEndpoint = &target->endpoint[EpID];
	target->TX_comp_cnt++;

	do {
		pPacket = htc_lookup_tx_packet(target, pEndpoint, netbuf);
		if (NULL == pPacket) {
			AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
					("HTC TX lookup failed!\n"));
			/* may have already been flushed and freed */
			netbuf = NULL;
			break;
		}
		if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM)
			hif_pm_runtime_put(target->hif_dev);

		if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_BUNDLED) {
			HTC_PACKET *pPacketTemp;
			HTC_PACKET_QUEUE *pQueueSave =
				(HTC_PACKET_QUEUE *) pPacket->pContext;
			HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pQueueSave,
							      pPacketTemp) {
				pPacket->Status = QDF_STATUS_SUCCESS;
				send_packet_completion(target, pPacketTemp);
			}
			HTC_PACKET_QUEUE_ITERATE_END;
			free_htc_bundle_packet(target, pPacket);

			if (hif_get_bus_type(target->hif_dev) ==
					     QDF_BUS_TYPE_USB) {
				if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint))
					htc_try_send(target, pEndpoint, NULL);
			}

			return QDF_STATUS_SUCCESS;
		}
		/* will be giving this buffer back to upper layers */
		netbuf = NULL;
		pPacket->Status = QDF_STATUS_SUCCESS;
		send_packet_completion(target, pPacket);

	} while (false);

	if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
		/* note: when using TX credit flow, the re-checking of queues
		 * happens when credits flow back from the target. In the non-TX
		 * credit case, we recheck after the packet completes
		 */
		if ((qdf_atomic_read(&pEndpoint->TxProcessCount) == 0) ||
				(!pEndpoint->async_update)) {
			htc_try_send(target, pEndpoint, NULL);
		}
	}

	return QDF_STATUS_SUCCESS;
}

#ifdef WLAN_FEATURE_FASTPATH
/**
 * htc_ctrl_msg_cmpl(): checks for tx completion for the endpoint specified
 * @HTC_HANDLE : pointer to the htc target context
 * @htc_ep_id  : end point id
 *
 * checks HTC tx completion
 *
 * Return: none
 */
void htc_ctrl_msg_cmpl(HTC_HANDLE htc_pdev, HTC_ENDPOINT_ID htc_ep_id)
{
	HTC_TARGET      *target = GET_HTC_TARGET_FROM_HANDLE(htc_pdev);
	HTC_ENDPOINT    *pendpoint = &target->endpoint[htc_ep_id];

	htc_send_complete_check(pendpoint, 1);
}
qdf_export_symbol(htc_ctrl_msg_cmpl);
#endif

/* callback when TX resources become available */
void htc_tx_resource_avail_handler(void *context, uint8_t pipeID)
{
	int i;
	HTC_TARGET *target = (HTC_TARGET *) context;
	HTC_ENDPOINT *pEndpoint = NULL;

	for (i = 0; i < ENDPOINT_MAX; i++) {
		pEndpoint = &target->endpoint[i];
		if (pEndpoint->service_id != 0) {
			if (pEndpoint->UL_PipeID == pipeID)
				break;
		}
	}

	if (i >= ENDPOINT_MAX) {
		AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
				("Invalid pipe indicated for TX resource avail : %d!\n",
				 pipeID));
		return;
	}

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("HIF indicated more resources for pipe:%d\n",
			 pipeID));

	htc_try_send(target, pEndpoint, NULL);
}

#ifdef FEATURE_RUNTIME_PM
/**
 * htc_kick_queues(): resumes tx transactions of suspended endpoints
 * @context: pointer to the htc target context
 *
 * Iterates throught the enpoints and provides a context to empty queues
 * int the hif layer when they are stalled due to runtime suspend.
 *
 * Return: none
 */
void htc_kick_queues(void *context)
{
	int i;
	HTC_TARGET *target = (HTC_TARGET *)context;
	HTC_ENDPOINT *endpoint = NULL;

	for (i = 0; i < ENDPOINT_MAX; i++) {
		endpoint = &target->endpoint[i];

		if (endpoint->service_id == 0)
			continue;

		if (endpoint->EpCallBacks.ep_resume_tx_queue)
			endpoint->EpCallBacks.ep_resume_tx_queue(
					endpoint->EpCallBacks.pContext);

		htc_try_send(target, endpoint, NULL);
	}

	hif_fastpath_resume(target->hif_dev);
}
#endif

/* flush endpoint TX queue */
void htc_flush_endpoint_tx(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint,
			   HTC_TX_TAG Tag)
{
	HTC_PACKET *pPacket;

	LOCK_HTC_TX(target);
	while (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) {
		pPacket = htc_packet_dequeue(&pEndpoint->TxQueue);

		if (pPacket) {
			/* let the sender know the packet was not delivered */
			pPacket->Status = QDF_STATUS_E_CANCELED;
			send_packet_completion(target, pPacket);
		}
	}
	UNLOCK_HTC_TX(target);
}

/* HTC API to flush an endpoint's TX queue*/
void htc_flush_endpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint,
			HTC_TX_TAG Tag)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
	HTC_ENDPOINT *pEndpoint = &target->endpoint[Endpoint];

	if (pEndpoint->service_id == 0) {
		AR_DEBUG_ASSERT(false);
		/* not in use.. */
		return;
	}

	htc_flush_endpoint_tx(target, pEndpoint, Tag);
}

/* HTC API to indicate activity to the credit distribution function */
void htc_indicate_activity_change(HTC_HANDLE HTCHandle,
				  HTC_ENDPOINT_ID Endpoint, bool Active)
{
	/*  TODO */
}

bool htc_is_endpoint_active(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint)
{
	return true;
}

void htc_set_nodrop_pkt(HTC_HANDLE HTCHandle, A_BOOL isNodropPkt)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);

	target->is_nodrop_pkt = isNodropPkt;
}

/**
 * htc_process_credit_rpt() - process credit report, call distribution function
 * @target: pointer to HTC_TARGET
 * @pRpt: pointer to HTC_CREDIT_REPORT
 * @NumEntries: number of entries in credit report
 * @FromEndpoint: endpoint for which  credit report is received
 *
 * Return: A_OK for success or an appropriate A_STATUS error
 */
void htc_process_credit_rpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt,
			    int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
{
	int i;
	HTC_ENDPOINT *pEndpoint;
	int totalCredits = 0;
	uint8_t rpt_credits, rpt_ep_id;

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("+htc_process_credit_rpt, Credit Report Entries:%d\n",
			 NumEntries));

	/* lock out TX while we update credits */
	LOCK_HTC_TX(target);

	for (i = 0; i < NumEntries; i++, pRpt++) {

		rpt_ep_id = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, ENDPOINTID);

		if (rpt_ep_id >= ENDPOINT_MAX) {
			AR_DEBUG_ASSERT(false);
			break;
		}

		rpt_credits = HTC_GET_FIELD(pRpt, HTC_CREDIT_REPORT, CREDITS);

		pEndpoint = &target->endpoint[rpt_ep_id];
#if DEBUG_CREDIT
		if (ep_debug_mask & (1 << pEndpoint->Id)) {
			AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
					(" <HTC> Increase EP%d %d + %d = %d credits\n",
					 rpt_ep_id, pEndpoint->TxCredits,
					 rpt_credits,
					 pEndpoint->TxCredits + rpt_credits));
		}
#endif

#ifdef HTC_EP_STAT_PROFILING

		INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1);
		INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, rpt_credits);

		if (FromEndpoint == rpt_ep_id) {
			/* this credit report arrived on the same endpoint
			 * indicating it arrived in an RX packet
			 */
			INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx,
					rpt_credits);
			INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1);
		} else if (FromEndpoint == ENDPOINT_0) {
			/* this credit arrived on endpoint 0 as a NULL msg */
			INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0,
					rpt_credits);
			INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1);
		} else {
			/* arrived on another endpoint */
			INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther,
					rpt_credits);
			INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1);
		}

#endif

		if (pEndpoint->service_id == WMI_CONTROL_SVC) {
			htc_credit_record(HTC_PROCESS_CREDIT_REPORT,
					  pEndpoint->TxCredits + rpt_credits,
					  HTC_PACKET_QUEUE_DEPTH(&pEndpoint->
							TxQueue));
		}

		pEndpoint->TxCredits += rpt_credits;

		if (pEndpoint->TxCredits
		    && HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) {
			UNLOCK_HTC_TX(target);
#ifdef ATH_11AC_TXCOMPACT
			htc_try_send(target, pEndpoint, NULL);
#else
			if (pEndpoint->service_id == HTT_DATA_MSG_SVC)
				htc_send_data_pkt(target, NULL, 0);
			else
				htc_try_send(target, pEndpoint, NULL);
#endif
			LOCK_HTC_TX(target);
		}
		totalCredits += rpt_credits;
	}

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
			("  Report indicated %d credits to distribute\n",
			 totalCredits));

	UNLOCK_HTC_TX(target);

	AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-htc_process_credit_rpt\n"));
}

/* function to fetch stats from htc layer*/
struct ol_ath_htc_stats *ieee80211_ioctl_get_htc_stats(HTC_HANDLE HTCHandle)
{
	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);

	return &(target->htc_pkt_stats);
}
