blob: b72fedb4cba03ca69d8102d191e416258961d411 [file] [log] [blame]
/*
* Copyright (c) 2013-2014, 2016-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 */
#if defined(HIF_PCI)
#include "if_pci.h"
#endif
#include <vos_api.h>
extern unsigned int htc_credit_flow;
#ifndef DEBUG_CREDIT
#define DEBUG_CREDIT 0
#endif
A_STATUS HTCConnectService(HTC_HANDLE HTCHandle,
HTC_SERVICE_CONNECT_REQ *pConnectReq,
HTC_SERVICE_CONNECT_RESP *pConnectResp)
{
HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
A_STATUS status = A_OK;
HTC_PACKET *pSendPacket = NULL;
HTC_CONNECT_SERVICE_RESPONSE_MSG *pResponseMsg;
HTC_CONNECT_SERVICE_MSG *pConnectMsg;
HTC_ENDPOINT_ID assignedEndpoint = ENDPOINT_MAX;
HTC_ENDPOINT *pEndpoint;
unsigned int maxMsgSize = 0;
adf_nbuf_t netbuf;
A_UINT8 txAlloc;
int length;
A_BOOL disableCreditFlowCtrl = FALSE;
A_UINT16 conn_flags;
A_UINT16 rsp_msg_id, rsp_msg_serv_id, rsp_msg_max_msg_size;
A_UINT8 rsp_msg_status, rsp_msg_end_id, rsp_msg_serv_meta_len;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("+HTCConnectService, target:%pK SvcID:0x%X \n",
target, pConnectReq->ServiceID));
do {
AR_DEBUG_ASSERT(pConnectReq->ServiceID != 0);
if (HTC_CTRL_RSVD_SVC == pConnectReq->ServiceID) {
/* special case for pseudo control service */
assignedEndpoint = ENDPOINT_0;
maxMsgSize = HTC_MAX_CONTROL_MESSAGE_LENGTH;
txAlloc = 0;
} else {
txAlloc = HTCGetCreditAllocation(target,pConnectReq->ServiceID);
if (!txAlloc) {
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("Service %d does not allocate target credits!\n",
pConnectReq->ServiceID));
}
/* allocate a packet to send to the target */
pSendPacket = HTCAllocControlTxPacket(target);
if (NULL == pSendPacket) {
AR_DEBUG_ASSERT(FALSE);
status = A_NO_MEMORY;
break;
}
netbuf = (adf_nbuf_t)GET_HTC_PACKET_NET_BUF_CONTEXT(pSendPacket);
length = sizeof(HTC_CONNECT_SERVICE_MSG) + pConnectReq->MetaDataLength;
/* assemble connect service message */
adf_nbuf_put_tail(netbuf, length);
pConnectMsg = (HTC_CONNECT_SERVICE_MSG *)adf_nbuf_data(netbuf);
if (NULL == pConnectMsg) {
AR_DEBUG_ASSERT(0);
status = A_EFAULT;
break;
}
A_MEMZERO(pConnectMsg,sizeof(HTC_CONNECT_SERVICE_MSG));
conn_flags = (pConnectReq->ConnectionFlags & ~HTC_SET_RECV_ALLOC_MASK) |
HTC_CONNECT_FLAGS_SET_RECV_ALLOCATION(txAlloc);
HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, MESSAGEID,
HTC_MSG_CONNECT_SERVICE_ID);
HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG, SERVICE_ID,
pConnectReq->ServiceID);
HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG,
CONNECTIONFLAGS, conn_flags);
if (pConnectReq->ConnectionFlags & HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL) {
disableCreditFlowCtrl = TRUE;
}
#if defined(HIF_USB)
if (!htc_credit_flow) {
disableCreditFlowCtrl = TRUE;
}
#else
/* Only enable credit for WMI service */
if (!htc_credit_flow && pConnectReq->ServiceID != WMI_CONTROL_SVC) {
disableCreditFlowCtrl = TRUE;
}
#endif
/* check caller if it wants to transfer meta data */
if ((pConnectReq->pMetaData != NULL) &&
(pConnectReq->MetaDataLength <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
/* copy meta data into message buffer (after header ) */
A_MEMCPY((A_UINT8 *)pConnectMsg + sizeof(HTC_CONNECT_SERVICE_MSG),
pConnectReq->pMetaData,
pConnectReq->MetaDataLength);
HTC_SET_FIELD(pConnectMsg, HTC_CONNECT_SERVICE_MSG,
SERVICEMETALENGTH, pConnectReq->MetaDataLength);
}
SET_HTC_PACKET_INFO_TX(pSendPacket,
NULL,
(A_UINT8 *)pConnectMsg,
length,
ENDPOINT_0,
HTC_SERVICE_TX_PACKET_TAG);
status = HTCSendPkt((HTC_HANDLE)target,pSendPacket);
/* we don't own it anymore */
pSendPacket = NULL;
if (A_FAILED(status)) {
break;
}
/* wait for response */
status = HTCWaitRecvCtrlMessage(target);
if (A_FAILED(status)) {
break;
}
/* we controlled the buffer creation so it has to be properly aligned */
pResponseMsg = (HTC_CONNECT_SERVICE_RESPONSE_MSG *)target->CtrlResponseBuffer;
rsp_msg_id = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, MESSAGEID);
rsp_msg_serv_id = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, SERVICEID);
rsp_msg_status = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, STATUS);
rsp_msg_end_id = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, ENDPOINTID);
rsp_msg_max_msg_size = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, MAXMSGSIZE);
rsp_msg_serv_meta_len = HTC_GET_FIELD(pResponseMsg,
HTC_CONNECT_SERVICE_RESPONSE_MSG, SERVICEMETALENGTH);
if ((rsp_msg_id != HTC_MSG_CONNECT_SERVICE_RESPONSE_ID) ||
(target->CtrlResponseLength < sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG))) {
/* this message is not valid */
AR_DEBUG_ASSERT(FALSE);
status = A_EPROTO;
break;
}
AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
("HTCConnectService, service 0x%X connect response from target status:%d, assigned ep: %d\n",
rsp_msg_serv_id, rsp_msg_status, rsp_msg_end_id));
pConnectResp->ConnectRespCode = rsp_msg_status;
/* check response status */
if (rsp_msg_status != HTC_SERVICE_SUCCESS) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
(" Target failed service 0x%X connect request (status:%d)\n",
rsp_msg_serv_id, rsp_msg_status));
status = A_EPROTO;
#ifdef QCA_TX_HTT2_SUPPORT
/* Keep work and not to block the control message. */
target->CtrlResponseProcessing = FALSE;
#endif /* QCA_TX_HTT2_SUPPORT */
break;
}
assignedEndpoint = (HTC_ENDPOINT_ID)rsp_msg_end_id;
maxMsgSize = rsp_msg_max_msg_size;
if ((pConnectResp->pMetaData != NULL) &&
(rsp_msg_serv_meta_len > 0) &&
(rsp_msg_serv_meta_len <= HTC_SERVICE_META_DATA_MAX_LENGTH)) {
/* caller supplied a buffer and the target responded with data */
int copyLength = min((int)pConnectResp->BufferLength, (int)rsp_msg_serv_meta_len);
/* copy the meta data */
A_MEMCPY(pConnectResp->pMetaData,
((A_UINT8 *)pResponseMsg) + sizeof(HTC_CONNECT_SERVICE_RESPONSE_MSG),
copyLength);
pConnectResp->ActualLength = copyLength;
}
/* done processing response buffer */
target->CtrlResponseProcessing = FALSE;
}
/* the rest of these are parameter checks so set the error status */
status = A_EPROTO;
if (assignedEndpoint >= ENDPOINT_MAX) {
AR_DEBUG_ASSERT(FALSE);
break;
}
if (0 == maxMsgSize) {
AR_DEBUG_ASSERT(FALSE);
break;
}
pEndpoint = &target->EndPoint[assignedEndpoint];
pEndpoint->Id = assignedEndpoint;
if (pEndpoint->ServiceID != 0) {
/* endpoint already in use! */
AR_DEBUG_ASSERT(FALSE);
break;
}
/* return assigned endpoint to caller */
pConnectResp->Endpoint = assignedEndpoint;
pConnectResp->MaxMsgLength = maxMsgSize;
/* setup the endpoint */
pEndpoint->ServiceID = pConnectReq->ServiceID; /* this marks the endpoint in use */
pEndpoint->MaxTxQueueDepth = pConnectReq->MaxSendQueueDepth;
pEndpoint->MaxMsgLength = maxMsgSize;
pEndpoint->TxCredits = txAlloc;
pEndpoint->TxCreditSize = target->TargetCreditSize;
pEndpoint->TxCreditsPerMaxMsg = maxMsgSize / target->TargetCreditSize;
if (maxMsgSize % target->TargetCreditSize) {
pEndpoint->TxCreditsPerMaxMsg++;
}
#if DEBUG_CREDIT
adf_os_print(" Endpoint%d initial credit:%d, size:%d.\n",
pEndpoint->Id, pEndpoint->TxCredits, pEndpoint->TxCreditSize);
#endif
/* copy all the callbacks */
pEndpoint->EpCallBacks = pConnectReq->EpCallbacks;
status = HIFMapServiceToPipe(target->hif_dev,
pEndpoint->ServiceID,
&pEndpoint->UL_PipeID,
&pEndpoint->DL_PipeID,
&pEndpoint->ul_is_polled,
&pEndpoint->dl_is_polled);
if (A_FAILED(status)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,("%s Failed to Map Service to Pipe\n",
__func__));
break;
}
#if defined(HIF_SDIO)
/*
When AltDataCreditSize is non zero, it indicates the credit size for
HTT and all other services on Mbox0. Mbox1 has WMI_CONTROL_SVC which
uses the default credit size. Use AltDataCreditSize only when mailbox
is swapped. Mailbox swap bit is set by bmi_target_ready at the end of
BMI phase.
The Credit Size is a parameter associated with the mbox rather than a
service. Multiple services can run on this mbox.
If AltDataCreditSize is 0, that means the firmware doesn't support
this feature. Default to the TargetCreditSize
*/
if ((target->AltDataCreditSize) && HIFIsMailBoxSwapped(target->hif_dev)
&& (pEndpoint->UL_PipeID == 1) && (pEndpoint->DL_PipeID == 0))
pEndpoint->TxCreditSize = target->AltDataCreditSize;
#elif defined(HIF_USB)
/*
* Endpoing to pipe is one-to-one mapping in USB.
* If AltDataCreditSize is not zero, it indicates the credit size for
* HTT_DATA_MSG_SVC services use AltDataCrditSize.
*/
if ((target->AltDataCreditSize) && (pEndpoint->ServiceID == HTT_DATA_MSG_SVC))
pEndpoint->TxCreditSize = target->AltDataCreditSize;
#endif
adf_os_assert(!pEndpoint->dl_is_polled); /* not currently supported */
if (pEndpoint->ul_is_polled) {
adf_os_timer_init(
target->osdev,
&pEndpoint->ul_poll_timer,
HTCSendCompleteCheckCleanup,
pEndpoint, ADF_DEFERRABLE_TIMER);
}
AR_DEBUG_PRINTF(ATH_DEBUG_SETUP, ("HTC Service:0x%4.4X, ULpipe:%d DLpipe:%d id:%d Ready\n",
pEndpoint->ServiceID,pEndpoint->UL_PipeID,pEndpoint->DL_PipeID,pEndpoint->Id));
if (disableCreditFlowCtrl && pEndpoint->TxCreditFlowEnabled) {
pEndpoint->TxCreditFlowEnabled = FALSE;
AR_DEBUG_PRINTF(ATH_DEBUG_WARN,("HTC Service:0x%4.4X ep:%d TX flow control disabled\n",
pEndpoint->ServiceID, assignedEndpoint));
}
} while (FALSE);
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("-HTCConnectService \n"));
return status;
}
void htc_disconnect_service(HTC_ENDPOINT_ID endpoint_id)
{
void *vos_ctx = vos_get_global_context(VOS_MODULE_ID_HTC, NULL);
HTC_HANDLE *htc_hdl = vos_get_context(VOS_MODULE_ID_HTC, vos_ctx);
HTC_TARGET *target;
HTC_ENDPOINT *endpoint;
if (htc_hdl == NULL)
return;
target = GET_HTC_TARGET_FROM_HANDLE(htc_hdl);
endpoint = &target->EndPoint[endpoint_id];
LOCK_HTC_ENDPOINT_RX(endpoint);
endpoint->EpCallBacks.EpRecv = NULL;
UNLOCK_HTC_ENDPOINT_RX(endpoint);
return;
}
void HTCSetCreditDistribution(HTC_HANDLE HTCHandle,
void *pCreditDistContext,
HTC_CREDIT_DIST_CALLBACK CreditDistFunc,
HTC_CREDIT_INIT_CALLBACK CreditInitFunc,
HTC_SERVICE_ID ServicePriorityOrder[],
int ListLength)
{
/* NOT Supported, this transport does not use a credit based flow control mechanism */
}
void HTCFwEventHandler(void *context, A_STATUS status)
{
HTC_TARGET *target = (HTC_TARGET *)context;
HTC_INIT_INFO *initInfo = &target->HTCInitInfo;
/* check if target failure handler exists and pass error code to it. */
if (target->HTCInitInfo.TargetFailure != NULL) {
initInfo->TargetFailure(initInfo->pContext, status);
}
}
/* Disable ASPM : disable PCIe low power */
void htc_disable_aspm(void)
{
#if defined(HIF_PCI)
hif_disable_aspm();
#endif
}