| /* Copyright (c) 2015, 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 "rssi_monitor.h" |
| |
| /* Used to handle rssi command events from driver/firmware.*/ |
| typedef struct rssi_monitor_event_handler_s { |
| RSSIMonitorCommand* mRSSIMonitorCommandInstance; |
| } rssi_monitor_event_handlers; |
| |
| wifi_error initializeRSSIMonitorHandler(hal_info *info) |
| { |
| info->rssi_handlers = (rssi_monitor_event_handlers *)malloc(sizeof( |
| rssi_monitor_event_handlers)); |
| if (info->rssi_handlers) { |
| memset(info->rssi_handlers, 0, sizeof(rssi_monitor_event_handlers)); |
| } |
| else { |
| ALOGE("%s: Allocation of RSSI event handlers failed", |
| __FUNCTION__); |
| return WIFI_ERROR_OUT_OF_MEMORY; |
| } |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error cleanupRSSIMonitorHandler(hal_info *info) |
| { |
| rssi_monitor_event_handlers* event_handlers; |
| if (info && info->rssi_handlers) { |
| event_handlers = (rssi_monitor_event_handlers*) info->rssi_handlers; |
| if (event_handlers->mRSSIMonitorCommandInstance) { |
| delete event_handlers->mRSSIMonitorCommandInstance; |
| } |
| memset(event_handlers, 0, sizeof(rssi_monitor_event_handlers)); |
| free(info->rssi_handlers); |
| info->rssi_handlers = NULL; |
| return WIFI_SUCCESS; |
| } |
| ALOGE ("%s: info or info->rssi_handlers NULL", __FUNCTION__); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| void RSSIMonitorCommand::enableEventHandling() |
| { |
| pthread_mutex_lock(&rm_lock); |
| mEventHandlingEnabled = true; |
| pthread_mutex_unlock(&rm_lock); |
| } |
| |
| void RSSIMonitorCommand::disableEventHandling() |
| { |
| pthread_mutex_lock(&rm_lock); |
| mEventHandlingEnabled = false; |
| pthread_mutex_unlock(&rm_lock); |
| } |
| |
| bool RSSIMonitorCommand::isEventHandlingEnabled() |
| { |
| bool eventHandlingEnabled; |
| pthread_mutex_lock(&rm_lock); |
| eventHandlingEnabled = mEventHandlingEnabled; |
| pthread_mutex_unlock(&rm_lock); |
| |
| return eventHandlingEnabled; |
| } |
| |
| void RSSIMonitorCommand::setCallbackHandler(wifi_rssi_event_handler handler) |
| { |
| mHandler = handler; |
| } |
| |
| RSSIMonitorCommand::RSSIMonitorCommand(wifi_handle handle, int id, |
| u32 vendor_id, u32 subcmd) |
| : WifiVendorCommand(handle, id, vendor_id, subcmd) |
| { |
| memset(&mHandler, 0, sizeof(mHandler)); |
| if (registerVendorHandler(vendor_id, subcmd)) { |
| /* Error case should not happen print log */ |
| ALOGE("%s: Unable to register Vendor Handler Vendor Id=0x%x subcmd=%u", |
| __FUNCTION__, vendor_id, subcmd); |
| } |
| pthread_mutex_init(&rm_lock, NULL); |
| disableEventHandling(); |
| } |
| |
| RSSIMonitorCommand::~RSSIMonitorCommand() |
| { |
| unregisterVendorHandler(mVendor_id, mSubcmd); |
| pthread_mutex_destroy(&rm_lock); |
| } |
| |
| void RSSIMonitorCommand::setReqId(wifi_request_id reqid) |
| { |
| mId = reqid; |
| } |
| |
| RSSIMonitorCommand* RSSIMonitorCommand::instance(wifi_handle handle, |
| wifi_request_id id) |
| { |
| if (handle == NULL) { |
| ALOGE("Interface Handle is invalid"); |
| return NULL; |
| } |
| hal_info *info = getHalInfo(handle); |
| if (!info || !info->rssi_handlers) { |
| ALOGE("rssi_handlers is invalid"); |
| return NULL; |
| } |
| |
| RSSIMonitorCommand* mRSSIMonitorCommandInstance = |
| info->rssi_handlers->mRSSIMonitorCommandInstance; |
| |
| if (mRSSIMonitorCommandInstance == NULL) { |
| mRSSIMonitorCommandInstance = new RSSIMonitorCommand(handle, id, |
| OUI_QCA, |
| QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI); |
| info->rssi_handlers->mRSSIMonitorCommandInstance = mRSSIMonitorCommandInstance; |
| return mRSSIMonitorCommandInstance; |
| } |
| else |
| { |
| if (handle != getWifiHandle(mRSSIMonitorCommandInstance->mInfo)) |
| { |
| /* upper layer must have cleaned up the handle and reinitialized, |
| so we need to update the same */ |
| ALOGV("Handle different, update the handle"); |
| mRSSIMonitorCommandInstance->mInfo = (hal_info *)handle; |
| } |
| mRSSIMonitorCommandInstance->setReqId(id); |
| } |
| return mRSSIMonitorCommandInstance; |
| } |
| |
| /* This function will be the main handler for incoming event. |
| * Call the appropriate callback handler after parsing the vendor data. |
| */ |
| int RSSIMonitorCommand::handleEvent(WifiEvent &event) |
| { |
| int ret = WIFI_SUCCESS; |
| |
| if (isEventHandlingEnabled() == false) { |
| ALOGE("%s: RSSI monitor isn't running or already stopped. " |
| "Nothing to do. Exit", __FUNCTION__); |
| return ret; |
| } |
| |
| WifiVendorCommand::handleEvent(event); |
| |
| /* Parse the vendordata and get the attribute */ |
| switch(mSubcmd) |
| { |
| case QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: |
| { |
| mac_addr addr; |
| s8 rssi; |
| wifi_request_id reqId; |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX |
| + 1]; |
| nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX, |
| (struct nlattr *)mVendorData, |
| mDataLen, NULL); |
| |
| memset(addr, 0, sizeof(mac_addr)); |
| |
| if (!tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID]) |
| { |
| ALOGE("%s: ATTR_RSSI_MONITORING_REQUEST_ID not found. Exit.", |
| __FUNCTION__); |
| ret = WIFI_ERROR_INVALID_ARGS; |
| break; |
| } |
| reqId = nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID] |
| ); |
| /* If event has a different request_id, ignore that and use the |
| * request_id value which we're maintaining. |
| */ |
| if (reqId != id()) { |
| ALOGV("%s: Event has Req. ID:%d <> Ours:%d, continue...", |
| __FUNCTION__, reqId, id()); |
| reqId = id(); |
| } |
| ret = get_mac_addr(tb_vendor, |
| QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, |
| addr); |
| if (ret != WIFI_SUCCESS) { |
| return ret; |
| } |
| ALOGV(MAC_ADDR_STR, MAC_ADDR_ARRAY(addr)); |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI]) |
| { |
| ALOGE("%s: QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI" |
| " not found", __FUNCTION__); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| rssi = get_s8(tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI]); |
| ALOGV("Current RSSI : %d ", rssi); |
| |
| if (mHandler.on_rssi_threshold_breached) |
| (*mHandler.on_rssi_threshold_breached)(reqId, addr, rssi); |
| else |
| ALOGE("RSSI Monitoring: No Callback registered: "); |
| } |
| break; |
| |
| default: |
| /* Error case should not happen print log */ |
| ALOGE("%s: Wrong subcmd received %d", __FUNCTION__, mSubcmd); |
| } |
| |
| return ret; |
| } |
| |
| wifi_error wifi_start_rssi_monitoring(wifi_request_id id, |
| wifi_interface_handle iface, |
| s8 max_rssi, |
| s8 min_rssi, |
| wifi_rssi_event_handler eh) |
| { |
| wifi_error ret; |
| struct nlattr *nlData; |
| WifiVendorCommand *vCommand = NULL; |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| RSSIMonitorCommand *rssiCommand; |
| |
| ret = initialize_vendor_cmd(iface, id, |
| QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI, |
| &vCommand); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Initialization failed", __FUNCTION__); |
| return ret; |
| } |
| |
| ALOGV("%s: Max RSSI:%d Min RSSI:%d", __FUNCTION__, |
| max_rssi, min_rssi); |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, |
| QCA_WLAN_RSSI_MONITORING_START); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, |
| id); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| ret = vCommand->put_s8(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI, |
| max_rssi); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| ret = vCommand->put_s8(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI, |
| min_rssi); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| vCommand->attr_end(nlData); |
| |
| rssiCommand = RSSIMonitorCommand::instance(wifiHandle, id); |
| if (rssiCommand == NULL) { |
| ALOGE("%s: Error rssiCommand NULL", __FUNCTION__); |
| ret = WIFI_ERROR_OUT_OF_MEMORY; |
| goto cleanup; |
| } |
| |
| rssiCommand->setCallbackHandler(eh); |
| |
| ret = vCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| rssiCommand->enableEventHandling(); |
| |
| cleanup: |
| delete vCommand; |
| return ret; |
| } |
| |
| wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, |
| wifi_interface_handle iface) |
| { |
| wifi_error ret; |
| struct nlattr *nlData; |
| WifiVendorCommand *vCommand = NULL; |
| wifi_handle wifiHandle = getWifiHandle(iface); |
| RSSIMonitorCommand *rssiCommand; |
| rssi_monitor_event_handlers* event_handlers; |
| hal_info *info = getHalInfo(wifiHandle); |
| |
| event_handlers = info->rssi_handlers; |
| rssiCommand = event_handlers->mRSSIMonitorCommandInstance; |
| |
| if (rssiCommand == NULL || |
| rssiCommand->isEventHandlingEnabled() == false) { |
| ALOGE("%s: RSSI monitor isn't running or already stopped. " |
| "Nothing to do. Exit", __FUNCTION__); |
| return WIFI_ERROR_NOT_AVAILABLE; |
| } |
| |
| ret = initialize_vendor_cmd(iface, id, |
| QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI, |
| &vCommand); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s: Initialization failed", __FUNCTION__); |
| return ret; |
| } |
| |
| /* Add the vendor specific attributes for the NL command. */ |
| nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (!nlData){ |
| ret = WIFI_ERROR_UNKNOWN; |
| goto cleanup; |
| } |
| |
| ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, |
| QCA_WLAN_RSSI_MONITORING_STOP); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| ret = vCommand->put_u32(QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, |
| id); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| vCommand->attr_end(nlData); |
| |
| ret = vCommand->requestResponse(); |
| if (ret != WIFI_SUCCESS) |
| goto cleanup; |
| |
| rssiCommand->disableEventHandling(); |
| |
| cleanup: |
| delete vCommand; |
| return ret; |
| } |