| /* |
| * 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 |
| } |