| /* Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * * Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * * Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in | |
| * the documentation and/or other materials provided with the | |
| * distribution. | |
| * * Neither the name of The Linux Foundation nor the names of its | |
| * contributors may be used to endorse or promote products derived | |
| * from this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED | |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS | |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * Changes from Qualcomm Innovation Center are provided under the following license: | |
| * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. | |
| * SPDX-License-Identifier: BSD-3-Clause-Clear | |
| */ | |
| #include "sync.h" | |
| #define LOG_TAG "WifiHAL" | |
| #include <utils/Log.h> | |
| #include "wifi_hal.h" | |
| #include "common.h" | |
| #include "cpp_bindings.h" | |
| #include "tdlsCommand.h" | |
| #include "vendor_definitions.h" | |
| /* Singleton Static Instance */ | |
| TdlsCommand* TdlsCommand::mTdlsCommandInstance = NULL; | |
| TdlsCommand::TdlsCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd) | |
| : WifiVendorCommand(handle, id, vendor_id, subcmd) | |
| { | |
| memset(&mHandler, 0, sizeof(mHandler)); | |
| memset(&mTDLSgetStatusRspParams, 0, sizeof(wifi_tdls_status)); | |
| mRequestId = 0; | |
| } | |
| TdlsCommand::~TdlsCommand() | |
| { | |
| mTdlsCommandInstance = NULL; | |
| unregisterVendorHandler(mVendor_id, mSubcmd); | |
| } | |
| TdlsCommand* TdlsCommand::instance(wifi_handle handle) | |
| { | |
| if (handle == NULL) { | |
| ALOGE("Interface Handle is invalid"); | |
| return NULL; | |
| } | |
| if (mTdlsCommandInstance == NULL) { | |
| mTdlsCommandInstance = new TdlsCommand(handle, 0, | |
| OUI_QCA, | |
| QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE); | |
| ALOGV("TdlsCommand %p created", mTdlsCommandInstance); | |
| return mTdlsCommandInstance; | |
| } | |
| else | |
| { | |
| if (handle != getWifiHandle(mTdlsCommandInstance->mInfo)) | |
| { | |
| /* upper layer must have cleaned up the handle and reinitialized, | |
| so we need to update the same */ | |
| ALOGV("Handle different, update the handle"); | |
| mTdlsCommandInstance->mInfo = (hal_info *)handle; | |
| } | |
| } | |
| ALOGV("TdlsCommand %p created already", mTdlsCommandInstance); | |
| return mTdlsCommandInstance; | |
| } | |
| void TdlsCommand::setSubCmd(u32 subcmd) | |
| { | |
| mSubcmd = subcmd; | |
| } | |
| /* This function will be the main handler for incoming event SUBCMD_TDLS | |
| * Call the appropriate callback handler after parsing the vendor data. | |
| */ | |
| int TdlsCommand::handleEvent(WifiEvent &event) | |
| { | |
| ALOGV("Got a TDLS message from Driver"); | |
| WifiVendorCommand::handleEvent(event); | |
| /* Parse the vendordata and get the attribute */ | |
| switch(mSubcmd) | |
| { | |
| case QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE: | |
| { | |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX | |
| + 1]; | |
| mac_addr addr; | |
| wifi_tdls_status status; | |
| memset(&addr, 0, sizeof(mac_addr)); | |
| memset(&status, 0, sizeof(wifi_tdls_status)); | |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX, | |
| (struct nlattr *)mVendorData, | |
| mDataLen, NULL); | |
| ALOGV("QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE Received"); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_MAC_ADDR]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_MAC_ADDR not found", | |
| __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| memcpy(addr, | |
| (u8 *)nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_MAC_ADDR]), | |
| nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_MAC_ADDR])); | |
| ALOGV(MAC_ADDR_STR, MAC_ADDR_ARRAY(addr)); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_STATE]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_STATE not found", | |
| __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| status.state = (wifi_tdls_state) | |
| get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_STATE]); | |
| ALOGV("TDLS: State New : %d ", status.state); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_REASON]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_REASON not found", | |
| __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| status.reason = (wifi_tdls_reason) | |
| get_s32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_REASON]); | |
| ALOGV("TDLS: Reason : %d ", status.reason); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_CHANNEL]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_CHANNEL not found", | |
| __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| status.channel = | |
| get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_CHANNEL]); | |
| ALOGV("TDLS: channel : %d ", status.channel); | |
| if (!tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GLOBAL_OPERATING_CLASS]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GLOBAL_OPERATING_CLASS" | |
| " not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| status.global_operating_class = get_u32( | |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GLOBAL_OPERATING_CLASS]); | |
| ALOGV("TDLS: global_operating_class: %d ", | |
| status.global_operating_class); | |
| if (mHandler.on_tdls_state_changed) | |
| (*mHandler.on_tdls_state_changed)(addr, status); | |
| else | |
| ALOGE("TDLS: No Callback registered: "); | |
| } | |
| break; | |
| default: | |
| /* Error case should not happen print log */ | |
| ALOGE("%s: Wrong TDLS subcmd received %d", __FUNCTION__, mSubcmd); | |
| } | |
| return NL_SKIP; | |
| } | |
| int TdlsCommand::handleResponse(WifiEvent &reply) | |
| { | |
| WifiVendorCommand::handleResponse(reply); | |
| switch(mSubcmd) | |
| { | |
| case QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS: | |
| { | |
| struct nlattr *tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1]; | |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX, | |
| (struct nlattr *)mVendorData, | |
| mDataLen, NULL); | |
| ALOGV("QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS Received"); | |
| memset(&mTDLSgetStatusRspParams, 0, sizeof(wifi_tdls_status)); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE" | |
| " not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetStatusRspParams.state = (wifi_tdls_state)get_u32( | |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE]); | |
| ALOGV("TDLS: State : %u ", mTDLSgetStatusRspParams.state); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON" | |
| " not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetStatusRspParams.reason = (wifi_tdls_reason)get_s32( | |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON]); | |
| ALOGV("TDLS: Reason : %d ", mTDLSgetStatusRspParams.reason); | |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL" | |
| " not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetStatusRspParams.channel = get_u32(tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL]); | |
| ALOGV("TDLS: channel : %d ", mTDLSgetStatusRspParams.channel); | |
| if (!tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS]) | |
| { | |
| ALOGE("%s:" | |
| "QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS" | |
| " not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetStatusRspParams.global_operating_class = | |
| get_u32(tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS]); | |
| ALOGV("TDLS: global_operating_class: %d ", | |
| mTDLSgetStatusRspParams.global_operating_class); | |
| } | |
| break; | |
| case QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: | |
| { | |
| struct nlattr *tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX + 1]; | |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX, | |
| (struct nlattr *)mVendorData, | |
| mDataLen, NULL); | |
| memset(&mTDLSgetCaps, 0, sizeof(wifiTdlsCapabilities)); | |
| if (!tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS] | |
| ) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_" | |
| "MAX_CONC_SESSIONS not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetCaps.maxConcurrentTdlsSessionNum = get_u32(tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS]); | |
| if (!tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED]) | |
| { | |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_" | |
| "FEATURES_SUPPORTED not found", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| mTDLSgetCaps.tdlsSupportedFeatures = get_u32(tb_vendor[ | |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED]); | |
| } | |
| break; | |
| default : | |
| ALOGE("%s: Wrong TDLS subcmd response received %d", | |
| __FUNCTION__, mSubcmd); | |
| } | |
| return NL_SKIP; | |
| } | |
| wifi_error TdlsCommand::setCallbackHandler(wifi_tdls_handler nHandler, u32 event) | |
| { | |
| wifi_error res; | |
| mHandler = nHandler; | |
| res = registerVendorHandler(mVendor_id, event); | |
| if (res != WIFI_SUCCESS) { | |
| /* Error case should not happen print log */ | |
| ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u", | |
| __FUNCTION__, mVendor_id, mSubcmd); | |
| } | |
| return res; | |
| } | |
| void TdlsCommand::unregisterHandler(u32 subCmd) | |
| { | |
| unregisterVendorHandler(mVendor_id, subCmd); | |
| } | |
| void TdlsCommand::getStatusRspParams(wifi_tdls_status *status) | |
| { | |
| status->channel = mTDLSgetStatusRspParams.channel; | |
| status->global_operating_class = | |
| mTDLSgetStatusRspParams.global_operating_class; | |
| status->state = mTDLSgetStatusRspParams.state; | |
| status->reason = mTDLSgetStatusRspParams.reason; | |
| } | |
| wifi_error TdlsCommand::requestResponse() | |
| { | |
| return WifiCommand::requestResponse(mMsg); | |
| } | |
| void TdlsCommand::getCapsRspParams(wifi_tdls_capabilities *caps) | |
| { | |
| caps->max_concurrent_tdls_session_num = | |
| mTDLSgetCaps.maxConcurrentTdlsSessionNum; | |
| caps->is_global_tdls_supported = | |
| !!(mTDLSgetCaps.tdlsSupportedFeatures & IS_GLOBAL_TDLS_SUPPORTED); | |
| caps->is_per_mac_tdls_supported = | |
| !!(mTDLSgetCaps.tdlsSupportedFeatures & IS_PER_MAC_TDLS_SUPPORTED); | |
| caps->is_off_channel_tdls_supported = | |
| !!(mTDLSgetCaps.tdlsSupportedFeatures & IS_OFF_CHANNEL_TDLS_SUPPORTED); | |
| ALOGV("TDLS capabilities:"); | |
| ALOGV("max_concurrent_tdls_session_numChannel : %d\n", | |
| caps->max_concurrent_tdls_session_num); | |
| ALOGV("is_global_tdls_supported : %d\n", | |
| caps->is_global_tdls_supported); | |
| ALOGV("is_per_mac_tdls_supported : %d\n", | |
| caps->is_per_mac_tdls_supported); | |
| ALOGV("is_off_channel_tdls_supported : %d \n", | |
| caps->is_off_channel_tdls_supported); | |
| } | |
| /* wifi_enable_tdls - enables TDLS-auto mode for a specific route | |
| * | |
| * params specifies hints, which provide more information about | |
| * why TDLS is being sought. The firmware should do its best to | |
| * honor the hints before downgrading regular AP link | |
| * | |
| * On successful completion, must fire on_tdls_state_changed event | |
| * to indicate the status of TDLS operation. | |
| */ | |
| wifi_error wifi_enable_tdls(wifi_interface_handle iface, | |
| mac_addr addr, | |
| wifi_tdls_params *params, | |
| wifi_tdls_handler handler) | |
| { | |
| wifi_error ret; | |
| TdlsCommand *pTdlsCommand; | |
| struct nlattr *nl_data; | |
| interface_info *iinfo = getIfaceInfo(iface); | |
| wifi_handle handle = getWifiHandle(iface); | |
| pTdlsCommand = TdlsCommand::instance(handle); | |
| if (pTdlsCommand == NULL) { | |
| ALOGE("%s: Error TdlsCommand NULL", __FUNCTION__); | |
| return WIFI_ERROR_UNKNOWN; | |
| } | |
| pTdlsCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE); | |
| /* Create the message */ | |
| ret = pTdlsCommand->create(); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->set_iface_id(iinfo->name); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| /* Add the attributes */ | |
| nl_data = pTdlsCommand->attr_start(NL80211_ATTR_VENDOR_DATA); | |
| if (!nl_data){ | |
| ret = WIFI_ERROR_UNKNOWN; | |
| goto cleanup; | |
| } | |
| ALOGV("%s: MAC_ADDR: " MAC_ADDR_STR, __FUNCTION__, MAC_ADDR_ARRAY(addr)); | |
| ret = pTdlsCommand->put_bytes(QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR, | |
| (char *)addr, 6); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| if (params != NULL) { | |
| ALOGV("%s: Channel: %d, Global operating class: %d, " | |
| "Max Latency: %dms, Min Bandwidth: %dKbps", | |
| __FUNCTION__, params->channel, params->global_operating_class, | |
| params->max_latency_ms, params->min_bandwidth_kbps); | |
| ret = pTdlsCommand->put_u32( | |
| QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL, | |
| params->channel); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->put_u32( | |
| QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS, | |
| params->global_operating_class); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->put_u32( | |
| QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS, | |
| params->max_latency_ms); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->put_u32( | |
| QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS, | |
| params->min_bandwidth_kbps); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| } | |
| pTdlsCommand->attr_end(nl_data); | |
| ret = pTdlsCommand->setCallbackHandler(handler, | |
| QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->requestResponse(); | |
| if (ret != WIFI_SUCCESS) | |
| ALOGE("%s: requestResponse Error:%d", __FUNCTION__, ret); | |
| cleanup: | |
| return ret; | |
| } | |
| /* wifi_disable_tdls - disables TDLS-auto mode for a specific route | |
| * | |
| * This terminates any existing TDLS with addr device, and frees the | |
| * device resources to make TDLS connections on new routes. | |
| * | |
| * DON'T fire any more events on 'handler' specified in earlier call to | |
| * wifi_enable_tdls after this action. | |
| */ | |
| wifi_error wifi_disable_tdls(wifi_interface_handle iface, mac_addr addr) | |
| { | |
| wifi_error ret; | |
| TdlsCommand *pTdlsCommand; | |
| struct nlattr *nl_data; | |
| interface_info *iinfo = getIfaceInfo(iface); | |
| wifi_handle handle = getWifiHandle(iface); | |
| pTdlsCommand = TdlsCommand::instance(handle); | |
| if (pTdlsCommand == NULL) { | |
| ALOGE("%s: Error TdlsCommand NULL", __FUNCTION__); | |
| return WIFI_ERROR_UNKNOWN; | |
| } | |
| pTdlsCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE); | |
| /* Create the message */ | |
| ret = pTdlsCommand->create(); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->set_iface_id(iinfo->name); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ALOGV("%s: ifindex obtained:%d", __FUNCTION__, ret); | |
| ALOGV("%s: MAC_ADDR: " MAC_ADDR_STR, __FUNCTION__, MAC_ADDR_ARRAY(addr)); | |
| /* Add the attributes */ | |
| nl_data = pTdlsCommand->attr_start(NL80211_ATTR_VENDOR_DATA); | |
| if (!nl_data){ | |
| ret = WIFI_ERROR_UNKNOWN; | |
| goto cleanup; | |
| } | |
| ret = pTdlsCommand->put_bytes(QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR, | |
| (char *)addr, 6); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| pTdlsCommand->attr_end(nl_data); | |
| ret = pTdlsCommand->requestResponse(); | |
| if (ret != WIFI_SUCCESS) | |
| ALOGE("%s: requestResponse Error:%d", __FUNCTION__, ret); | |
| cleanup: | |
| delete pTdlsCommand; | |
| return ret; | |
| } | |
| /* wifi_get_tdls_status - allows getting the status of TDLS for a specific | |
| * route | |
| */ | |
| wifi_error wifi_get_tdls_status(wifi_interface_handle iface, mac_addr addr, | |
| wifi_tdls_status *status) | |
| { | |
| wifi_error ret; | |
| TdlsCommand *pTdlsCommand; | |
| struct nlattr *nl_data; | |
| interface_info *iinfo = getIfaceInfo(iface); | |
| wifi_handle handle = getWifiHandle(iface); | |
| pTdlsCommand = TdlsCommand::instance(handle); | |
| if (pTdlsCommand == NULL) { | |
| ALOGE("%s: Error TdlsCommand NULL", __FUNCTION__); | |
| return WIFI_ERROR_UNKNOWN; | |
| } | |
| pTdlsCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS); | |
| /* Create the message */ | |
| ret = pTdlsCommand->create(); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->set_iface_id(iinfo->name); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ALOGV("%s: ifindex obtained:%d", __FUNCTION__, ret); | |
| /* Add the attributes */ | |
| nl_data = pTdlsCommand->attr_start(NL80211_ATTR_VENDOR_DATA); | |
| if (!nl_data){ | |
| ret = WIFI_ERROR_UNKNOWN; | |
| goto cleanup; | |
| } | |
| ret = pTdlsCommand->put_bytes(QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR, | |
| (char *)addr, 6); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| pTdlsCommand->attr_end(nl_data); | |
| ret = pTdlsCommand->requestResponse(); | |
| if (ret != WIFI_SUCCESS) | |
| ALOGE("%s: requestResponse Error:%d", __FUNCTION__, ret); | |
| pTdlsCommand->getStatusRspParams(status); | |
| cleanup: | |
| return ret; | |
| } | |
| /* return the current HW + Firmware combination's TDLS capabilities */ | |
| wifi_error wifi_get_tdls_capabilities(wifi_interface_handle iface, | |
| wifi_tdls_capabilities *capabilities) | |
| { | |
| wifi_error ret; | |
| TdlsCommand *pTdlsCommand; | |
| if (capabilities == NULL) { | |
| ALOGE("%s: capabilities is NULL", __FUNCTION__); | |
| return WIFI_ERROR_INVALID_ARGS; | |
| } | |
| interface_info *iinfo = getIfaceInfo(iface); | |
| wifi_handle handle = getWifiHandle(iface); | |
| pTdlsCommand = TdlsCommand::instance(handle); | |
| if (pTdlsCommand == NULL) { | |
| ALOGE("%s: Error TdlsCommand NULL", __FUNCTION__); | |
| return WIFI_ERROR_UNKNOWN; | |
| } | |
| pTdlsCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES); | |
| /* Create the message */ | |
| ret = pTdlsCommand->create(); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->set_iface_id(iinfo->name); | |
| if (ret != WIFI_SUCCESS) | |
| goto cleanup; | |
| ret = pTdlsCommand->requestResponse(); | |
| if (ret != WIFI_SUCCESS) { | |
| ALOGE("%s: requestResponse Error:%d", __FUNCTION__, ret); | |
| goto cleanup; | |
| } | |
| pTdlsCommand->getCapsRspParams(capabilities); | |
| cleanup: | |
| if (ret != WIFI_SUCCESS) | |
| memset(capabilities, 0, sizeof(wifi_tdls_capabilities)); | |
| delete pTdlsCommand; | |
| return ret; | |
| } |