| /* |
| * Copyright (c) 2012-2014, 2016 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. |
| */ |
| |
| #ifdef FEATURE_OEM_DATA_SUPPORT |
| /** ------------------------------------------------------------------------- * |
| ------------------------------------------------------------------------- * |
| |
| |
| \file oemDataApi.c |
| |
| Implementation for the OEM DATA REQ/RSP interfaces. |
| ========================================================================== */ |
| #include "aniGlobal.h" |
| #include "oemDataApi.h" |
| #include "palApi.h" |
| #include "smeInside.h" |
| #include "smsDebug.h" |
| |
| #include "csrSupport.h" |
| #include "wlan_qct_tl.h" |
| |
| #include "vos_diag_core_log.h" |
| #include "vos_diag_core_event.h" |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_OemDataReqOpen |
| \brief This function must be called before any API call to (OEM DATA REQ/RSP module) |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| |
| eHalStatus oemData_OemDataReqOpen(tHalHandle hHal) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tpAniSirGlobal pMac = PMAC_STRUCT(hHal); |
| |
| do |
| { |
| //initialize all the variables to null |
| vos_mem_set(&(pMac->oemData), sizeof(tOemDataStruct), 0); |
| if(!HAL_STATUS_SUCCESS(status)) |
| { |
| smsLog(pMac, LOGE, "oemData_OemDataReqOpen: Cannot allocate memory for the timer function"); |
| break; |
| } |
| } while(0); |
| |
| return status; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_OemDataReqClose |
| \brief This function must be called before closing the csr module |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| |
| eHalStatus oemData_OemDataReqClose(tHalHandle hHal) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tpAniSirGlobal pMac = PMAC_STRUCT(hHal); |
| |
| do |
| { |
| if(!HAL_STATUS_SUCCESS(status)) |
| { |
| smsLog(pMac, LOGE, "oemData_OemDataReqClose: Failed in oemData_OemDataReqClose at StopTimers"); |
| break; |
| } |
| |
| //initialize all the variables to null |
| vos_mem_set(&(pMac->oemData), sizeof(tOemDataStruct), 0); |
| } while(0); |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_ReleaseOemDataReqCommand |
| \brief This function removes the oemDataCommand from the active list and |
| and frees up any memory occupied by this |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| void oemData_ReleaseOemDataReqCommand(tpAniSirGlobal pMac, tSmeCmd *pOemDataCmd, eOemDataReqStatus oemDataReqStatus) |
| { |
| |
| //First take this command out of the active list |
| if(csrLLRemoveEntry(&pMac->sme.smeCmdActiveList, &pOemDataCmd->Link, LL_ACCESS_LOCK)) |
| { |
| if (pOemDataCmd->u.oemDataCmd.oemDataReq.data) { |
| vos_mem_free(pOemDataCmd->u.oemDataCmd.oemDataReq.data); |
| pOemDataCmd->u.oemDataCmd.oemDataReq.data = NULL; |
| } |
| vos_mem_zero(&(pOemDataCmd->u.oemDataCmd), sizeof(tOemDataCmd)); |
| /* Now put this command back on the available command list */ |
| smeReleaseCommand(pMac, pOemDataCmd); |
| } |
| else |
| { |
| smsLog(pMac, LOGE, "OEM_DATA: **************** oemData_ReleaseOemDataReqCommand cannot release the command"); |
| } |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_OemDataReq |
| \brief Request an OEM DATA RSP |
| \param sessionId - Id of session to be used |
| \param pOemDataReqID - pointer to an object to get back the request ID |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| eHalStatus oemData_OemDataReq(tHalHandle hHal, |
| tANI_U8 sessionId, |
| tOemDataReqConfig *oemDataReqConfig, |
| tANI_U32 *pOemDataReqID) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tpAniSirGlobal pMac = PMAC_STRUCT( hHal ); |
| tSmeCmd *pOemDataCmd = NULL; |
| tOemDataReq *cmd_req; |
| |
| do |
| { |
| if( !CSR_IS_SESSION_VALID( pMac, sessionId ) ) |
| { |
| status = eHAL_STATUS_FAILURE; |
| break; |
| } |
| |
| pMac->oemData.oemDataReqID = *(pOemDataReqID); |
| |
| pMac->oemData.oemDataReqActive = eANI_BOOLEAN_FALSE; |
| |
| pOemDataCmd = smeGetCommandBuffer(pMac); |
| |
| //fill up the command before posting it. |
| if(pOemDataCmd) |
| { |
| pOemDataCmd->command = eSmeCommandOemDataReq; |
| pOemDataCmd->u.oemDataCmd.oemDataReqID = pMac->oemData.oemDataReqID; |
| |
| cmd_req = &(pOemDataCmd->u.oemDataCmd.oemDataReq); |
| /* set the oem data request */ |
| cmd_req->sessionId = sessionId; |
| cmd_req->data_len = oemDataReqConfig->data_len; |
| cmd_req->data = vos_mem_malloc(cmd_req->data_len); |
| |
| if (!cmd_req->data) { |
| smsLog(pMac, LOGE, FL("memory alloc failed")); |
| status = eHAL_STATUS_FAILED_ALLOC; |
| break; |
| } |
| |
| vos_mem_copy(cmd_req->data, oemDataReqConfig->data, |
| cmd_req->data_len); |
| } |
| else |
| { |
| status = eHAL_STATUS_FAILURE; |
| break; |
| } |
| |
| //now queue this command in the sme command queue |
| //Here since this is not interacting with the csr just push the command |
| //into the sme queue. Also push this command with the normal priority |
| smePushCommand(pMac, pOemDataCmd, eANI_BOOLEAN_FALSE); |
| |
| } while(0); |
| |
| if(!HAL_STATUS_SUCCESS(status) && pOemDataCmd) |
| { |
| oemData_ReleaseOemDataReqCommand(pMac, pOemDataCmd, eOEM_DATA_REQ_FAILURE); |
| pMac->oemData.oemDataReqActive = eANI_BOOLEAN_FALSE; |
| } |
| |
| return status; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_SendMBOemDataReq |
| \brief Request an OEM DATA REQ to be passed down to PE |
| \param pMac: |
| \param pOemDataReq: Pointer to the oem data request |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| eHalStatus oemData_SendMBOemDataReq(tpAniSirGlobal pMac, tOemDataReq *pOemDataReq) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tSirOemDataReq* pMsg; |
| tCsrRoamSession *pSession; |
| |
| smsLog(pMac, LOGW, "OEM_DATA: entering Function %s", __func__); |
| |
| if (!pOemDataReq) { |
| smsLog(pMac, LOGE, FL("oem data req is NULL")); |
| return eHAL_STATUS_INVALID_PARAMETER; |
| } |
| |
| pSession = CSR_GET_SESSION(pMac, pOemDataReq->sessionId); |
| pMsg = vos_mem_malloc(sizeof(*pMsg)); |
| if (NULL == pMsg) { |
| smsLog(pMac, LOGE, "Memory Allocation failed. %s", __func__); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| pMsg->messageType = pal_cpu_to_be16((tANI_U16)eWNI_SME_OEM_DATA_REQ); |
| pMsg->messageLen = pal_cpu_to_be16((uint16_t) sizeof(*pMsg)); |
| vos_mem_copy(pMsg->selfMacAddr, pSession->selfMacAddr, sizeof(tSirMacAddr) ); |
| pMsg->data_len = pOemDataReq->data_len; |
| /* Incoming buffer ptr saved, set to null to avoid free by caller */ |
| pMsg->data = pOemDataReq->data; |
| pOemDataReq->data = NULL; |
| smsLog(pMac, LOGW, "OEM_DATA: sending message to pe%s", __func__); |
| status = palSendMBMessage(pMac->hHdd, pMsg); |
| |
| smsLog(pMac, LOGW, "OEM_DATA: exiting Function %s", __func__); |
| return status; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_ProcessOemDataReqCommand |
| \brief This function is called by the smeProcessCommand when the case hits |
| eSmeCommandOemDataReq |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| eHalStatus oemData_ProcessOemDataReqCommand(tpAniSirGlobal pMac, tSmeCmd *pOemDataReqCmd) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| |
| //check if the system is in proper mode of operation for |
| //oem data req/rsp to be functional. Currently, concurrency is not |
| //supported and the driver must be operational only as |
| //STA for oem data req/rsp to be functional. We return an invalid |
| //mode flag if it is operational as any one of the following |
| //in any of the active sessions |
| //1. AP Mode |
| //2. IBSS Mode |
| //3. BTAMP Mode ... |
| |
| if(eHAL_STATUS_SUCCESS == oemData_IsOemDataReqAllowed(pMac)) |
| { |
| smsLog(pMac, LOG1, "%s: OEM_DATA REQ allowed in the current mode", __func__); |
| pMac->oemData.oemDataReqActive = eANI_BOOLEAN_TRUE; |
| status = oemData_SendMBOemDataReq(pMac, &(pOemDataReqCmd->u.oemDataCmd.oemDataReq)); |
| } |
| else |
| { |
| smsLog(pMac, LOG1, "%s: OEM_DATA REQ not allowed in the current mode", __func__); |
| status = eHAL_STATUS_FAILURE; |
| } |
| |
| if (!HAL_STATUS_SUCCESS(status)) { |
| smsLog(pMac, LOG1, FL("OEM_DATA Failure, Release command")); |
| oemData_ReleaseOemDataReqCommand(pMac, pOemDataReqCmd, eOEM_DATA_REQ_INVALID_MODE); |
| pMac->oemData.oemDataReqActive = eANI_BOOLEAN_FALSE; |
| } |
| |
| return status; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn sme_HandleOemDataRsp |
| \brief This function processes the oem data response obtained from the PE |
| \param pMsg - Pointer to the pSirOemDataRsp |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| eHalStatus sme_HandleOemDataRsp(tHalHandle hHal, tANI_U8* pMsg) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tpAniSirGlobal pMac; |
| tListElem *pEntry = NULL; |
| tSmeCmd *pCommand = NULL; |
| tSirOemDataRsp* pOemDataRsp = NULL; |
| tOemDataReq *req; |
| |
| pMac = PMAC_STRUCT(hHal); |
| |
| smsLog(pMac, LOG1, "%s: OEM_DATA Entering", __func__); |
| |
| do |
| { |
| if(pMsg == NULL) |
| { |
| smsLog(pMac, LOGE, "in %s msg ptr is NULL", __func__); |
| status = eHAL_STATUS_FAILURE; |
| break; |
| } |
| |
| /* In this case, there can be multiple OEM Data Responses for one |
| * OEM Data request, SME does not peek into data response so SME |
| * can not know which response is the last one. So SME clears active |
| * request command on receiving first response and thereafter SME |
| * passes each subsequent response to upper user layer. |
| */ |
| pEntry = csrLLPeekHead(&pMac->sme.smeCmdActiveList, LL_ACCESS_LOCK); |
| if (pEntry) |
| { |
| pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); |
| if (eSmeCommandOemDataReq == pCommand->command) |
| { |
| if (csrLLRemoveEntry(&pMac->sme.smeCmdActiveList, |
| &pCommand->Link, LL_ACCESS_LOCK)) |
| { |
| req = &(pCommand->u.oemDataCmd.oemDataReq); |
| vos_mem_free(req->data); |
| vos_mem_set(&(pCommand->u.oemDataCmd), |
| sizeof(tOemDataCmd), 0); |
| smeReleaseCommand(pMac, pCommand); |
| } |
| } |
| } |
| |
| pOemDataRsp = (tSirOemDataRsp *)pMsg; |
| |
| /* Send to upper layer only if rsp is from target */ |
| if (pOemDataRsp->target_rsp) { |
| smsLog(pMac, LOG1, FL("received target oem data resp")); |
| send_oem_data_rsp_msg(pOemDataRsp->rsp_len, |
| pOemDataRsp->oem_data_rsp); |
| /* free this memory only if rsp is from target */ |
| vos_mem_free(pOemDataRsp->oem_data_rsp); |
| pOemDataRsp->oem_data_rsp = NULL; |
| } else { |
| smsLog(pMac, LOG1, FL("received internal oem data resp")); |
| } |
| } while(0); |
| |
| return status; |
| } |
| |
| /* --------------------------------------------------------------------------- |
| \fn oemData_IsOemDataReqAllowed |
| \brief This function checks if OEM DATA REQs can be performed in the |
| current driver state |
| \return eHalStatus |
| -------------------------------------------------------------------------------*/ |
| eHalStatus oemData_IsOemDataReqAllowed(tHalHandle hHal) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tANI_U32 sessionId; |
| |
| tpAniSirGlobal pMac = PMAC_STRUCT(hHal); |
| |
| for(sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; sessionId++) |
| { |
| if(CSR_IS_SESSION_VALID(pMac, sessionId)) |
| { |
| //co-exist with IBSS or BT-AMP mode is not supported |
| if(csrIsConnStateIbss(pMac, sessionId) || csrIsBTAMP(pMac, sessionId) ) |
| { |
| //co-exist with IBSS or BT-AMP mode is not supported |
| smsLog(pMac, LOGW, "OEM DATA REQ is not allowed due to IBSS|BTAMP exist in session %d", sessionId); |
| status = eHAL_STATUS_CSR_WRONG_STATE; |
| break; |
| } |
| } |
| } |
| |
| smsLog(pMac, LOG1, "Exiting oemData_IsOemDataReqAllowed with status %d", status); |
| |
| return (status); |
| } |
| |
| #endif /*FEATURE_OEM_DATA_SUPPORT*/ |