| /* |
| * Copyright (c) 2012-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. |
| */ |
| |
| /** |
| * DOC: wlan_hdd_ftm.c |
| * |
| * This file contains the WLAN factory test mode implementation |
| */ |
| |
| #include <cds_mq.h> |
| #include "cds_sched.h" |
| #include <cds_api.h> |
| #include "sir_types.h" |
| #include "qdf_types.h" |
| #include "sir_api.h" |
| #include "sir_mac_prot_def.h" |
| #include "sme_api.h" |
| #include "mac_init_api.h" |
| #include "wlan_qct_sys.h" |
| #include "wlan_hdd_misc.h" |
| #include "i_cds_packet.h" |
| #include "cds_reg_service.h" |
| #include "wlan_hdd_main.h" |
| #include "qwlan_version.h" |
| #include "wma_types.h" |
| #include "cfg_api.h" |
| |
| #if defined(QCA_WIFI_FTM) |
| #include "bmi.h" |
| #include "ol_fw.h" |
| #include "wlan_hdd_cfg80211.h" |
| #include "wlan_hdd_main.h" |
| #include "hif.h" |
| #endif |
| |
| #define HDD_FTM_WMA_PRE_START_TIMEOUT (30000) /* 30 seconds */ |
| |
| #if defined(QCA_WIFI_FTM) |
| #if defined(LINUX_QCMBR) |
| #define ATH_XIOCTL_UNIFIED_UTF_CMD 0x1000 |
| #define ATH_XIOCTL_UNIFIED_UTF_RSP 0x1001 |
| #define MAX_UTF_LENGTH 1024 |
| typedef struct qcmbr_data_s { |
| unsigned int cmd; |
| unsigned int length; |
| unsigned char buf[MAX_UTF_LENGTH + 4]; |
| unsigned int copy_to_user; |
| } qcmbr_data_t; |
| typedef struct qcmbr_queue_s { |
| unsigned char utf_buf[MAX_UTF_LENGTH + 4]; |
| struct list_head list; |
| } qcmbr_queue_t; |
| LIST_HEAD(qcmbr_queue_head); |
| DEFINE_SPINLOCK(qcmbr_queue_lock); |
| #endif |
| #endif |
| |
| /** |
| * wlan_ftm_postmsg() - Post FTM message |
| * @cmd_ptr: Pointer to FTM command buffer |
| * @cmd_len: Length of command in @cmd_ptr |
| * |
| * This function is used to send FTM commands to firmware |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| static uint32_t wlan_ftm_postmsg(uint8_t *cmd_ptr, uint16_t cmd_len) |
| { |
| cds_msg_t ftmMsg; |
| |
| ENTER(); |
| |
| ftmMsg.type = WMA_FTM_CMD_REQ; |
| ftmMsg.reserved = 0; |
| ftmMsg.bodyptr = (uint8_t *) cmd_ptr; |
| ftmMsg.bodyval = 0; |
| |
| if (QDF_STATUS_SUCCESS != cds_mq_post_message(QDF_MODULE_ID_WMA, |
| &ftmMsg)) { |
| hddLog(QDF_TRACE_LEVEL_ERROR, "%s: : Failed to post Msg to HAL", |
| __func__); |
| |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| EXIT(); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_hdd_ftm_update_tgt_cfg() - Update target configuration |
| * @context: context registered with WMA |
| * @param: target configuration |
| * |
| * This function is registered with WMA via wma_open(), and is |
| * invoked via callback when target parameters are received |
| * from firmware. |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_ftm_update_tgt_cfg(void *context, void *param) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *) context; |
| struct wma_tgt_cfg *cfg = (struct wma_tgt_cfg *)param; |
| |
| if (!qdf_is_macaddr_zero(&cfg->hw_macaddr)) { |
| hdd_update_macaddr(hdd_ctx->config, cfg->hw_macaddr); |
| } else { |
| hddLog(QDF_TRACE_LEVEL_ERROR, |
| "%s: Invalid MAC passed from target, using MAC from ini file" |
| MAC_ADDRESS_STR, __func__, |
| MAC_ADDR_ARRAY(hdd_ctx->config->intfMacAddr[0].bytes)); |
| } |
| } |
| |
| #ifdef WLAN_FEATURE_LPSS |
| static inline void hdd_is_lpass_supported(tMacOpenParameters *mac_openParms, |
| hdd_context_t *hdd_ctx) |
| { |
| mac_openParms->is_lpass_enabled = hdd_ctx->config->enable_lpass_support; |
| } |
| #else |
| static inline void hdd_is_lpass_supported(tMacOpenParameters *mac_openParms, |
| hdd_context_t *hdd_ctx) |
| { } |
| #endif |
| |
| /** |
| * wlan_ftm_cds_open() - Open the CDS Module in FTM mode |
| * @p_cds_context: pointer to the global CDS context |
| * @hddContextSize: Size of the HDD context to allocate. |
| * |
| * The wlan_ftm_cds_open() function opens the CDF Scheduler |
| * Upon successful initialization: |
| * - All CDS submodules should have been initialized |
| * - The CDS scheduler should have opened |
| * - All the WLAN SW components should have been opened. This includes MAC. |
| * |
| * Returns: |
| * QDF_STATUS_SUCCESS - Scheduler was successfully initialized and |
| * is ready to be used. |
| * QDF_STATUS_E_RESOURCES - System resources (other than memory) |
| * are unavailable to initialize the scheduler |
| * QDF_STATUS_E_FAILURE - Failure to initialize the scheduler |
| */ |
| static QDF_STATUS wlan_ftm_cds_open(v_CONTEXT_t p_cds_context, |
| uint32_t hddContextSize) |
| { |
| QDF_STATUS vStatus = QDF_STATUS_SUCCESS; |
| int iter = 0; |
| tSirRetStatus sirStatus = eSIR_SUCCESS; |
| tMacOpenParameters mac_openParms; |
| p_cds_contextType gp_cds_context = (p_cds_contextType) p_cds_context; |
| #if defined(QCA_WIFI_FTM) |
| qdf_device_t qdf_ctx; |
| HTC_INIT_INFO htcInfo; |
| void *pHifContext = NULL; |
| void *pHtcContext = NULL; |
| struct ol_context *ol_ctx; |
| #endif |
| hdd_context_t *hdd_ctx; |
| |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Opening CDS", __func__); |
| |
| if (NULL == gp_cds_context) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Trying to open CDS without a PreOpen", __func__); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* Initialize the probe event */ |
| if (qdf_event_create(&gp_cds_context->ProbeEvent) != QDF_STATUS_SUCCESS) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Unable to init probeEvent", __func__); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (qdf_event_create(&(gp_cds_context->wmaCompleteEvent)) != |
| QDF_STATUS_SUCCESS) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Unable to init wmaCompleteEvent", __func__); |
| QDF_ASSERT(0); |
| |
| goto err_probe_event; |
| } |
| |
| /* Initialize the free message queue */ |
| vStatus = cds_mq_init(&gp_cds_context->freeVosMq); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to initialize CDS free message queue %d", |
| __func__, vStatus); |
| QDF_ASSERT(0); |
| goto err_wma_complete_event; |
| } |
| |
| for (iter = 0; iter < CDS_CORE_MAX_MESSAGES; iter++) { |
| (gp_cds_context->aMsgWrappers[iter]).pVosMsg = |
| &(gp_cds_context->aMsgBuffers[iter]); |
| INIT_LIST_HEAD(&gp_cds_context->aMsgWrappers[iter].msgNode); |
| cds_mq_put(&gp_cds_context->freeVosMq, |
| &(gp_cds_context->aMsgWrappers[iter])); |
| } |
| |
| /* Now Open the CDS Scheduler */ |
| vStatus = cds_sched_open(gp_cds_context, &gp_cds_context->cdf_sched, |
| sizeof(cds_sched_context)); |
| |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to open CDS Scheduler %d", __func__, |
| vStatus); |
| QDF_ASSERT(0); |
| goto err_msg_queue; |
| } |
| #if defined(QCA_WIFI_FTM) |
| /* Initialize BMI and Download firmware */ |
| pHifContext = cds_get_context(QDF_MODULE_ID_HIF); |
| if (!pHifContext) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: failed to get HIF context", __func__); |
| goto err_sched_close; |
| } |
| |
| ol_ctx = cds_get_context(QDF_MODULE_ID_BMI); |
| if (bmi_download_firmware(ol_ctx)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: BMI failed to download target", __func__); |
| goto err_bmi_close; |
| } |
| |
| htcInfo.pContext = ol_ctx; |
| htcInfo.TargetFailure = ol_target_failure; |
| htcInfo.TargetSendSuspendComplete = wma_target_suspend_acknowledge; |
| qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); |
| |
| /* Create HTC */ |
| gp_cds_context->htc_ctx = |
| htc_create(pHifContext, &htcInfo, qdf_ctx); |
| if (!gp_cds_context->htc_ctx) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: Failed to Create HTC", __func__); |
| goto err_bmi_close; |
| } |
| |
| if (bmi_done(ol_ctx)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: Failed to complete BMI phase", __func__); |
| goto err_htc_close; |
| } |
| #endif /* QCA_WIFI_FTM */ |
| |
| /*Open the WMA module */ |
| qdf_mem_set(&mac_openParms, sizeof(mac_openParms), 0); |
| mac_openParms.driverType = eDRIVER_TYPE_MFG; |
| |
| hdd_ctx = (hdd_context_t *) (gp_cds_context->pHDDContext); |
| if ((NULL == hdd_ctx) || (NULL == hdd_ctx->config)) { |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Hdd Context is Null", __func__); |
| QDF_ASSERT(0); |
| goto err_htc_close; |
| } |
| |
| mac_openParms.powersaveOffloadEnabled = |
| hdd_ctx->config->enablePowersaveOffload; |
| |
| hdd_is_lpass_supported(&mac_openParms, hdd_ctx); |
| |
| vStatus = wma_open(gp_cds_context, |
| wlan_hdd_ftm_update_tgt_cfg, NULL, &mac_openParms); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to open WMA module %d", __func__, |
| vStatus); |
| QDF_ASSERT(0); |
| goto err_htc_close; |
| } |
| #if defined(QCA_WIFI_FTM) |
| hdd_update_config(hdd_ctx); |
| |
| pHtcContext = cds_get_context(QDF_MODULE_ID_HTC); |
| if (!pHtcContext) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: failed to get HTC context", __func__); |
| goto err_wma_close; |
| } |
| if (htc_wait_target(pHtcContext)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL, |
| "%s: Failed to complete BMI phase", __func__); |
| goto err_wma_close; |
| } |
| #endif |
| |
| /* Now proceed to open the MAC */ |
| |
| /* UMA is supported in hardware for performing the |
| * frame translation 802.11 <-> 802.3 |
| */ |
| mac_openParms.frameTransRequired = 1; |
| |
| sirStatus = |
| mac_open(&(gp_cds_context->pMACContext), |
| gp_cds_context->pHDDContext, |
| &mac_openParms); |
| |
| if (eSIR_SUCCESS != sirStatus) { |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to open MAC %d", __func__, sirStatus); |
| QDF_ASSERT(0); |
| goto err_wma_close; |
| } |
| #ifndef QCA_WIFI_FTM |
| /* Now proceed to open the SME */ |
| vStatus = sme_open(gp_cds_context->pMACContext); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| /* Critical Error ... Cannot proceed further */ |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to open SME %d", __func__, vStatus); |
| goto err_mac_close; |
| } |
| |
| vStatus = sme_init_chan_list(gp_cds_context->pMACContext, |
| hdd_ctx->reg.alpha2, hdd_ctx->reg.cc_src); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to init sme channel list", __func__); |
| } else { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: CDS successfully Opened", __func__); |
| return QDF_STATUS_SUCCESS; |
| } |
| #else |
| return QDF_STATUS_SUCCESS; |
| #endif |
| |
| #ifndef QCA_WIFI_FTM |
| err_mac_close: |
| #endif |
| mac_close(gp_cds_context->pMACContext); |
| |
| err_wma_close: |
| wma_close(gp_cds_context); |
| |
| err_htc_close: |
| #if defined(QCA_WIFI_FTM) |
| if (gp_cds_context->htc_ctx) { |
| htc_destroy(gp_cds_context->htc_ctx); |
| gp_cds_context->htc_ctx = NULL; |
| } |
| |
| err_bmi_close: |
| bmi_cleanup(ol_ctx); |
| #endif /* QCA_WIFI_FTM */ |
| |
| err_sched_close: |
| cds_sched_close(gp_cds_context); |
| err_msg_queue: |
| cds_mq_deinit(&gp_cds_context->freeVosMq); |
| |
| err_wma_complete_event: |
| qdf_event_destroy(&gp_cds_context->wmaCompleteEvent); |
| |
| err_probe_event: |
| qdf_event_destroy(&gp_cds_context->ProbeEvent); |
| |
| return QDF_STATUS_E_FAILURE; |
| |
| } /* wlan_ftm_cds_open() */ |
| |
| /** |
| * wlan_ftm_cds_close() - Close the CDF Module in FTM mode |
| * @cds_context: context of cds |
| * |
| * The wlan_ftm_cds_close() function closes the CDF Module |
| * |
| * Return: QDF_STATUS_SUCCESS - successfully closed |
| */ |
| static QDF_STATUS wlan_ftm_cds_close(v_CONTEXT_t cds_context) |
| { |
| QDF_STATUS qdf_status; |
| p_cds_contextType gp_cds_context = (p_cds_contextType) cds_context; |
| |
| #ifndef QCA_WIFI_FTM |
| qdf_status = sme_close(((p_cds_contextType) cds_context)->pMACContext); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to close SME %d", __func__, qdf_status); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| #endif |
| |
| qdf_status = mac_close(((p_cds_contextType) cds_context)->pMACContext); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to close MAC %d", __func__, qdf_status); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| |
| ((p_cds_contextType) cds_context)->pMACContext = NULL; |
| |
| |
| qdf_status = wma_close(cds_context); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to close WMA %d", __func__, qdf_status); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| #if defined(QCA_WIFI_FTM) |
| if (gp_cds_context->htc_ctx) { |
| htc_stop(gp_cds_context->htc_ctx); |
| htc_destroy(gp_cds_context->htc_ctx); |
| gp_cds_context->htc_ctx = NULL; |
| } |
| qdf_status = wma_wmi_service_close(cds_context); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to close wma_wmi_service", __func__); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| |
| hif_disable_isr(gp_cds_context->pHIFContext); |
| #endif |
| |
| cds_mq_deinit(&((p_cds_contextType) cds_context)->freeVosMq); |
| |
| qdf_status = qdf_event_destroy(&gp_cds_context->ProbeEvent); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to destroy ProbeEvent %d", __func__, |
| qdf_status); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| |
| qdf_status = qdf_event_destroy(&gp_cds_context->wmaCompleteEvent); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to destroy wmaCompleteEvent %d", __func__, |
| qdf_status); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * cds_ftm_pre_start() - Pre-start CDS Module in FTM Mode |
| * @cds_context: The CDS context |
| * |
| * The cds_ftm_pre_start() function performs all pre-start activities |
| * in FTM mode. |
| * |
| * Return: QDF_STATUS_SUCCESS if pre-start was successful, an |
| * appropriate CDF_STATUS_E_* error code otherwise |
| */ |
| static QDF_STATUS cds_ftm_pre_start(v_CONTEXT_t cds_context) |
| { |
| QDF_STATUS vStatus = QDF_STATUS_SUCCESS; |
| QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; |
| p_cds_contextType p_cds_context = (p_cds_contextType) cds_context; |
| #if defined(QCA_WIFI_FTM) |
| p_cds_contextType gp_cds_context = |
| cds_get_global_context(); |
| #endif |
| |
| QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_INFO, "cds prestart"); |
| if (NULL == p_cds_context->pWMAContext) { |
| QDF_ASSERT(0); |
| QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, |
| "%s: WMA NULL context", __func__); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* Reset WMA wait event */ |
| qdf_event_reset(&p_cds_context->wmaCompleteEvent); |
| |
| /*call WMA pre start */ |
| vStatus = wma_pre_start(p_cds_context); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_ERROR, |
| "Failed to WMA prestart "); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* Need to update time out of complete */ |
| qdf_status = qdf_wait_single_event(&p_cds_context->wmaCompleteEvent, |
| HDD_FTM_WMA_PRE_START_TIMEOUT); |
| if (qdf_status != QDF_STATUS_SUCCESS) { |
| if (qdf_status == QDF_STATUS_E_TIMEOUT) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Timeout occurred before WMA complete", |
| __func__); |
| } else { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: wma_pre_start reporting other error", |
| __func__); |
| } |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| #if defined(QCA_WIFI_FTM) |
| vStatus = htc_start(gp_cds_context->htc_ctx); |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_FATAL, |
| "Failed to Start HTC"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wma_wait_for_ready_event(gp_cds_context->pWMAContext); |
| #endif /* QCA_WIFI_FTM */ |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_hdd_ftm_open() - Open HDD in FTM Mode |
| * @hdd_ctx: global HDD context |
| * |
| * The function hdd_wlan_startup calls this function to initialize the |
| * FTM specific modules. |
| * |
| * Return: 0 on success, non-zero on error |
| */ |
| int wlan_hdd_ftm_open(hdd_context_t *hdd_ctx) |
| { |
| QDF_STATUS vStatus = QDF_STATUS_SUCCESS; |
| p_cds_contextType p_cds_context = NULL; |
| |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Opening CDS", __func__); |
| |
| p_cds_context = cds_get_global_context(); |
| |
| if (NULL == p_cds_context) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| "%s: Trying to open CDS without a PreOpen", __func__); |
| QDF_ASSERT(0); |
| goto err_cdf_status_failure; |
| } |
| |
| vStatus = wlan_ftm_cds_open(p_cds_context, 0); |
| |
| if (!QDF_IS_STATUS_SUCCESS(vStatus)) { |
| hddLog(QDF_TRACE_LEVEL_FATAL, "%s: cds_open failed", __func__); |
| goto err_cdf_status_failure; |
| } |
| |
| /* |
| * only needed to start WMA, which happens in wlan_hdd_ftm_start() |
| */ |
| |
| /* Save the hal context in Adapter */ |
| hdd_ctx->hHal = |
| (tHalHandle) cds_get_context(QDF_MODULE_ID_SME); |
| |
| if (NULL == hdd_ctx->hHal) { |
| hddLog(QDF_TRACE_LEVEL_ERROR, "%s: HAL context is null", |
| __func__); |
| goto err_ftm_close; |
| } |
| |
| return 0; |
| |
| err_ftm_close: |
| wlan_ftm_cds_close(p_cds_context); |
| |
| err_cdf_status_failure: |
| return -EPERM; |
| } |
| |
| /** |
| * hdd_ftm_service_registration() - Register FTM service |
| * @hdd_ctx: global HDD context |
| * |
| * Return: 0 on success, non-zero on failure |
| */ |
| static int hdd_ftm_service_registration(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_t *adapter; |
| adapter = hdd_open_adapter(hdd_ctx, WLAN_HDD_FTM, "wlan%d", |
| wlan_hdd_get_intf_addr(hdd_ctx), false); |
| if (NULL == adapter) { |
| hddLog(QDF_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", |
| __func__); |
| goto err_adapter_open_failure; |
| } |
| |
| hdd_ctx->ftm.ftm_state = WLAN_FTM_INITIALIZED; |
| |
| return 0; |
| |
| err_adapter_open_failure: |
| |
| return -EPERM; |
| } |
| |
| /** |
| * wlan_ftm_stop() - Stop HDD in FTM mode |
| * @hdd_ctx: pointer to HDD context |
| * |
| * This function stops the following modules |
| * WMA |
| * |
| * Return: 0 on success, non-zero on failure |
| */ |
| static int wlan_ftm_stop(hdd_context_t *hdd_ctx) |
| { |
| if (hdd_ctx->ftm.ftm_state != WLAN_FTM_STARTED) { |
| hddLog(LOGP, FL("FTM has not started. No need to stop")); |
| return -EPERM; |
| } |
| wma_stop(hdd_ctx->pcds_context, HAL_STOP_TYPE_RF_KILL); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_ftm_close() - Close HDD in FTM mode |
| * @hdd_ctx: pointer to HDD context |
| * |
| * Return: 0 on success, non-zero on failure |
| */ |
| int wlan_hdd_ftm_close(hdd_context_t *hdd_ctx) |
| { |
| QDF_STATUS qdf_status; |
| v_CONTEXT_t cds_context = hdd_ctx->pcds_context; |
| |
| hdd_adapter_t *adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_FTM); |
| ENTER(); |
| if (adapter == NULL) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_FATAL, |
| "%s:adapter is NULL", __func__); |
| return -ENXIO; |
| } |
| |
| if (WLAN_FTM_STARTED == hdd_ctx->ftm.ftm_state) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_FATAL, |
| "%s: Ftm has been started. stopping ftm", __func__); |
| wlan_ftm_stop(hdd_ctx); |
| } |
| |
| hdd_close_all_adapters(hdd_ctx, false); |
| |
| qdf_status = cds_sched_close(cds_context); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to close CDS Scheduler", __func__); |
| QDF_ASSERT(QDF_IS_STATUS_SUCCESS(qdf_status)); |
| } |
| /* Close CDS */ |
| wlan_ftm_cds_close(cds_context); |
| |
| #if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR) |
| spin_lock_bh(&qcmbr_queue_lock); |
| if (!list_empty(&qcmbr_queue_head)) { |
| qcmbr_queue_t *msg_buf, *tmp_buf; |
| list_for_each_entry_safe(msg_buf, tmp_buf, &qcmbr_queue_head, |
| list) { |
| list_del(&msg_buf->list); |
| kfree(msg_buf); |
| } |
| } |
| spin_unlock_bh(&qcmbr_queue_lock); |
| #endif |
| |
| return 0; |
| |
| } |
| |
| |
| /** |
| * hdd_ftm_mc_process_msg() - Process FTM mailbox message |
| * @message: FTM response message |
| * |
| * Process FTM mailbox message |
| * |
| * Return: void |
| */ |
| static void hdd_ftm_mc_process_msg(void *message) |
| { |
| void *data; |
| uint32_t data_len; |
| |
| if (!message) { |
| hdd_err("Message is NULL, nothing to process."); |
| return; |
| } |
| |
| data_len = *((uint32_t *) message); |
| data = (uint32_t *) message + 1; |
| |
| #if defined(LINUX_QCMBR) |
| wlanqcmbr_mc_process_msg(message); |
| #else |
| #ifdef CONFIG_NL80211_TESTMODE |
| wlan_hdd_testmode_rx_event(data, (size_t) data_len); |
| #endif |
| #endif |
| return; |
| } |
| |
| /** |
| * wlan_hdd_ftm_start() - Start HDD in FTM mode |
| * @hdd_ctx: Global HDD context |
| * |
| * This function starts the following modules. |
| * 1) WMA Start. |
| * 2) HTC Start. |
| * 3) MAC Start to download the firmware. |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| static int wlan_hdd_ftm_start(hdd_context_t *hdd_ctx) |
| { |
| QDF_STATUS vStatus = QDF_STATUS_SUCCESS; |
| p_cds_contextType p_cds_context = |
| (p_cds_contextType) (hdd_ctx->pcds_context); |
| |
| if (WLAN_FTM_STARTED == hdd_ctx->ftm.ftm_state) { |
| return 0; |
| } |
| |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO, |
| "%s: Starting CLD SW", __func__); |
| |
| /* We support only one instance for now ... */ |
| if (p_cds_context == NULL) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| "%s: mismatch in context", __func__); |
| goto err_status_failure; |
| } |
| |
| if (p_cds_context->pMACContext == NULL) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| "%s: MAC NULL context", __func__); |
| goto err_status_failure; |
| } |
| |
| /* Vos preStart is calling */ |
| if (!QDF_IS_STATUS_SUCCESS(cds_ftm_pre_start(hdd_ctx->pcds_context))) { |
| hddLog(QDF_TRACE_LEVEL_FATAL, "%s: cds_pre_enable failed", |
| __func__); |
| goto err_status_failure; |
| } |
| |
| sme_register_ftm_msg_processor(hdd_ctx->hHal, hdd_ftm_mc_process_msg); |
| |
| vStatus = wma_start(p_cds_context); |
| if (vStatus != QDF_STATUS_SUCCESS) { |
| QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, |
| "%s: Failed to start WMA", __func__); |
| goto err_status_failure; |
| } |
| |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO, |
| "%s: MAC correctly started", __func__); |
| |
| if (hdd_ftm_service_registration(hdd_ctx)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| "%s: failed", __func__); |
| goto err_ftm_service_reg; |
| } |
| |
| hdd_ctx->ftm.ftm_state = WLAN_FTM_STARTED; |
| |
| return 0; |
| |
| err_ftm_service_reg: |
| wlan_hdd_ftm_close(hdd_ctx); |
| |
| err_status_failure: |
| |
| return -EPERM; |
| |
| } |
| |
| #if defined(QCA_WIFI_FTM) |
| /** |
| * hdd_ftm_start() - Start HDD in FTM mode |
| * @hdd_ctx: Global HDD context |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| int hdd_ftm_start(hdd_context_t *hdd_ctx) |
| { |
| return wlan_hdd_ftm_start(hdd_ctx); |
| } |
| #endif |
| |
| #if defined(QCA_WIFI_FTM) |
| /** |
| * hdd_ftm_stop() - Stop HDD in FTM mode |
| * @hdd_ctx: Global HDD context |
| * |
| * Return: 0 for success, non zero for failure |
| */ |
| int hdd_ftm_stop(hdd_context_t *hdd_ctx) |
| { |
| return wlan_ftm_stop(hdd_ctx); |
| } |
| #endif |
| |
| #if defined(QCA_WIFI_FTM) |
| #if defined(LINUX_QCMBR) |
| /** |
| * wlan_hdd_qcmbr_command() - QCMBR command handler |
| * @adapter: adapter upon which the command was received |
| * @pqcmbr_data: QCMBR command |
| * |
| * Return: 0 on success, non-zero on error |
| */ |
| static int wlan_hdd_qcmbr_command(hdd_adapter_t *adapter, |
| qcmbr_data_t *pqcmbr_data) |
| { |
| int ret = 0; |
| qcmbr_queue_t *qcmbr_buf = NULL; |
| |
| switch (pqcmbr_data->cmd) { |
| case ATH_XIOCTL_UNIFIED_UTF_CMD: { |
| pqcmbr_data->copy_to_user = 0; |
| if (pqcmbr_data->length) { |
| if (wlan_hdd_ftm_testmode_cmd(pqcmbr_data->buf, |
| pqcmbr_data-> |
| length) |
| != QDF_STATUS_SUCCESS) { |
| ret = -EBUSY; |
| } else { |
| ret = 0; |
| } |
| } |
| } |
| break; |
| |
| case ATH_XIOCTL_UNIFIED_UTF_RSP: { |
| pqcmbr_data->copy_to_user = 1; |
| if (!list_empty(&qcmbr_queue_head)) { |
| spin_lock_bh(&qcmbr_queue_lock); |
| qcmbr_buf = list_first_entry(&qcmbr_queue_head, |
| qcmbr_queue_t, |
| list); |
| list_del(&qcmbr_buf->list); |
| spin_unlock_bh(&qcmbr_queue_lock); |
| ret = 0; |
| } else { |
| ret = -1; |
| } |
| |
| if (!ret) { |
| memcpy(pqcmbr_data->buf, qcmbr_buf->utf_buf, |
| (MAX_UTF_LENGTH + 4)); |
| kfree(qcmbr_buf); |
| } else { |
| ret = -EAGAIN; |
| } |
| } |
| break; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| /** |
| * wlan_hdd_qcmbr_ioctl() - Compatability-mode QCMBR ioctl handler |
| * @adapter: adapter upon which the ioctl was received |
| * @ifr: the ioctl request |
| * |
| * Return: 0 on success, non-zero on error |
| */ |
| static int wlan_hdd_qcmbr_compat_ioctl(hdd_adapter_t *adapter, |
| struct ifreq *ifr) |
| { |
| qcmbr_data_t *qcmbr_data; |
| int ret = 0; |
| |
| qcmbr_data = kzalloc(sizeof(qcmbr_data_t), GFP_KERNEL); |
| if (qcmbr_data == NULL) |
| return -ENOMEM; |
| |
| if (copy_from_user(qcmbr_data, ifr->ifr_data, sizeof(*qcmbr_data))) { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| ret = wlan_hdd_qcmbr_command(adapter, qcmbr_data); |
| if (qcmbr_data->copy_to_user) { |
| ret = copy_to_user(ifr->ifr_data, qcmbr_data->buf, |
| (MAX_UTF_LENGTH + 4)); |
| } |
| |
| exit: |
| kfree(qcmbr_data); |
| return ret; |
| } |
| #else /* CONFIG_COMPAT */ |
| static int wlan_hdd_qcmbr_compat_ioctl(hdd_adapter_t *adapter, |
| struct ifreq *ifr) |
| { |
| return 0; |
| } |
| #endif /* CONFIG_COMPAT */ |
| |
| /** |
| * wlan_hdd_qcmbr_ioctl() - Standard QCMBR ioctl handler |
| * @adapter: adapter upon which the ioctl was received |
| * @ifr: the ioctl request |
| * |
| * Return: 0 on success, non-zero on error |
| */ |
| static int wlan_hdd_qcmbr_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr) |
| { |
| qcmbr_data_t *qcmbr_data; |
| int ret = 0; |
| |
| qcmbr_data = kzalloc(sizeof(qcmbr_data_t), GFP_KERNEL); |
| if (qcmbr_data == NULL) |
| return -ENOMEM; |
| |
| if (copy_from_user(qcmbr_data, ifr->ifr_data, sizeof(*qcmbr_data))) { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| ret = wlan_hdd_qcmbr_command(adapter, qcmbr_data); |
| if (qcmbr_data->copy_to_user) { |
| ret = copy_to_user(ifr->ifr_data, qcmbr_data->buf, |
| (MAX_UTF_LENGTH + 4)); |
| } |
| |
| exit: |
| kfree(qcmbr_data); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_qcmbr_unified_ioctl() - Unified QCMBR ioctl handler |
| * @adapter: adapter upon which the ioctl was received |
| * @ifr: the ioctl request |
| * |
| * Return: 0 on success, non-zero on error |
| */ |
| int wlan_hdd_qcmbr_unified_ioctl(hdd_adapter_t *adapter, struct ifreq *ifr) |
| { |
| int ret = 0; |
| |
| if (is_compat_task()) { |
| ret = wlan_hdd_qcmbr_compat_ioctl(adapter, ifr); |
| } else { |
| ret = wlan_hdd_qcmbr_ioctl(adapter, ifr); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * wlanqcmbr_mc_process_msg() - Process QCMBR response message |
| * @message: QCMBR message |
| * |
| * Return: None |
| */ |
| static void wlanqcmbr_mc_process_msg(void *message) |
| { |
| qcmbr_queue_t *qcmbr_buf = NULL; |
| uint32_t data_len; |
| |
| data_len = *((uint32_t *) message) + sizeof(uint32_t); |
| qcmbr_buf = kzalloc(sizeof(qcmbr_queue_t), GFP_KERNEL); |
| if (qcmbr_buf != NULL) { |
| memcpy(qcmbr_buf->utf_buf, message, data_len); |
| spin_lock_bh(&qcmbr_queue_lock); |
| list_add_tail(&(qcmbr_buf->list), &qcmbr_queue_head); |
| spin_unlock_bh(&qcmbr_queue_lock); |
| } |
| } |
| #endif /*LINUX_QCMBR */ |
| |
| /** |
| * wlan_hdd_ftm_testmode_cmd() - Process FTM testmode command |
| * @data: FTM testmode command |
| * @len: length of @data |
| * |
| * Return: QDF_STATUS_SUCCESS on success, CDF_STATUS_E_* on error |
| */ |
| QDF_STATUS wlan_hdd_ftm_testmode_cmd(void *data, int len) |
| { |
| struct ar6k_testmode_cmd_data *cmd_data; |
| |
| cmd_data = (struct ar6k_testmode_cmd_data *) |
| qdf_mem_malloc(sizeof(*cmd_data)); |
| |
| if (!cmd_data) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| ("Failed to allocate FTM command data")); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| cmd_data->data = qdf_mem_malloc(len); |
| |
| if (!cmd_data->data) { |
| QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR, |
| ("Failed to allocate FTM command data buffer")); |
| qdf_mem_free(cmd_data); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| cmd_data->len = len; |
| qdf_mem_copy(cmd_data->data, data, len); |
| |
| if (wlan_ftm_postmsg((uint8_t *) cmd_data, sizeof(*cmd_data))) { |
| qdf_mem_free(cmd_data->data); |
| qdf_mem_free(cmd_data); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif /*QCA_WIFI_FTM */ |