blob: a2038f8c36641b5b80f48a6bd449caccbaaf97fc [file] [log] [blame]
/* 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;
}