/* Copyright (c) 2015, 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"
#include "qca-vendor.h"
#include "vendor_definitions.h"
//Singleton Static Instance
RSSIMonitorCommand* RSSIMonitorCommand::mRSSIMonitorCommandInstance = NULL;
RSSIMonitorCommand::RSSIMonitorCommand(wifi_handle handle, int id,
u32 vendor_id, u32 subcmd)
: WifiVendorCommand(handle, id, vendor_id, subcmd)
{
mRSSIMonitorCommandInstance = NULL;
memset(&mHandler, 0, sizeof(mHandler));
}
RSSIMonitorCommand::~RSSIMonitorCommand()
{
mRSSIMonitorCommandInstance = NULL;
}
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;
}
if (mRSSIMonitorCommandInstance == NULL) {
mRSSIMonitorCommandInstance = new RSSIMonitorCommand(handle, id,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI);
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 */
ALOGI("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;
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));
ALOGI("QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI Received");
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()) {
ALOGD("%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;
}
ALOGI(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]);
ALOGI("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 NL_SKIP;
}
int RSSIMonitorCommand::setCallbackHandler(wifi_rssi_event_handler nHandler,
u32 event)
{
int ret;
mHandler = nHandler;
ret = registerVendorHandler(mVendor_id, event);
if (ret != 0) {
/* 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 ret;
}
wifi_error RSSIMonitorCommand::unregisterHandler(u32 subCmd)
{
unregisterVendorHandler(mVendor_id, subCmd);
return WIFI_SUCCESS;
}
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)
{
int ret = WIFI_SUCCESS;
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 (wifi_error)ret;
}
ALOGI("%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;
if (vCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
QCA_WLAN_RSSI_MONITORING_START) ||
vCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
id) ||
vCommand->put_s8(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI,
max_rssi) ||
vCommand->put_s8(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI,
min_rssi))
{
goto cleanup;
}
vCommand->attr_end(nlData);
rssiCommand = RSSIMonitorCommand::instance(wifiHandle, id);
if (rssiCommand == NULL) {
ALOGE("%s: Error rssiCommand NULL", __FUNCTION__);
return WIFI_ERROR_OUT_OF_MEMORY;
}
ret = rssiCommand->setCallbackHandler(eh,
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI);
if (ret < 0)
goto cleanup;
ret = vCommand->requestResponse();
if (ret < 0)
goto cleanup;
cleanup:
delete vCommand;
return (wifi_error)ret;
}
wifi_error wifi_stop_rssi_monitoring(wifi_request_id id,
wifi_interface_handle iface)
{
int ret = WIFI_SUCCESS;
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 (wifi_error)ret;
}
/* Add the vendor specific attributes for the NL command. */
nlData = vCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nlData)
goto cleanup;
if (vCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
QCA_WLAN_RSSI_MONITORING_STOP) ||
vCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
id))
{
goto cleanup;
}
vCommand->attr_end(nlData);
ret = vCommand->requestResponse();
if (ret < 0)
goto cleanup;
rssiCommand = RSSIMonitorCommand::instance(wifiHandle, id);
if (rssiCommand == NULL) {
ALOGE("%s: Error rssiCommand NULL", __FUNCTION__);
ret = WIFI_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
ret = rssiCommand->unregisterHandler(
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI);
if (ret != WIFI_SUCCESS)
goto cleanup;
delete rssiCommand;
cleanup:
delete vCommand;
return (wifi_error)ret;
}