diff --git a/htc/htc_api.h b/htc/htc_api.h index ceff09588438..9c06ac4d334c 100644 --- a/htc/htc_api.h +++ b/htc/htc_api.h @@ -69,6 +69,8 @@ struct ol_ath_htc_stats { /* To resume HTT Tx queue during runtime resume */ typedef void (*HTC_EP_RESUME_TX_QUEUE)(void *); +typedef int (*HTC_EP_PADDING_CREDIT_UPDATE) (void *, int); + /* per service connection send completion */ typedef void (*HTC_EP_SEND_PKT_COMPLETE)(void *, HTC_PACKET *); /* per service connection callback when a plurality of packets have been sent @@ -175,6 +177,8 @@ struct htc_ep_callbacks { HTC_EP_SEND_PKT_COMP_MULTIPLE EpTxCompleteMultiple; HTC_EP_RESUME_TX_QUEUE ep_resume_tx_queue; + + HTC_EP_PADDING_CREDIT_UPDATE ep_padding_credit_update; /* if EpRecvAllocThresh is non-NULL, HTC will compare the * threshold value to the current recv packet length and invoke * the EpRecvAllocThresh callback to acquire a packet buffer diff --git a/htc/htc_send.c b/htc/htc_send.c index 7e3a30b0a7fd..00320dd41387 100644 --- a/htc/htc_send.c +++ b/htc/htc_send.c @@ -398,6 +398,103 @@ static QDF_STATUS htc_send_bundled_netbuf(HTC_TARGET *target, return status; } +#ifdef QCA_TX_PADDING_CREDIT_SUPPORT +#define SDIO_BLOCK_SIZE 512 +static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep) +{ + int ret = 0; + + if (!ep || !ep->EpCallBacks.pContext || + !ep->EpCallBacks.ep_padding_credit_update) + return 1; + + ret = ep->EpCallBacks.ep_padding_credit_update(ep->EpCallBacks.pContext, + 0); + + if (ret < 2) + AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s ret %d\n", __func__, ret)); + + return ret; +} + +static bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep, + HTC_PACKET *p_last_htc_pkt, + unsigned char *p_last_pkt_bundle_buffer, + unsigned char **p_bundle_buffer, + int tot_data_len) +{ + bool extra_tx_credit = FALSE; + HTC_FRAME_HDR *p_htc_hdr; + int first_buf_bundled_len = 0, last_buf_len = 0; + int sdio_pad = 0, free_space = 0; + int (*update_ep_padding_credit)(void *, int); + + update_ep_padding_credit = ep->EpCallBacks.ep_padding_credit_update; + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("%s Tot data_len = %d\n", __func__, tot_data_len)); + + if (!p_last_htc_pkt) + return extra_tx_credit; + + last_buf_len = (p_last_htc_pkt->ActualLength + HTC_HDR_LENGTH); + if (tot_data_len != last_buf_len) { + first_buf_bundled_len = tot_data_len - ep->TxCreditSize; + free_space = tot_data_len - + (first_buf_bundled_len + last_buf_len); + } else { + free_space = ep->TxCreditSize - tot_data_len; + } + + sdio_pad = SDIO_BLOCK_SIZE - ((first_buf_bundled_len + last_buf_len) % + SDIO_BLOCK_SIZE); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("%s first_buf_bundled_len = %d last_buf_len = %d\n", + __func__, first_buf_bundled_len, last_buf_len)); + + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("%s sdio_pad = %d free_space = %d\n", __func__, + sdio_pad, free_space)); + + if (sdio_pad <= free_space) { + if (p_bundle_buffer && *p_bundle_buffer) { + /* Align Tx bundled buf to avoid a extra Padding buf */ + *p_bundle_buffer -= (free_space - sdio_pad); + } + } else { + /* Extra Padding Buffer needed, consume extra tx credit */ + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, + ("%s Used a Tx credit for Padding Buffer\n", + __func__)); + p_htc_hdr = (HTC_FRAME_HDR *)(p_last_pkt_bundle_buffer); + p_htc_hdr->Flags |= HTC_FLAGS_PADDING_CHECK; + extra_tx_credit = TRUE; + if (ep->EpCallBacks.ep_padding_credit_update) { + /* Decrement 1 credit at host, + * due to extra tx credit consumed by padding buffer + */ + update_ep_padding_credit(ep->EpCallBacks.pContext, -1); + } + } + return extra_tx_credit; +} +#else +static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep) +{ + return 1; +} + +static bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep, + HTC_PACKET *p_last_htc_pkt, + unsigned char *p_last_pkt_bundle_buffer, + unsigned char **p_bundle_buffer, + int tot_data_len) +{ + return FALSE; +} +#endif + /** * htc_issue_packets_bundle() - HTC function to send bundle packets from a queue * @target: HTC target on which packets need to be sent @@ -419,6 +516,8 @@ static void htc_issue_packets_bundle(HTC_TARGET *target, int creditPad, creditRemainder, transferLength, bundlesSpaceRemaining = 0; HTC_PACKET_QUEUE *pQueueSave = NULL; + HTC_PACKET *p_last_htc_pkt = NULL; + unsigned char *p_last_pkt_bundle_buffer = NULL; bundlesSpaceRemaining = target->MaxMsgsPerHTCBundle * pEndpoint->TxCreditSize; @@ -434,6 +533,10 @@ static void htc_issue_packets_bundle(HTC_TARGET *target, pBundleBuffer = qdf_nbuf_data(bundleBuf); pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext; while (1) { + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (htc_tx_pad_credit_avail(pEndpoint) < 1) + break; + } pPacket = htc_packet_dequeue(pPktQueue); if (!pPacket) break; @@ -451,6 +554,12 @@ static void htc_issue_packets_bundle(HTC_TARGET *target, } if (bundlesSpaceRemaining < transferLength) { + htc_handle_extra_tx_credit(pEndpoint, p_last_htc_pkt, + p_last_pkt_bundle_buffer, + &pBundleBuffer, + pBundleBuffer - + qdf_nbuf_data(bundleBuf)); + /* send out previous buffer */ htc_send_bundled_netbuf(target, pEndpoint, pBundleBuffer - last_credit_pad, @@ -480,6 +589,9 @@ static void htc_issue_packets_bundle(HTC_TARGET *target, pQueueSave = (HTC_PACKET_QUEUE *) pPacketTx->pContext; } + p_last_htc_pkt = pPacket; + p_last_pkt_bundle_buffer = pBundleBuffer; + bundlesSpaceRemaining -= transferLength; netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); @@ -528,15 +640,41 @@ static void htc_issue_packets_bundle(HTC_TARGET *target, last_credit_pad = creditPad; } /* send out remaining buffer */ - if (pBundleBuffer != qdf_nbuf_data(bundleBuf)) + if (pBundleBuffer != qdf_nbuf_data(bundleBuf)) { + htc_handle_extra_tx_credit(pEndpoint, p_last_htc_pkt, + p_last_pkt_bundle_buffer, + &pBundleBuffer, + pBundleBuffer - + qdf_nbuf_data(bundleBuf)); + htc_send_bundled_netbuf(target, pEndpoint, pBundleBuffer - last_credit_pad, pPacketTx); - else + } else { free_htc_bundle_packet(target, pPacketTx); + } } #endif /* ENABLE_BUNDLE_TX */ #else +static int htc_tx_pad_credit_avail(HTC_ENDPOINT *ep) +{ + return 1; +} + +bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep, + HTC_PACKET *p_last_htc_pkt, + unsigned char *p_last_pkt_bundle_buffer, + unsigned char **p_bundle_buffer, + int tot_data_len); +bool htc_handle_extra_tx_credit(HTC_ENDPOINT *ep, + HTC_PACKET *p_last_htc_pkt, + unsigned char *p_last_pkt_bundle_buffer, + unsigned char **p_bundle_buffer, + int tot_data_len) +{ + return FALSE; +} + static void htc_issue_packets_bundle(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET_QUEUE *pPktQueue) @@ -565,6 +703,13 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target, enum qdf_bus_type bus_type; QDF_STATUS ret; bool rt_put = false; + bool used_extra_tx_credit = false; + uint8_t *buf = NULL; + int (*update_ep_padding_credit)(void *, int); + void *ctx = NULL; + + update_ep_padding_credit = + pEndpoint->EpCallBacks.ep_padding_credit_update; bus_type = hif_get_bus_type(target->hif_dev); @@ -579,6 +724,11 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target, case QDF_BUS_TYPE_SDIO: if (!IS_TX_CREDIT_FLOW_ENABLED(pEndpoint)) break; + if (update_ep_padding_credit) { + if (htc_tx_pad_credit_avail + (pEndpoint) < 1) + break; + } case QDF_BUS_TYPE_USB: htc_issue_packets_bundle(target, pEndpoint, @@ -591,6 +741,13 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target, /* if not bundling or there was a packet that could not be * placed in a bundle, and send it by normal way */ + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (htc_tx_pad_credit_avail(pEndpoint) < 1) { + status = QDF_STATUS_E_FAILURE; + break; + } + } + pPacket = htc_packet_dequeue(pPktQueue); if (!pPacket) { /* local queue is fully drained */ @@ -678,11 +835,26 @@ static QDF_STATUS htc_issue_packets(HTC_TARGET *target, pEndpoint->TxCreditSize, HTC_HDR_LENGTH + pPacket->ActualLength); #endif + buf = (uint8_t *)qdf_nbuf_get_frag_vaddr(netbuf, 0); + used_extra_tx_credit = + htc_handle_extra_tx_credit(pEndpoint, pPacket, buf, + NULL, pPacket->ActualLength + + HTC_HDR_LENGTH); + status = hif_send_head(target->hif_dev, pEndpoint->UL_PipeID, pEndpoint->Id, HTC_HDR_LENGTH + pPacket->ActualLength, netbuf, data_attr); + if (status != QDF_STATUS_SUCCESS) { + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (used_extra_tx_credit) { + ctx = pEndpoint->EpCallBacks.pContext; + update_ep_padding_credit(ctx, 1); + } + } + } + htc_issue_tx_bundle_stats_inc(target); target->ce_send_cnt++; @@ -1690,6 +1862,7 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket, int tx_resources; QDF_STATUS status = QDF_STATUS_SUCCESS; uint32_t data_attr = 0; + bool used_extra_tx_credit = false; if (pPacket) { if ((pPacket->Endpoint >= ENDPOINT_MAX) || @@ -1834,12 +2007,41 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket, 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)) { + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (htc_tx_pad_credit_avail(pEndpoint) < 1) { + status = QDF_STATUS_E_RESOURCES; + /* put the sendQueue back at the front + * of pEndpoint->TxQueue + */ + LOCK_HTC_TX(target); + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD( + &pEndpoint->TxQueue, + &sendQueue); + UNLOCK_HTC_TX(target); + break; + } + } htc_issue_packets_bundle(target, pEndpoint, &sendQueue); } + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (htc_tx_pad_credit_avail(pEndpoint) < 1) { + status = QDF_STATUS_E_RESOURCES; + /* put the sendQueue back at the front + * of pEndpoint->TxQueue + */ + LOCK_HTC_TX(target); + HTC_PACKET_QUEUE_TRANSFER_TO_HEAD( + &pEndpoint->TxQueue, + &sendQueue); + UNLOCK_HTC_TX(target); + break; + } + } pPacket = htc_packet_dequeue(&sendQueue); if (!pPacket) break; netbuf = GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket); + pHtcHdr = (HTC_FRAME_HDR *)qdf_nbuf_get_frag_vaddr(netbuf, 0); LOCK_HTC_TX(target); /* store in look up queue to match completions */ @@ -1848,11 +2050,27 @@ QDF_STATUS htc_send_data_pkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket, pEndpoint->ul_outstanding_cnt++; UNLOCK_HTC_TX(target); + used_extra_tx_credit = + htc_handle_extra_tx_credit(pEndpoint, pPacket, + (uint8_t *)pHtcHdr, + NULL, + pPacket->ActualLength + + HTC_HDR_LENGTH); + status = hif_send_head(target->hif_dev, pEndpoint->UL_PipeID, pEndpoint->Id, HTC_HDR_LENGTH + pPacket->ActualLength, netbuf, data_attr); + if (status != QDF_STATUS_SUCCESS) { + if (pEndpoint->EpCallBacks.ep_padding_credit_update) { + if (used_extra_tx_credit) { + pEndpoint->EpCallBacks. + ep_padding_credit_update + (pEndpoint->EpCallBacks.pContext, 1); + } + } + } #if DEBUG_BUNDLE qdf_print(" Send single EP%d buffer size:0x%x, total:0x%x.", pEndpoint->Id,