blob: ad47adc79ded1ea15190ff33b722e1d8e887db17 [file] [log] [blame]
/*
* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#include "htc_debug.h"
#include "htc_internal.h"
#include <adf_nbuf.h> /* adf_nbuf_t */
#include <adf_os_mem.h> /* adf_os_mem_alloc */
#include <vos_getBin.h>
#include "epping_main.h"
#include "adf_trace.h"
#define HTC_DATA_RESOURCE_THRS 256
#define HTC_DATA_MINDESC_PERPACKET 2
typedef enum _HTC_SEND_QUEUE_RESULT {
HTC_SEND_QUEUE_OK = 0, /* packet was queued */
HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */
} HTC_SEND_QUEUE_RESULT;
#ifndef DEBUG_CREDIT
#define DEBUG_CREDIT 0
#endif
#if DEBUG_CREDIT
/* bit mask to enable debug certain endpoint */
static unsigned ep_debug_mask = (1 << ENDPOINT_0) | (1 << ENDPOINT_1) | (1 << ENDPOINT_2);
#endif
/* HTC Control Path Credit History */
A_UINT32 g_htc_credit_history_idx = 0;
HTC_CREDIT_HISTORY htc_credit_history_buffer[HTC_CREDIT_HISTORY_MAX];
void htc_credit_record(htc_credit_exchange_type type, A_UINT32 tx_credit,
A_UINT32 htc_tx_queue_depth)
{
if (HTC_CREDIT_HISTORY_MAX <= g_htc_credit_history_idx)
g_htc_credit_history_idx = 0;
htc_credit_history_buffer[g_htc_credit_history_idx].type = type;
htc_credit_history_buffer[g_htc_credit_history_idx].time =
adf_get_boottime();
htc_credit_history_buffer[g_htc_credit_history_idx].tx_credit = tx_credit;
htc_credit_history_buffer[g_htc_credit_history_idx].htc_tx_queue_depth =
htc_tx_queue_depth;
g_htc_credit_history_idx++;
}
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));
}
void HTCGetControlEndpointTxHostCredits(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->ServiceID == WMI_CONTROL_SVC) {
*credits = pEndpoint->TxCredits;
break;
}
}
UNLOCK_HTC_TX(target);
}
static INLINE void RestoreTxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
if (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) {
adf_nbuf_t netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
adf_nbuf_unmap(target->osdev, netbuf, ADF_OS_DMA_TO_DEVICE);
adf_nbuf_pull_head(netbuf, sizeof(HTC_FRAME_HDR));
pPacket->PktInfo.AsTx.Flags &= ~HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
}
}
static void DoSendCompletion(HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueueToIndicate)
{
do {
if (HTC_QUEUE_EMPTY(pQueueToIndicate)) {
/* nothing to indicate */
break;
}
if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) {
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d, send complete multiple callback (%d pkts) \n",
pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate)));
/* a multiple send complete handler is being used, pass the queue to the handler */
pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint->EpCallBacks.pContext,
pQueueToIndicate);
/* all packets are now owned by the callback, reset queue to be safe */
INIT_HTC_PACKET_QUEUE(pQueueToIndicate);
} else {
HTC_PACKET *pPacket;
/* using legacy EpTxComplete */
do {
pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d send complete callback on packet %pK \n",
pEndpoint->Id, pPacket));
pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);
} while (!HTC_QUEUE_EMPTY(pQueueToIndicate));
}
} while (FALSE);
}
static void SendPacketCompletion(HTC_TARGET *target, HTC_PACKET *pPacket)
{
HTC_ENDPOINT *pEndpoint = &target->EndPoint[pPacket->Endpoint];
HTC_PACKET_QUEUE container;
RestoreTxPacket(target, pPacket);
INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
/* do completion */
DoSendCompletion(pEndpoint,&container);
}
void
HTCSendCompleteCheckCleanup(void *context)
{
HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *) context;
HTCSendCompleteCheck(pEndpoint, 1);
}
HTC_PACKET *AllocateHTCBundleTxPacket(HTC_TARGET *target)
{
HTC_PACKET *pPacket;
HTC_PACKET_QUEUE *pQueueSave;
adf_nbuf_t netbuf;
LOCK_HTC_TX(target);
if (NULL == target->pBundleFreeTxList) {
UNLOCK_HTC_TX(target);
/* for HTT packets, if AltDataCreditSize is non zero, we will have
allocated more space per packet (i.e., TargetCreditSize-AltDataCreditSize)
per bundle packet, but this should is required since we reuse the packet
for all services and all endpoints */
netbuf = adf_nbuf_alloc(NULL,
HTC_MAX_MSG_PER_BUNDLE_TX * target->TargetCreditSize,
0,
4,
FALSE);
AR_DEBUG_ASSERT(netbuf);
if (!netbuf)
{
return NULL;
}
pPacket = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET));
AR_DEBUG_ASSERT(pPacket);
if (!pPacket)
{
adf_nbuf_free(netbuf);
return NULL;
}
pQueueSave = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET_QUEUE));
AR_DEBUG_ASSERT(pQueueSave);
if (!pQueueSave)
{
adf_nbuf_free(netbuf);
adf_os_mem_free(pPacket);
return NULL;
}
INIT_HTC_PACKET_QUEUE(pQueueSave);
pPacket->pContext = pQueueSave;
SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
pPacket->pBuffer = adf_nbuf_data(netbuf);
pPacket->BufferLength = adf_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 = adf_nbuf_headroom(netbuf);
return pPacket;
}
//already done malloc - restore from free list
pPacket = target->pBundleFreeTxList;
AR_DEBUG_ASSERT(pPacket);
if (!pPacket)
{
UNLOCK_HTC_TX(target);
return NULL;
}
target->pBundleFreeTxList = (HTC_PACKET *)pPacket->ListLink.pNext;
UNLOCK_HTC_TX(target);
pPacket->ListLink.pNext = NULL;
return pPacket;
}
void FreeHTCBundleTxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
A_UINT32 curentHeadRoom;
adf_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 the 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 = adf_nbuf_headroom(netbuf);
adf_nbuf_pull_head(netbuf, pPacket->netbufOrigHeadRoom - curentHeadRoom);
adf_nbuf_trim_tail(netbuf, adf_nbuf_len(netbuf));
//restore the pBuffer pointer. HIF changes this
pPacket->pBuffer = adf_nbuf_data(netbuf);
pPacket->BufferLength = adf_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->pBundleFreeTxList == NULL) {
target->pBundleFreeTxList = pPacket;
pPacket->ListLink.pNext = NULL;
} else {
pPacket->ListLink.pNext = (DL_LIST *)target->pBundleFreeTxList;
target->pBundleFreeTxList = pPacket;
}
UNLOCK_HTC_TX(target);
}
HTC_PACKET *AllocateHTCBundleRxPacket(HTC_TARGET *target)
{
HTC_PACKET *pPacket;
HTC_PACKET_QUEUE *pQueueSave;
adf_nbuf_t netbuf;
LOCK_HTC_TX(target);
if (NULL == target->pBundleFreeRxList) {
UNLOCK_HTC_TX(target);
netbuf = adf_nbuf_alloc(NULL,
HTC_MAX_MSG_PER_BUNDLE_RX * target->TargetCreditSize,
0,
4,
FALSE);
AR_DEBUG_ASSERT(netbuf);
if (!netbuf)
{
return NULL;
}
pPacket = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET));
AR_DEBUG_ASSERT(pPacket);
if (!pPacket)
{
adf_nbuf_free(netbuf);
return NULL;
}
pQueueSave = adf_os_mem_alloc(NULL, sizeof(HTC_PACKET_QUEUE));
AR_DEBUG_ASSERT(pQueueSave);
if (!pQueueSave)
{
adf_nbuf_free(netbuf);
adf_os_mem_free(pPacket);
return NULL;
}
INIT_HTC_PACKET_QUEUE(pQueueSave);
pPacket->pContext = pQueueSave;
SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
pPacket->pBuffer = adf_nbuf_data(netbuf);
pPacket->BufferLength = adf_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 = adf_nbuf_headroom(netbuf);
return pPacket;
}
//already done malloc - restore from free list
pPacket = target->pBundleFreeRxList;
AR_DEBUG_ASSERT(pPacket);
if (!pPacket)
{
UNLOCK_HTC_TX(target);
return NULL;
}
target->pBundleFreeRxList = (HTC_PACKET *)pPacket->ListLink.pNext;
UNLOCK_HTC_TX(target);
pPacket->ListLink.pNext = NULL;
return pPacket;
}
void FreeHTCBundleRxPacket(HTC_TARGET *target, HTC_PACKET *pPacket)
{
A_UINT32 curentHeadRoom;
adf_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 the 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 = adf_nbuf_headroom(netbuf);
adf_nbuf_pull_head(netbuf, pPacket->netbufOrigHeadRoom - curentHeadRoom);
adf_nbuf_trim_tail(netbuf, adf_nbuf_len(netbuf));
//restore the pBuffer pointer. HIF changes this
pPacket->pBuffer = adf_nbuf_data(netbuf);
pPacket->BufferLength = adf_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->pBundleFreeRxList == NULL) {
target->pBundleFreeRxList = pPacket;
pPacket->ListLink.pNext = NULL;
} else {
pPacket->ListLink.pNext = (DL_LIST *)target->pBundleFreeRxList;
target->pBundleFreeRxList = pPacket;
}
UNLOCK_HTC_TX(target);
}
#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
static A_STATUS HTCSendBundledNetbuf(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
unsigned char *pBundleBuffer,
HTC_PACKET *pPacketTx)
{
adf_os_size_t data_len;
A_STATUS status;
adf_nbuf_t bundleBuf;
bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
data_len = pBundleBuffer - adf_nbuf_data(bundleBuf);
adf_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
adf_os_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
#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
if ((data_len / pEndpoint->TxCreditSize) <= HTC_MAX_MSG_PER_BUNDLE_TX) {
target->tx_bundle_stats[(data_len / pEndpoint->TxCreditSize) - 1]++;
}
#endif
status = HIFSend_head(target->hif_dev,
pEndpoint->UL_PipeID,
pEndpoint->Id,
data_len,
bundleBuf);
if (status != A_OK){
adf_os_print("%s:HIFSend_head failed(len=%zu).\n", __FUNCTION__,
data_len);
LOCK_HTC_TX(target);
HTC_PACKET_REMOVE(&pEndpoint->TxLookupQueue, pPacketTx);
UNLOCK_HTC_TX(target);
}
return status;
}
static A_STATUS HTCIssuePacketsBundle(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pPktQueue)
{
int i, frag_count, nbytes;
adf_nbuf_t netbuf, bundleBuf;
unsigned char *pBundleBuffer = NULL;
HTC_PACKET *pPacket = NULL, *pPacketTx = NULL;
#ifndef HIF_USB
HTC_FRAME_HDR *pHtcHdr;
#endif
int last_creditPad = 0;
int creditPad, creditRemainder,transferLength, bundlesSpaceRemaining = 0;
HTC_PACKET_QUEUE *pQueueSave = NULL;
A_STATUS ret;
bundlesSpaceRemaining = HTC_MAX_MSG_PER_BUNDLE_TX * pEndpoint->TxCreditSize;
pPacketTx = AllocateHTCBundleTxPacket(target);
if (!pPacketTx)
{
//good time to panic
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AllocateHTCBundleTxPacket failed \n"));
AR_DEBUG_ASSERT(FALSE);
ret = A_NO_MEMORY;
goto failed1;
}
bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
pBundleBuffer = adf_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 */
if (A_OK != HTCSendBundledNetbuf(target, pEndpoint,
pBundleBuffer - last_creditPad, pPacketTx)) {
ret = A_EBUSY;
HTC_PACKET_ENQUEUE(pQueueSave, pPacket);
goto failed2;
}
/* 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);
goto success;
}
bundlesSpaceRemaining = HTC_MAX_MSG_PER_BUNDLE_TX * pEndpoint->TxCreditSize;
pPacketTx = AllocateHTCBundleTxPacket(target);
if (!pPacketTx)
{
HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue, pPacket);
//good time to panic
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AllocateHTCBundleTxPacket failed \n"));
AR_DEBUG_ASSERT(FALSE);
ret = A_NO_MEMORY;
goto failed1;
}
bundleBuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacketTx);
pBundleBuffer = adf_nbuf_data(bundleBuf);
pQueueSave = (HTC_PACKET_QUEUE*)pPacketTx->pContext;
}
bundlesSpaceRemaining -= transferLength;
netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
#ifndef HIF_USB
pHtcHdr = (HTC_FRAME_HDR *) adf_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((A_UINT32 *)pHtcHdr + 1,
SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1) |
SM(creditPad, HTC_FRAME_HDR_RESERVED));
pHtcHdr->reserved = creditPad;
#endif
frag_count = adf_nbuf_get_num_frags(netbuf);
nbytes = pPacket->ActualLength + HTC_HDR_LENGTH;
for (i = 0; i < frag_count && nbytes > 0; i ++){
int frag_len = adf_nbuf_get_frag_len(netbuf, i);
unsigned char *frag_addr = adf_nbuf_get_frag_vaddr(netbuf, i);
if (frag_len > nbytes){
frag_len = nbytes;
}
A_MEMCPY(pBundleBuffer, frag_addr, frag_len);
nbytes -= frag_len;
pBundleBuffer += frag_len;
}
HTC_PACKET_ENQUEUE(pQueueSave, pPacket);
pBundleBuffer += creditPad;
#ifdef HIF_USB
/* last one can't be packed. */
last_creditPad = creditPad;
#endif
}
if (pBundleBuffer != adf_nbuf_data(bundleBuf)){
/* send out remaining buffer */
if (A_OK != HTCSendBundledNetbuf(target, pEndpoint,
pBundleBuffer - last_creditPad, pPacketTx))
{
ret = A_EBUSY;
goto failed2;
}
} else {
FreeHTCBundleTxPacket(target, pPacketTx);
}
success:
return A_OK;
failed2:
if (!HTC_QUEUE_EMPTY(pQueueSave))
HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(pPktQueue, pQueueSave);
FreeHTCBundleTxPacket(target, pPacketTx);
failed1:
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("send bundle buffer failed(%d) \n", ret));
return ret;
}
#endif /* ENABLE_BUNDLE_TX */
#endif
static A_STATUS HTCIssuePackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pPktQueue)
{
A_STATUS status = A_OK;
adf_nbuf_t netbuf;
HTC_PACKET *pPacket = NULL;
u_int16_t payloadLen;
HTC_FRAME_HDR *pHtcHdr;
bool is_tx_runtime_put = false;
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCIssuePackets: Queue: %pK, Pkts %d \n",
pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
while (TRUE) {
#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
if(
#ifndef HIF_USB
IS_TX_CREDIT_FLOW_ENABLED(pEndpoint) &&
#endif
HTC_ENABLE_BUNDLE(target) &&
HTC_PACKET_QUEUE_DEPTH(pPktQueue) >= HTC_MIN_MSG_PER_BUNDLE){
HTCIssuePacketsBundle(target, pEndpoint, pPktQueue);
}
#endif
#endif
/* 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 *) adf_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(((A_UINT32 *)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.
* Check the "FIXUP_NETBUF" flag to see whether this is a data netbuf
* that is already mapped, or a non-data netbuf that needs to be
* mapped.
*/
if (pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) {
adf_nbuf_map(
target->osdev,
GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
ADF_OS_DMA_TO_DEVICE);
}
}
LOCK_HTC_TX(target);
/* store in look up queue to match completions */
#ifdef ATH_11AC_TXCOMPACT
if (HTT_DATA_MSG_SVC != pEndpoint->ServiceID)
#endif /* ATH_11AC_TXCOMPACT */
{
HTC_PACKET_ENQUEUE(&pEndpoint->TxLookupQueue,pPacket);
}
INC_HTC_EP_STAT(pEndpoint,TxIssued,1);
pEndpoint->ul_outstanding_cnt++;
UNLOCK_HTC_TX(target);
if (pPacket->PktInfo.AsTx.Tag == HTC_TX_PACKET_TAG_RUNTIME_PUT)
is_tx_runtime_put = true;
status = HIFSend_head(target->hif_dev,
pEndpoint->UL_PipeID, pEndpoint->Id,
HTC_HDR_LENGTH + pPacket->ActualLength,
netbuf);
#if DEBUG_BUNDLE
adf_os_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
pEndpoint->Id,
pEndpoint->TxCreditSize,
HTC_HDR_LENGTH + pPacket->ActualLength);
#endif
#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
target->tx_bundle_stats[0]++;
#endif
target->CE_send_cnt++;
if (adf_os_unlikely(A_FAILED(status))) {
/* 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, ("HIFSend Failed status: %d\n", status));
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;
/* put it back into the callers queue */
HTC_PACKET_ENQUEUE_TO_HEAD(pPktQueue,pPacket);
UNLOCK_HTC_TX(target);
break;
}
/*
* For HTT messages the Tx complete is disabled and for some HTT
* messages which doesn't have response from FW, runtime pm put
* is not happening. To address that the HTT packet which is tagged
* with HTC_TX_PACKET_TAG_RUNTIME_PUT releases the count after the
* packet sent is successful
*/
if (is_tx_runtime_put) {
is_tx_runtime_put = false;
hif_pm_runtime_put(target->hif_dev);
}
}
if (adf_os_unlikely(A_FAILED(status)))
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("htc_issue_packets, failed pkt:0x%pK status:%d",
pPacket, status));
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCIssuePackets \n"));
return status;
}
#ifdef FEATURE_RUNTIME_PM
static void GetHTCAutoPmSendPackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue)
{
HTC_PACKET *pPacket;
/*
* If the EP is not WMI EP then it is not required to go through
* the queue to find the tagged packkets.
*/
if (pEndpoint->ServiceID != WMI_CONTROL_SVC)
return;
ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket,
HTC_PACKET, ListLink) {
if (pPacket->PktInfo.AsTx.Tag != HTC_TX_PACKET_TAG_AUTO_PM)
continue;
HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket);
HTC_PACKET_ENQUEUE(pQueue, pPacket);
} ITERATE_END
}
static void RequeueHTCAutoPmSendPackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue)
{
if (pEndpoint->ServiceID != WMI_CONTROL_SVC)
return;
/*
* Go through remaining packets in the temporary queue and add them to
* head of the queue so that those would be sent at priority than other
* packets
*/
HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue, pQueue);
}
#else
static inline void GetHTCAutoPmSendPackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue)
{
return;
}
static inline void RequeueHTCAutoPmSendPackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue)
{
return;
}
#endif
/* get HTC send packets from the TX queue on an endpoint, based on available credits */
void GetHTCSendPacketsCreditBased(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue)
{
int creditsRequired;
int remainder;
A_UINT8 sendFlags;
HTC_PACKET *pPacket;
unsigned int transferLength;
HTC_PACKET_QUEUE *pTxQueue;
HTC_PACKET_QUEUE tempQueue;
A_BOOL AutoPMQueue = FALSE;
/****** NOTE : the TX lock is held when this function is called *****************/
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPacketsCreditBased \n"));
INIT_HTC_PACKET_QUEUE(&tempQueue);
GetHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);
if (!HTC_QUEUE_EMPTY(&tempQueue)) {
pTxQueue = &tempQueue;
AutoPMQueue = TRUE;
} else
pTxQueue = &pEndpoint->TxQueue;
/* loop until we can grab as many packets out of the queue as we can */
while (TRUE) {
sendFlags = 0;
if (!AutoPMQueue && hif_pm_runtime_get(target->hif_dev)) {
A_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
break;
}
/* get packet at head, but don't remove it */
pPacket = HTC_GET_PKT_AT_HEAD(pTxQueue);
if (pPacket == NULL) {
if (!AutoPMQueue)
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(pTxQueue)));
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 (!AutoPMQueue)
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->ServiceID == WMI_CONTROL_SVC) {
LOCK_HTC_CREDIT(target);
htc_credit_record(HTC_REQUEST_CREDIT, pEndpoint->TxCredits,
HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue));
UNLOCK_HTC_CREDIT(target);
}
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(pTxQueue);
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 (AutoPMQueue)
RequeueHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPacketsCreditBased \n"));
}
void GetHTCSendPackets(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pQueue,
int Resources)
{
HTC_PACKET *pPacket;
HTC_PACKET_QUEUE *pTxQueue;
HTC_PACKET_QUEUE tempQueue;
A_BOOL AutoPMQueue = FALSE;
/****** NOTE : the TX lock is held when this function is called *****************/
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets %d resources\n",Resources));
INIT_HTC_PACKET_QUEUE(&tempQueue);
GetHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);
if (!HTC_QUEUE_EMPTY(&tempQueue)) {
pTxQueue = &tempQueue;
AutoPMQueue = TRUE;
} else
pTxQueue = &pEndpoint->TxQueue;
/* loop until we can grab as many packets out of the queue as we can */
while (Resources > 0) {
int num_frags;
if (!AutoPMQueue && hif_pm_runtime_get(target->hif_dev)) {
A_ASSERT(HTC_PACKET_QUEUE_DEPTH(pQueue) == 0);
break;
}
pPacket = HTC_PACKET_DEQUEUE(pTxQueue);
if (pPacket == NULL) {
if (!AutoPMQueue)
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(pTxQueue)));
/* For non-credit path the sequence number is already embedded
* in the constructed HTC header
*/
#if 0
pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
pEndpoint->SeqNo++;
#endif
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 adf_nbuf_get_num_frags before calling
* adf_nbuf_map, because the MacOS version of adf_nbuf_t doesn't
* support adf_nbuf_get_num_frags until after adf_nbuf_map has
* been done.
* Assume that the non-data netbufs, i.e. the WMI message netbufs,
* consist of a single fragment.
*/
num_frags =
(pPacket->PktInfo.AsTx.Flags & HTC_TX_PACKET_FLAG_FIXUP_NETBUF) ?
1 /* WMI messages are in a single-fragment network buffer */ :
adf_nbuf_get_num_frags(GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket));
Resources -= num_frags;
}
if (AutoPMQueue)
RequeueHTCAutoPmSendPackets(target, pEndpoint, &tempQueue);
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n"));
}
static HTC_SEND_QUEUE_RESULT HTCTrySend(HTC_TARGET *target,
HTC_ENDPOINT *pEndpoint,
HTC_PACKET_QUEUE *pCallersSendQueue)
{
HTC_PACKET_QUEUE sendQueue; /* temp queue to hold packets at various stages */
HTC_PACKET *pPacket;
int tx_resources;
int overflow;
HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (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 {
if (NULL == pCallersSendQueue) {
/* caller didn't provide a queue, just wants us to check queues and send */
break;
}
if (HTC_QUEUE_EMPTY(pCallersSendQueue)) {
/* empty queue */
OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,HTC_PKT_Q_EMPTY);
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Empty queue\n"));
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);
/* figure out 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 the 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 a 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.
*/
RestoreTxPacket(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 */
adf_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,("-HTCTrySend: \n"));
return result;
}
if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
tx_resources = HIFGetFreeQueueNumber(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 */
adf_os_atomic_inc(&pEndpoint->TxProcessCount);
if (adf_os_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 */
adf_os_atomic_dec(&pEndpoint->TxProcessCount);
UNLOCK_HTC_TX(target);
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (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 (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 */
GetHTCSendPacketsCreditBased(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 {
#ifdef HIF_USB
#ifdef ENABLE_BUNDLE_TX
/*
* Header and payload belongs to the different fragments and
* consume 2 resource for one HTC package but USB conbime into
* one transfer. And one WMI message only consumes one single
* resource.
*/
if (HTC_ENABLE_BUNDLE(target) && tx_resources) {
if (pEndpoint->ServiceID == WMI_CONTROL_SVC)
tx_resources = HTC_MAX_MSG_PER_BUNDLE_TX;
else
tx_resources = (HTC_MAX_MSG_PER_BUNDLE_TX * 2);
}
#endif
#endif
/* get all the packets for this endpoint that we can for this pass */
GetHTCSendPackets(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;
}
UNLOCK_HTC_TX(target);
/* send what we can */
result = HTCIssuePackets(target,pEndpoint,&sendQueue);
if (result) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("htc_issue_packets, failed status:%d put it back to head of callers SendQueue",
result));
LOCK_HTC_TX(target);
HTC_PACKET_QUEUE_TRANSFER_TO_HEAD(&pEndpoint->TxQueue,
&sendQueue);
break;
}
if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
tx_resources = HIFGetFreeQueueNumber(target->hif_dev,pEndpoint->UL_PipeID);
}
LOCK_HTC_TX(target);
}
/* done with this endpoint, we can clear the count */
adf_os_atomic_init(&pEndpoint->TxProcessCount);
UNLOCK_HTC_TX(target);
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
return HTC_SEND_QUEUE_OK;
}
A_STATUS HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_ENDPOINT *pEndpoint;
HTC_PACKET *pPacket;
adf_nbuf_t netbuf;
HTC_FRAME_HDR *pHtcHdr;
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: %pK, Pkts %d \n",
pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
/* get packet at head to figure out which endpoint these packets will go into */
pPacket = HTC_GET_PKT_AT_HEAD(pPktQueue);
if (NULL == pPacket) {
OL_ATH_HTC_PKT_ERROR_COUNT_INCR(target,GET_HTC_PKT_Q_FAIL);
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("-HTCSendPktsMultiple \n"));
return A_EINVAL;
}
AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
pEndpoint = &target->EndPoint[pPacket->Endpoint];
if (!pEndpoint->ServiceID) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: ServiceID is invalid\n",
__func__));
return A_EINVAL;
}
#ifdef HTC_EP_STAT_PROFILING
LOCK_HTC_TX(target);
INC_HTC_EP_STAT(pEndpoint,TxPosted,HTC_PACKET_QUEUE_DEPTH(pPktQueue));
UNLOCK_HTC_TX(target);
#endif
/* provide room in each packet's netbuf for the HTC frame header */
HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
AR_DEBUG_ASSERT(netbuf);
adf_nbuf_push_head(netbuf, sizeof(HTC_FRAME_HDR));
/* setup HTC frame header */
pHtcHdr = (HTC_FRAME_HDR *) adf_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(((A_UINT32 *)pHtcHdr) + 1,
SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));
UNLOCK_HTC_TX(target);
/*
* 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.
*/
adf_nbuf_map(
target->osdev,
GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket),
ADF_OS_DMA_TO_DEVICE);
pPacket->PktInfo.AsTx.Flags |= HTC_TX_PACKET_FLAG_FIXUP_NETBUF;
} HTC_PACKET_QUEUE_ITERATE_END;
HTCTrySend(target,pEndpoint,pPktQueue);
/* do completion on any packets that couldn't get in */
if (!HTC_QUEUE_EMPTY(pPktQueue)) {
HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
/* remove the headroom reserved for HTC_FRAME_HDR */
RestoreTxPacket(target, pPacket);
if (HTC_STOPPING(target)) {
pPacket->Status = A_ECANCELED;
} else {
pPacket->Status = A_NO_RESOURCE;
}
} HTC_PACKET_QUEUE_ITERATE_END;
DoSendCompletion(pEndpoint,pPktQueue);
}
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
return A_OK;
}
/* HTC API - HTCSendPkt */
A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
{
HTC_PACKET_QUEUE queue;
if (HTCHandle == NULL || pPacket == NULL) {
return A_ERROR;
}
a_mem_trace(GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket));
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
("+-HTCSendPkt: Enter endPointId: %d, buffer: %pK, length: %d \n",
pPacket->Endpoint, pPacket->pBuffer, pPacket->ActualLength));
INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
return HTCSendPktsMultiple(HTCHandle, &queue);
}
#ifdef ATH_11AC_TXCOMPACT
A_STATUS HTCSendDataPkt(HTC_HANDLE HTCHandle, adf_nbuf_t netbuf, int Epid, int ActualLength)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_ENDPOINT *pEndpoint;
HTC_FRAME_HDR *pHtcHdr;
A_STATUS status = A_OK;
int tx_resources;
pEndpoint = &target->EndPoint[Epid];
tx_resources = HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID);
if(tx_resources < HTC_DATA_RESOURCE_THRS){
if (pEndpoint->ul_is_polled){
HIFSendCompleteCheck(
pEndpoint->target->hif_dev, pEndpoint->UL_PipeID, 1);
tx_resources = HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID);
}
if(tx_resources < HTC_DATA_MINDESC_PERPACKET){
return A_ERROR;
}
}
if (hif_pm_runtime_get(target->hif_dev))
return A_ERROR;
pHtcHdr = (HTC_FRAME_HDR *) adf_nbuf_get_frag_vaddr(netbuf, 0);
AR_DEBUG_ASSERT(pHtcHdr);
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 HTCTxCompletionHandler,
* which can take the HTC tx lock, make the HIFSendCompleteCheck
* call before acquiring the HTC tx lock.
* Call HIFSendCompleteCheck directly, rather than calling
* HTCSendCompleteCheck, and call the PollTimerStart separately
* after calling HIFSend_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(((A_UINT32 *)pHtcHdr) + 1, SM(pEndpoint->SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));
pEndpoint->SeqNo++;
NBUF_UPDATE_TX_PKT_COUNT(netbuf, NBUF_TX_PKT_HTC);
DPTRACE(adf_dp_trace(netbuf, ADF_DP_TRACE_HTC_PACKET_PTR_RECORD,
adf_nbuf_data_addr(netbuf),
sizeof(adf_nbuf_data(netbuf)), ADF_TX));
status = HIFSend_head(target->hif_dev,
pEndpoint->UL_PipeID,
pEndpoint->Id,
ActualLength,
netbuf);
UNLOCK_HTC_TX(target);
return status ;
}
#else /*ATH_11AC_TXCOMPACT*/
A_STATUS HTCSendDataPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket,
A_UINT8 more_data)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_ENDPOINT *pEndpoint;
HTC_FRAME_HDR *pHtcHdr;
HTC_PACKET_QUEUE sendQueue;
adf_nbuf_t netbuf;
int tx_resources;
A_STATUS status = A_OK;
if (pPacket){
AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
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 *) adf_nbuf_get_frag_vaddr(netbuf, 0);
AR_DEBUG_ASSERT(pHtcHdr);
if (pPacket->ActualLength > (pEndpoint->TxCreditSize - HTC_HDR_LENGTH)) {
pPacket->ActualLength = pEndpoint->TxCreditSize - HTC_HDR_LENGTH;
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Packet Length %d exceeds endpoint credit size %d, set the packet length to credit size\n",
pPacket->ActualLength, pEndpoint->TxCreditSize));
}
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 HTCTxCompletionHandler,
* which can take the HTC tx lock, make the HIFSendCompleteCheck
* call before acquiring the HTC tx lock.
* Call HIFSendCompleteCheck directly, rather than calling
* HTCSendCompleteCheck, and call the PollTimerStart separately
* after calling HIFSend_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) {
HTCSendCompletePollTimerStop(pEndpoint);
HIFSendCompleteCheck(
pEndpoint->target->hif_dev, pEndpoint->UL_PipeID, 0);
}
LOCK_HTC_TX(target);
pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
pEndpoint->SeqNo++;
HTC_WRITE32(((A_UINT32 *)pHtcHdr) + 1, SM(pPacket->PktInfo.AsTx.SeqNo, HTC_FRAME_HDR_CONTROLBYTES1));
/* append new packet to pEndpoint->TxQueue */
HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue, pPacket);
#ifdef ENABLE_BUNDLE_TX
if (HTC_ENABLE_BUNDLE(target) && (more_data)) {
UNLOCK_HTC_TX(target);
return A_OK;
}
#endif
} else {
LOCK_HTC_TX(target);
pEndpoint = &target->EndPoint[1];
}
/* increment tx processing count on entry */
adf_os_atomic_inc(&pEndpoint->TxProcessCount);
if (adf_os_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.
*/
adf_os_atomic_dec(&pEndpoint->TxProcessCount);
UNLOCK_HTC_TX(target);
return A_OK;
}
/***** 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
GetHTCSendPacketsCreditBased(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);
}
#ifdef ENABLE_BUNDLE_TX
else if (HTC_ENABLE_BUNDLE(target)) {
#ifdef HIF_USB
if (HIFGetFreeQueueNumber(target->hif_dev, pEndpoint->UL_PipeID)) {
/*
* Header and payload belongs to the different fragments and
* consume 2 resource for one HTC package but USB conbime into
* one transfer.
*/
GetHTCSendPackets(target, pEndpoint, &sendQueue,
(HTC_MAX_MSG_PER_BUNDLE_TX * 2));
}
#else
/* Dequeue max packets from endpoint tx queue */
GetHTCSendPackets(target, pEndpoint, &sendQueue,
HTC_MAX_TX_BUNDLE_SEND_LIMIT);
#endif
UNLOCK_HTC_TX(target);
}
#endif
else {
/*
* Now drain the endpoint TX queue for transmission as long as we have
* enough transmit resources
*/
tx_resources = HIFGetFreeQueueNumber(target->hif_dev,pEndpoint->UL_PipeID);
GetHTCSendPackets(target, pEndpoint, &sendQueue, tx_resources);
UNLOCK_HTC_TX(target);
}
/* send what we can */
while (TRUE) {
#if defined(HIF_USB) || defined(HIF_SDIO)
#ifdef ENABLE_BUNDLE_TX
if (HTC_ENABLE_BUNDLE(target) &&
HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_MSG_PER_BUNDLE) {
HTCIssuePacketsBundle(target, pEndpoint, &sendQueue);
}
#endif
#endif
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 = HIFSend_head(target->hif_dev,
pEndpoint->UL_PipeID,
pEndpoint->Id,
HTC_HDR_LENGTH + pPacket->ActualLength,
netbuf);
#if DEBUG_BUNDLE
adf_os_print(" Send single EP%d buffer size:0x%x, total:0x%x.\n",
pEndpoint->Id,
pEndpoint->TxCreditSize,
HTC_HDR_LENGTH + pPacket->ActualLength);
#endif
#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
target->tx_bundle_stats[0]++;
#endif
if (adf_os_unlikely(A_FAILED(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 */
adf_os_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.
*/
HTCSendCompletePollTimerStart(pEndpoint);
}
return status;
}
#endif /*ATH_11AC_TXCOMPACT*/
/*
* In the adapted HIF layer, adf_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 *HTCLookupTxPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, adf_nbuf_t netbuf)
{
HTC_PACKET *pPacket = NULL;
HTC_PACKET *pFoundPacket = NULL;
HTC_PACKET_QUEUE lookupQueue;
INIT_HTC_PACKET_QUEUE(&lookupQueue);
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 (adf_os_unlikely(!pPacket)) {
UNLOCK_HTC_TX(target);
return NULL;
}
if (netbuf == (adf_nbuf_t)GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket)) {
UNLOCK_HTC_TX(target);
return pPacket;
} else {
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 == (adf_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);
return pFoundPacket;
}
A_STATUS HTCTxCompletionHandler(void *Context,
adf_nbuf_t netbuf,
unsigned int EpID)
{
HTC_TARGET *target = (HTC_TARGET *)Context;
HTC_ENDPOINT *pEndpoint;
HTC_PACKET *pPacket;
pEndpoint = &target->EndPoint[EpID];
target->TX_comp_cnt++;
do {
pPacket = HTCLookupTxPacket(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;
}
a_mem_trace(netbuf);
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 = A_OK;
SendPacketCompletion(target,pPacketTemp);
}HTC_PACKET_QUEUE_ITERATE_END;
FreeHTCBundleTxPacket(target, pPacket);
#ifdef HIF_USB
if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) {
HTCTrySend(target,pEndpoint,NULL);
}
#endif
return A_OK;
}
/* will be giving this buffer back to upper layers */
netbuf = NULL;
pPacket->Status = A_OK;
SendPacketCompletion(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 */
HTCTrySend(target,pEndpoint,NULL);
}
return A_OK;
}
/* callback when TX resources become available */
void HTCTxResourceAvailHandler(void *context, A_UINT8 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->ServiceID != 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));
HTCTrySend(target,pEndpoint,NULL);
}
void HTCTxResumeAllHandler(void *context)
{
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->ServiceID == 0)
continue;
if (pEndpoint->EpCallBacks.EpResumeTxQueue)
pEndpoint->EpCallBacks.EpResumeTxQueue
(pEndpoint->EpCallBacks.pContext);
HTCTrySend(target, pEndpoint, NULL);
}
}
/* flush endpoint TX queue */
void HTCFlushEndpointTX(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 = A_ECANCELED;
SendPacketCompletion(target, pPacket);
}
}
UNLOCK_HTC_TX(target);
}
/* HTC API to flush an endpoint's TX queue*/
void HTCFlushEndpoint(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->ServiceID == 0) {
AR_DEBUG_ASSERT(FALSE);
/* not in use.. */
return;
}
HTCFlushEndpointTX(target,pEndpoint,Tag);
}
/* HTC API to indicate activity to the credit distribution function */
void HTCIndicateActivityChange(HTC_HANDLE HTCHandle,
HTC_ENDPOINT_ID Endpoint,
A_BOOL Active)
{
/* TODO */
}
A_BOOL HTCIsEndpointActive(HTC_HANDLE HTCHandle,
HTC_ENDPOINT_ID Endpoint)
{
return TRUE;
}
void HTCSetNodropPkt(HTC_HANDLE HTCHandle, A_BOOL isNodropPkt)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
target->is_nodrop_pkt = isNodropPkt;
}
/* process credit reports and call distribution function */
void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
{
int i;
HTC_ENDPOINT *pEndpoint;
int totalCredits = 0;
A_UINT8 rpt_credits, rpt_ep_id;
AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, 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 message */
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
pEndpoint->TxCredits += rpt_credits;
if (pEndpoint->ServiceID == WMI_CONTROL_SVC) {
LOCK_HTC_CREDIT(target);
htc_credit_record(HTC_PROCESS_CREDIT_REPORT, pEndpoint->TxCredits,
HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue));
UNLOCK_HTC_CREDIT(target);
}
if (pEndpoint->TxCredits && HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)) {
UNLOCK_HTC_TX(target);
#ifdef ATH_11AC_TXCOMPACT
HTCTrySend(target,pEndpoint,NULL);
#else
if (pEndpoint->ServiceID == HTT_DATA_MSG_SVC){
HTCSendDataPkt(target, NULL, 0);
} else {
HTCTrySend(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, ("-HTCProcessCreditRpt \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));
}