| /* 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. | |
| */ | |
| #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) | |
| 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) | |
| 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; | |
| } |