Files
android_kernel_samsung_sm86…/htc/htc.c
Poddar, Siddarth f176340b9e qcacmn: Fix to avoid skb buff leak when NBUF alloc fail
propagation from qcacld-2.0 to qcacmn.

If host fail to allocate receive packet bundle buffer
it will return no memory without freeing receive pkt queue.
Fix is to free the receive pkt queue before returning from message handler.
Also, fill the rx free list during driver load time itself with the
pre allocated memory so that Tx won’t take away this memory.

Change-Id: I4bf2aeb7bc85cc68cfa1314e6dbf5057665ba7ce
CRs-Fixed: 1079623
2017-01-04 04:02:46 -08:00

1037 خطوط
27 KiB
C

/*
* 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 <hif.h>
#include <qdf_nbuf.h> /* qdf_nbuf_t */
#include <qdf_types.h> /* qdf_print */
#define MAX_HTC_RX_BUNDLE 2
#if defined(WLAN_DEBUG) || defined(DEBUG)
static ATH_DEBUG_MASK_DESCRIPTION g_htc_debug_description[] = {
{ATH_DEBUG_SEND, "Send"},
{ATH_DEBUG_RECV, "Recv"},
{ATH_DEBUG_SYNC, "Sync"},
{ATH_DEBUG_DUMP, "Dump Data (RX or TX)"},
{ATH_DEBUG_SETUP, "Setup"},
};
ATH_DEBUG_INSTANTIATE_MODULE_VAR(htc,
"htc",
"Host Target Communications",
ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_INFO |
ATH_DEBUG_SETUP,
ATH_DEBUG_DESCRIPTION_COUNT
(g_htc_debug_description),
g_htc_debug_description);
#endif
extern unsigned int htc_credit_flow;
static void reset_endpoint_states(HTC_TARGET *target);
static void destroy_htc_tx_ctrl_packet(HTC_PACKET *pPacket)
{
qdf_nbuf_t netbuf;
netbuf = (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("free ctrl netbuf :0x%p\n", netbuf));
if (netbuf != NULL) {
qdf_nbuf_free(netbuf);
}
qdf_mem_free(pPacket);
}
static HTC_PACKET *build_htc_tx_ctrl_packet(qdf_device_t osdev)
{
HTC_PACKET *pPacket = NULL;
qdf_nbuf_t netbuf;
do {
pPacket = (HTC_PACKET *) qdf_mem_malloc(sizeof(HTC_PACKET));
if (NULL == pPacket) {
break;
}
qdf_mem_zero(pPacket, sizeof(HTC_PACKET));
netbuf = qdf_nbuf_alloc(osdev, HTC_CONTROL_BUFFER_SIZE,
20, 4, true);
if (NULL == netbuf) {
qdf_mem_free(pPacket);
pPacket = NULL;
qdf_print("%s: nbuf alloc failed\n", __func__);
break;
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("alloc ctrl netbuf :0x%p \n", netbuf));
SET_HTC_PACKET_NET_BUF_CONTEXT(pPacket, netbuf);
} while (false);
return pPacket;
}
void htc_free_control_tx_packet(HTC_TARGET *target, HTC_PACKET *pPacket)
{
#ifdef TODO_FIXME
LOCK_HTC(target);
HTC_PACKET_ENQUEUE(&target->ControlBufferTXFreeList, pPacket);
UNLOCK_HTC(target);
/* TODO_FIXME netbufs cannot be RESET! */
#else
destroy_htc_tx_ctrl_packet(pPacket);
#endif
}
HTC_PACKET *htc_alloc_control_tx_packet(HTC_TARGET *target)
{
#ifdef TODO_FIXME
HTC_PACKET *pPacket;
LOCK_HTC(target);
pPacket = htc_packet_dequeue(&target->ControlBufferTXFreeList);
UNLOCK_HTC(target);
return pPacket;
#else
return build_htc_tx_ctrl_packet(target->osdev);
#endif
}
/* Set the target failure handling callback */
void htc_set_target_failure_callback(HTC_HANDLE HTCHandle,
HTC_TARGET_FAILURE Callback)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
target->HTCInitInfo.TargetFailure = Callback;
}
void htc_dump(HTC_HANDLE HTCHandle, uint8_t CmdId, bool start)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
hif_dump(target->hif_dev, CmdId, start);
}
/* cleanup the HTC instance */
static void htc_cleanup(HTC_TARGET *target)
{
HTC_PACKET *pPacket;
/* qdf_nbuf_t netbuf; */
if (target->hif_dev != NULL) {
hif_detach_htc(target->hif_dev);
hif_mask_interrupt_call(target->hif_dev);
target->hif_dev = NULL;
}
while (true) {
pPacket = allocate_htc_packet_container(target);
if (NULL == pPacket) {
break;
}
qdf_mem_free(pPacket);
}
pPacket = target->pBundleFreeList;
while (pPacket) {
HTC_PACKET *pPacketTmp = (HTC_PACKET *) pPacket->ListLink.pNext;
qdf_mem_free(pPacket);
pPacket = pPacketTmp;
}
#ifdef TODO_FIXME
while (true) {
pPacket = htc_alloc_control_tx_packet(target);
if (NULL == pPacket) {
break;
}
netbuf = (qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pPacket);
if (netbuf != NULL) {
qdf_nbuf_free(netbuf);
}
qdf_mem_free(pPacket);
}
#endif
qdf_spinlock_destroy(&target->HTCLock);
qdf_spinlock_destroy(&target->HTCRxLock);
qdf_spinlock_destroy(&target->HTCTxLock);
qdf_spinlock_destroy(&target->HTCCreditLock);
/* free our instance */
qdf_mem_free(target);
}
#ifdef FEATURE_RUNTIME_PM
/**
* htc_runtime_pm_init(): runtime pm related intialization
*
* need to initialize a work item.
*/
static void htc_runtime_pm_init(HTC_TARGET *target)
{
qdf_create_work(0, &target->queue_kicker, htc_kick_queues, target);
}
/**
* htc_runtime_suspend() - runtime suspend HTC
*
* @htc_ctx: HTC context pointer
*
* This is a dummy function for symmetry.
*
* Return: 0 for success
*/
int htc_runtime_suspend(HTC_HANDLE htc_ctx)
{
return 0;
}
/**
* htc_runtime_resume(): resume htc
*
* The htc message queue needs to be kicked off after
* a runtime resume. Otherwise messages would get stuck.
*
* @htc_ctx: HTC context pointer
*
* Return: 0 for success;
*/
int htc_runtime_resume(HTC_HANDLE htc_ctx)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_ctx);
if (target == NULL)
return 0;
qdf_sched_work(0, &target->queue_kicker);
return 0;
}
#else
static inline void htc_runtime_pm_init(HTC_TARGET *target) { }
#endif
/* registered target arrival callback from the HIF layer */
HTC_HANDLE htc_create(void *ol_sc, HTC_INIT_INFO *pInfo, qdf_device_t osdev,
uint32_t con_mode)
{
struct hif_msg_callbacks htcCallbacks;
HTC_ENDPOINT *pEndpoint = NULL;
HTC_TARGET *target = NULL;
int i;
if (ol_sc == NULL) {
HTC_ERROR("%s: ol_sc = NULL", __func__);
return NULL;
}
HTC_TRACE("+htc_create .. HIF :%p", ol_sc);
A_REGISTER_MODULE_DEBUG_INFO(htc);
target = (HTC_TARGET *) qdf_mem_malloc(sizeof(HTC_TARGET));
if (target == NULL) {
HTC_ERROR("%s: Unable to allocate memory", __func__);
return NULL;
}
qdf_mem_zero(target, sizeof(HTC_TARGET));
htc_runtime_pm_init(target);
qdf_spinlock_create(&target->HTCLock);
qdf_spinlock_create(&target->HTCRxLock);
qdf_spinlock_create(&target->HTCTxLock);
qdf_spinlock_create(&target->HTCCreditLock);
target->is_nodrop_pkt = false;
do {
qdf_mem_copy(&target->HTCInitInfo, pInfo,
sizeof(HTC_INIT_INFO));
target->host_handle = pInfo->pContext;
target->osdev = osdev;
target->con_mode = con_mode;
reset_endpoint_states(target);
INIT_HTC_PACKET_QUEUE(&target->ControlBufferTXFreeList);
for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) {
HTC_PACKET *pPacket =
(HTC_PACKET *) qdf_mem_malloc(sizeof(HTC_PACKET));
if (pPacket != NULL) {
qdf_mem_zero(pPacket, sizeof(HTC_PACKET));
free_htc_packet_container(target, pPacket);
}
}
#ifdef TODO_FIXME
for (i = 0; i < NUM_CONTROL_TX_BUFFERS; i++) {
pPacket = build_htc_tx_ctrl_packet();
if (NULL == pPacket) {
break;
}
htc_free_control_tx_packet(target, pPacket);
}
#endif
/* setup HIF layer callbacks */
qdf_mem_zero(&htcCallbacks, sizeof(struct hif_msg_callbacks));
htcCallbacks.Context = target;
htcCallbacks.rxCompletionHandler = htc_rx_completion_handler;
htcCallbacks.txCompletionHandler = htc_tx_completion_handler;
htcCallbacks.txResourceAvailHandler = htc_tx_resource_avail_handler;
htcCallbacks.fwEventHandler = htc_fw_event_handler;
target->hif_dev = ol_sc;
/* Get HIF default pipe for HTC message exchange */
pEndpoint = &target->endpoint[ENDPOINT_0];
hif_post_init(target->hif_dev, target, &htcCallbacks);
hif_get_default_pipe(target->hif_dev, &pEndpoint->UL_PipeID,
&pEndpoint->DL_PipeID);
} while (false);
htc_recv_init(target);
HTC_TRACE("-htc_create: (0x%p)", target);
return (HTC_HANDLE) target;
}
void htc_destroy(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("+htc_destroy .. Destroying :0x%p\n", target));
hif_stop(htc_get_hif_device(HTCHandle));
if (target)
htc_cleanup(target);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_destroy\n"));
}
/* get the low level HIF device for the caller , the caller may wish to do low level
* HIF requests */
void *htc_get_hif_device(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
return target->hif_dev;
}
static void htc_control_tx_complete(void *Context, HTC_PACKET *pPacket)
{
HTC_TARGET *target = (HTC_TARGET *) Context;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("+-htc_control_tx_complete 0x%p (l:%d) \n", pPacket,
pPacket->ActualLength));
htc_free_control_tx_packet(target, pPacket);
}
/* TODO, this is just a temporary max packet size */
#define MAX_MESSAGE_SIZE 1536
/**
* htc_setup_epping_credit_allocation() - allocate credits/HTC buffers to WMI
* @scn: pointer to hif_opaque_softc
* @pEntry: pointer to tx credit allocation entry
* @credits: number of credits
*
* Return: None
*/
static void
htc_setup_epping_credit_allocation(struct hif_opaque_softc *scn,
HTC_SERVICE_TX_CREDIT_ALLOCATION *pEntry,
int credits)
{
switch (hif_get_bus_type(scn)) {
case QDF_BUS_TYPE_PCI:
pEntry++;
pEntry->service_id = WMI_DATA_BE_SVC;
pEntry->CreditAllocation = (credits >> 1);
pEntry++;
pEntry->service_id = WMI_DATA_BK_SVC;
pEntry->CreditAllocation = (credits >> 1);
break;
case QDF_BUS_TYPE_SDIO:
pEntry++;
pEntry->service_id = WMI_DATA_BE_SVC;
pEntry->CreditAllocation = credits;
break;
default:
break;
}
return;
}
/**
* htc_setup_target_buffer_assignments() - setup target buffer assignments
* @target: HTC Target Pointer
*
* Return: A_STATUS
*/
static
A_STATUS htc_setup_target_buffer_assignments(HTC_TARGET *target)
{
HTC_SERVICE_TX_CREDIT_ALLOCATION *pEntry;
A_STATUS status;
int credits;
int creditsPerMaxMsg;
creditsPerMaxMsg = MAX_MESSAGE_SIZE / target->TargetCreditSize;
if (MAX_MESSAGE_SIZE % target->TargetCreditSize) {
creditsPerMaxMsg++;
}
/* TODO, this should be configured by the caller! */
credits = target->TotalTransmitCredits;
pEntry = &target->ServiceTxAllocTable[0];
/*
* Allocate all credists/HTC buffers to WMI.
* no buffers are used/required for data. data always
* remains on host.
*/
status = A_OK;
pEntry++;
pEntry->service_id = WMI_CONTROL_SVC;
pEntry->CreditAllocation = credits;
if (HTC_IS_EPPING_ENABLED(target->con_mode)) {
/* endpoint ping is a testing tool directly on top of HTC in
* both target and host sides.
* In target side, the endppint ping fw has no wlan stack and the
* FW mboxping app directly sits on HTC and it simply drops
* or loops back TX packets. For rx perf, FW mboxping app
* generates packets and passes packets to HTC to send to host.
* There is no WMI mesage exchanges between host and target
* in endpoint ping case.
* In host side, the endpoint ping driver is a Ethernet driver
* and it directly sits on HTC. Only HIF, HTC, QDF, ADF are
* used by the endpoint ping driver. There is no wifi stack
* at all in host side also. For tx perf use case,
* the user space mboxping app sends the raw packets to endpoint
* ping driver and it directly forwards to HTC for transmission
* to stress the bus. For the rx perf, HTC passes the received
* packets to endpoint ping driver and it is passed to the user
* space through the Ethernet interface.
* For credit allocation, in SDIO bus case, only BE service is
* used for tx/rx perf testing so that all credits are given
* to BE service. In PCIe and USB bus case, endpoint ping uses both
* BE and BK services to stress the bus so that the total credits
* are equally distributed to BE and BK services.
*/
htc_setup_epping_credit_allocation(target->hif_dev,
pEntry, credits);
}
if (A_SUCCESS(status)) {
int i;
for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
if (target->ServiceTxAllocTable[i].service_id != 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_INIT,
("HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n",
i,
target->ServiceTxAllocTable[i].
service_id,
target->ServiceTxAllocTable[i].
CreditAllocation));
}
}
}
return status;
}
uint8_t htc_get_credit_allocation(HTC_TARGET *target, uint16_t service_id)
{
uint8_t allocation = 0;
int i;
for (i = 0; i < HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
if (target->ServiceTxAllocTable[i].service_id == service_id) {
allocation =
target->ServiceTxAllocTable[i].CreditAllocation;
}
}
if (0 == allocation) {
AR_DEBUG_PRINTF(ATH_DEBUG_INIT,
("HTC Service TX : 0x%2.2X : allocation is zero!\n",
service_id));
}
return allocation;
}
A_STATUS htc_wait_target(HTC_HANDLE HTCHandle)
{
A_STATUS status = A_OK;
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_READY_EX_MSG *pReadyMsg;
HTC_SERVICE_CONNECT_REQ connect;
HTC_SERVICE_CONNECT_RESP resp;
HTC_READY_MSG *rdy_msg;
uint16_t htc_rdy_msg_id;
uint8_t i = 0;
HTC_PACKET *rx_bundle_packet, *temp_bundle_packet;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("htc_wait_target - Enter (target:0x%p)\n", HTCHandle));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("+HWT\n"));
do {
status = hif_start(target->hif_dev);
if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("hif_start failed\n"));
break;
}
status = htc_wait_recv_ctrl_message(target);
if (A_FAILED(status)) {
break;
}
if (target->CtrlResponseLength < (sizeof(HTC_READY_EX_MSG))) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid HTC Ready Msg Len:%d! \n",
target->CtrlResponseLength));
status = A_ECOMM;
break;
}
pReadyMsg = (HTC_READY_EX_MSG *) target->CtrlResponseBuffer;
rdy_msg = &pReadyMsg->Version2_0_Info;
htc_rdy_msg_id =
HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, MESSAGEID);
if (htc_rdy_msg_id != HTC_MSG_READY_ID) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
("Invalid HTC Ready Msg : 0x%X ! \n",
htc_rdy_msg_id));
status = A_ECOMM;
break;
}
target->TotalTransmitCredits =
HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, CREDITCOUNT);
target->TargetCreditSize =
(int)HTC_GET_FIELD(rdy_msg, HTC_READY_MSG, CREDITSIZE);
target->MaxMsgsPerHTCBundle =
(uint8_t) pReadyMsg->MaxMsgsPerHTCBundle;
/* for old fw this value is set to 0. But the minimum value should be 1,
* i.e., no bundling */
if (target->MaxMsgsPerHTCBundle < 1)
target->MaxMsgsPerHTCBundle = 1;
AR_DEBUG_PRINTF(ATH_DEBUG_INIT,
("Target Ready! : transmit resources : %d size:%d, MaxMsgsPerHTCBundle = %d\n",
target->TotalTransmitCredits,
target->TargetCreditSize,
target->MaxMsgsPerHTCBundle));
if ((0 == target->TotalTransmitCredits)
|| (0 == target->TargetCreditSize)) {
status = A_ECOMM;
break;
}
/* Allocate expected number of RX bundle buffer allocation */
if (HTC_RX_BUNDLE_ENABLED(target)) {
temp_bundle_packet = NULL;
for (i = 0; i < MAX_HTC_RX_BUNDLE; i++) {
rx_bundle_packet =
allocate_htc_bundle_packet(target);
if (rx_bundle_packet != NULL)
rx_bundle_packet->ListLink.pNext =
(DL_LIST *)temp_bundle_packet;
else
break;
temp_bundle_packet = rx_bundle_packet;
}
target->pBundleFreeList = temp_bundle_packet;
}
/* done processing */
target->CtrlResponseProcessing = false;
htc_setup_target_buffer_assignments(target);
/* setup our pseudo HTC control endpoint connection */
qdf_mem_zero(&connect, sizeof(connect));
qdf_mem_zero(&resp, sizeof(resp));
connect.EpCallbacks.pContext = target;
connect.EpCallbacks.EpTxComplete = htc_control_tx_complete;
connect.EpCallbacks.EpRecv = htc_control_rx_complete;
connect.MaxSendQueueDepth = NUM_CONTROL_TX_BUFFERS;
connect.service_id = HTC_CTRL_RSVD_SVC;
/* connect fake service */
status = htc_connect_service((HTC_HANDLE) target,
&connect, &resp);
} while (false);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_wait_target - Exit (%d)\n", status));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("-HWT\n"));
return status;
}
/* start HTC, this is called after all services are connected */
static A_STATUS htc_config_target_hif_pipe(HTC_TARGET *target)
{
return A_OK;
}
static void reset_endpoint_states(HTC_TARGET *target)
{
HTC_ENDPOINT *pEndpoint;
int i;
for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
pEndpoint = &target->endpoint[i];
pEndpoint->service_id = 0;
pEndpoint->MaxMsgLength = 0;
pEndpoint->MaxTxQueueDepth = 0;
pEndpoint->Id = i;
INIT_HTC_PACKET_QUEUE(&pEndpoint->TxQueue);
INIT_HTC_PACKET_QUEUE(&pEndpoint->TxLookupQueue);
INIT_HTC_PACKET_QUEUE(&pEndpoint->RxBufferHoldQueue);
pEndpoint->target = target;
pEndpoint->TxCreditFlowEnabled = (bool)htc_credit_flow;
qdf_atomic_init(&pEndpoint->TxProcessCount);
}
}
/**
* htc_start() - Main HTC function to trigger HTC start
* @HTCHandle: pointer to HTC handle
*
* Return: A_OK for success or an appropriate A_STATUS error
*/
A_STATUS htc_start(HTC_HANDLE HTCHandle)
{
qdf_nbuf_t netbuf;
A_STATUS status = A_OK;
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_SETUP_COMPLETE_EX_MSG *pSetupComp;
HTC_PACKET *pSendPacket;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Enter\n"));
do {
htc_config_target_hif_pipe(target);
/* allocate a buffer to send */
pSendPacket = htc_alloc_control_tx_packet(target);
if (NULL == pSendPacket) {
AR_DEBUG_ASSERT(false);
qdf_print("%s: allocControlTxPacket failed\n",
__func__);
status = A_NO_MEMORY;
break;
}
netbuf =
(qdf_nbuf_t) GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket);
/* assemble setup complete message */
qdf_nbuf_put_tail(netbuf, sizeof(HTC_SETUP_COMPLETE_EX_MSG));
pSetupComp =
(HTC_SETUP_COMPLETE_EX_MSG *) qdf_nbuf_data(netbuf);
qdf_mem_zero(pSetupComp, sizeof(HTC_SETUP_COMPLETE_EX_MSG));
HTC_SET_FIELD(pSetupComp, HTC_SETUP_COMPLETE_EX_MSG,
MESSAGEID, HTC_MSG_SETUP_COMPLETE_EX_ID);
if (!htc_credit_flow) {
AR_DEBUG_PRINTF(ATH_DEBUG_INIT,
("HTC will not use TX credit flow control\n"));
pSetupComp->SetupFlags |=
HTC_SETUP_COMPLETE_FLAGS_DISABLE_TX_CREDIT_FLOW;
} else {
AR_DEBUG_PRINTF(ATH_DEBUG_INIT,
("HTC using TX credit flow control\n"));
}
if ((hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_SDIO) ||
(hif_get_bus_type(target->hif_dev) == QDF_BUS_TYPE_USB)) {
if (HTC_RX_BUNDLE_ENABLED(target))
pSetupComp->SetupFlags |=
HTC_SETUP_COMPLETE_FLAGS_ENABLE_BUNDLE_RECV;
hif_set_bundle_mode(target->hif_dev, true,
HTC_MAX_MSG_PER_BUNDLE_RX);
}
SET_HTC_PACKET_INFO_TX(pSendPacket,
NULL,
(uint8_t *) pSetupComp,
sizeof(HTC_SETUP_COMPLETE_EX_MSG),
ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG);
status = htc_send_pkt((HTC_HANDLE) target, pSendPacket);
if (A_FAILED(status)) {
break;
}
} while (false);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htc_start Exit\n"));
return status;
}
/*flush all queued buffers for surpriseremove case*/
void htc_flush_surprise_remove(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
int i;
HTC_ENDPOINT *pEndpoint;
#ifdef RX_SG_SUPPORT
qdf_nbuf_t netbuf;
qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue;
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_flush_surprise_remove\n"));
/* cleanup endpoints */
for (i = 0; i < ENDPOINT_MAX; i++) {
pEndpoint = &target->endpoint[i];
htc_flush_rx_hold_queue(target, pEndpoint);
htc_flush_endpoint_tx(target, pEndpoint, HTC_TX_PACKET_TAG_ALL);
}
hif_flush_surprise_remove(target->hif_dev);
#ifdef RX_SG_SUPPORT
LOCK_HTC_RX(target);
while ((netbuf = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL)
qdf_nbuf_free(netbuf);
RESET_RX_SG_CONFIG(target);
UNLOCK_HTC_RX(target);
#endif
reset_endpoint_states(target);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_flush_surprise_remove \n"));
}
/* stop HTC communications, i.e. stop interrupt reception, and flush all queued buffers */
void htc_stop(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
int i;
HTC_ENDPOINT *pEndpoint;
#ifdef RX_SG_SUPPORT
qdf_nbuf_t netbuf;
qdf_nbuf_queue_t *rx_sg_queue = &target->RxSgQueue;
#endif
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+htc_stop\n"));
/* cleanup endpoints */
for (i = 0; i < ENDPOINT_MAX; i++) {
pEndpoint = &target->endpoint[i];
htc_flush_rx_hold_queue(target, pEndpoint);
htc_flush_endpoint_tx(target, pEndpoint, HTC_TX_PACKET_TAG_ALL);
if (pEndpoint->ul_is_polled) {
qdf_timer_stop(&pEndpoint->ul_poll_timer);
qdf_timer_free(&pEndpoint->ul_poll_timer);
}
}
/* Note: htc_flush_endpoint_tx for all endpoints should be called before
* hif_stop - otherwise htc_tx_completion_handler called from
* hif_send_buffer_cleanup_on_pipe for residual tx frames in HIF layer,
* might queue the packet again to HIF Layer - which could cause tx
* buffer leak
*/
hif_stop(target->hif_dev);
#ifdef RX_SG_SUPPORT
LOCK_HTC_RX(target);
while ((netbuf = qdf_nbuf_queue_remove(rx_sg_queue)) != NULL)
qdf_nbuf_free(netbuf);
RESET_RX_SG_CONFIG(target);
UNLOCK_HTC_RX(target);
#endif
reset_endpoint_states(target);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-htc_stop\n"));
}
void htc_dump_credit_states(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
HTC_ENDPOINT *pEndpoint;
int i;
for (i = 0; i < ENDPOINT_MAX; i++) {
pEndpoint = &target->endpoint[i];
if (0 == pEndpoint->service_id)
continue;
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("--- EP : %d service_id: 0x%X --------------\n",
pEndpoint->Id, pEndpoint->service_id));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
(" TxCredits : %d\n",
pEndpoint->TxCredits));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
(" TxCreditSize : %d\n",
pEndpoint->TxCreditSize));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
(" TxCreditsPerMaxMsg : %d\n",
pEndpoint->TxCreditsPerMaxMsg));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
(" TxQueueDepth : %d\n",
HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("----------------------------------------------------\n"));
}
}
bool htc_get_endpoint_statistics(HTC_HANDLE HTCHandle,
HTC_ENDPOINT_ID Endpoint,
HTC_ENDPOINT_STAT_ACTION Action,
HTC_ENDPOINT_STATS *pStats)
{
#ifdef HTC_EP_STAT_PROFILING
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
bool clearStats = false;
bool sample = false;
switch (Action) {
case HTC_EP_STAT_SAMPLE:
sample = true;
break;
case HTC_EP_STAT_SAMPLE_AND_CLEAR:
sample = true;
clearStats = true;
break;
case HTC_EP_STAT_CLEAR:
clearStats = true;
break;
default:
break;
}
A_ASSERT(Endpoint < ENDPOINT_MAX);
/* lock out TX and RX while we sample and/or clear */
LOCK_HTC_TX(target);
LOCK_HTC_RX(target);
if (sample) {
A_ASSERT(pStats != NULL);
/* return the stats to the caller */
qdf_mem_copy(pStats, &target->endpoint[Endpoint].endpoint_stats,
sizeof(HTC_ENDPOINT_STATS));
}
if (clearStats) {
/* reset stats */
qdf_mem_zero(&target->endpoint[Endpoint].endpoint_stats,
sizeof(HTC_ENDPOINT_STATS));
}
UNLOCK_HTC_RX(target);
UNLOCK_HTC_TX(target);
return true;
#else
return false;
#endif
}
void *htc_get_targetdef(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
return hif_get_targetdef(target->hif_dev);
}
#ifdef IPA_OFFLOAD
/**
* htc_ipa_get_ce_resource() - get uc resource on lower layer
* @htc_handle: htc context
* @ce_sr_base_paddr: copyengine source ring base physical address
* @ce_sr_ring_size: copyengine source ring size
* @ce_reg_paddr: copyengine register physical address
*
* Return: None
*/
void htc_ipa_get_ce_resource(HTC_HANDLE htc_handle,
qdf_dma_addr_t *ce_sr_base_paddr,
uint32_t *ce_sr_ring_size,
qdf_dma_addr_t *ce_reg_paddr)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
if (target->hif_dev != NULL) {
hif_ipa_get_ce_resource(target->hif_dev,
ce_sr_base_paddr,
ce_sr_ring_size, ce_reg_paddr);
}
}
#endif /* IPA_OFFLOAD */
#if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT)
void htc_dump_bundle_stats(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
int total, i;
total = 0;
for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_RX; i++)
total += target->rx_bundle_stats[i];
if (total) {
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("RX Bundle stats:\n"));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("Total RX packets: %d\n",
total));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (
"Number of bundle: Number of packets\n"));
for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_RX; i++)
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("%10d:%10d(%2d%s)\n", (i+1),
target->rx_bundle_stats[i],
((target->rx_bundle_stats[i]*100)/
total), "%"));
}
total = 0;
for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_TX; i++)
total += target->tx_bundle_stats[i];
if (total) {
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("TX Bundle stats:\n"));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("Total TX packets: %d\n",
total));
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("Number of bundle: Number of packets\n"));
for (i = 0; i < HTC_MAX_MSG_PER_BUNDLE_TX; i++)
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,
("%10d:%10d(%2d%s)\n", (i+1),
target->tx_bundle_stats[i],
((target->tx_bundle_stats[i]*100)/
total), "%"));
}
}
void htc_clear_bundle_stats(HTC_HANDLE HTCHandle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
qdf_mem_zero(&target->rx_bundle_stats, sizeof(target->rx_bundle_stats));
qdf_mem_zero(&target->tx_bundle_stats, sizeof(target->tx_bundle_stats));
}
#endif
/**
* htc_vote_link_down - API to vote for link down
* @htc_handle: HTC handle
*
* API for upper layers to call HIF to vote for link down
*
* Return: void
*/
void htc_vote_link_down(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
if (!target->hif_dev)
return;
hif_vote_link_down(target->hif_dev);
}
/**
* htc_vote_link_up - API to vote for link up
* @htc_handle: HTC Handle
*
* API for upper layers to call HIF to vote for link up
*
* Return: void
*/
void htc_vote_link_up(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
if (!target->hif_dev)
return;
hif_vote_link_up(target->hif_dev);
}
/**
* htc_can_suspend_link - API to query HIF for link status
* @htc_handle: HTC Handle
*
* API for upper layers to call HIF to query if the link can suspend
*
* Return: void
*/
bool htc_can_suspend_link(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
if (!target->hif_dev)
return false;
return hif_can_suspend_link(target->hif_dev);
}
#ifdef FEATURE_RUNTIME_PM
int htc_pm_runtime_get(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
HTC_INFO("%s: %pS\n", __func__, (void *)_RET_IP_);
return hif_pm_runtime_get(target->hif_dev);
}
int htc_pm_runtime_put(HTC_HANDLE htc_handle)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(htc_handle);
HTC_INFO("%s: %pS\n", __func__, (void *)_RET_IP_);
return hif_pm_runtime_put(target->hif_dev);
}
#endif