257 lines
10 KiB
C++
257 lines
10 KiB
C++
/* Copyright (c) 2017, 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"
|
|
|
|
#include <utils/Log.h>
|
|
|
|
#include "wifi_hal.h"
|
|
#include "common.h"
|
|
#include "cpp_bindings.h"
|
|
#include "radio_mode.h"
|
|
#include "vendor_definitions.h"
|
|
#include <netlink/genl/genl.h>
|
|
#include <string.h>
|
|
#include <net/if.h>
|
|
|
|
/* Used to handle radio mode command events from driver/firmware.*/
|
|
void RADIOModeCommand::setCallbackHandler(wifi_radio_mode_change_handler handler)
|
|
{
|
|
mHandler = handler;
|
|
}
|
|
|
|
void RADIOModeCommand::setReqId(wifi_request_id id)
|
|
{
|
|
mreqId = id;
|
|
}
|
|
|
|
RADIOModeCommand::RADIOModeCommand(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);
|
|
}
|
|
}
|
|
|
|
RADIOModeCommand::~RADIOModeCommand()
|
|
{
|
|
unregisterVendorHandler(mVendor_id, mSubcmd);
|
|
}
|
|
|
|
|
|
RADIOModeCommand* RADIOModeCommand::instance(wifi_handle handle,
|
|
wifi_request_id id)
|
|
{
|
|
RADIOModeCommand* mRADIOModeCommandInstance;
|
|
|
|
if (handle == NULL) {
|
|
ALOGE("Interface Handle is invalid");
|
|
return NULL;
|
|
}
|
|
hal_info *info = getHalInfo(handle);
|
|
if (!info) {
|
|
ALOGE("hal_info is invalid");
|
|
return NULL;
|
|
}
|
|
mRADIOModeCommandInstance = new RADIOModeCommand(handle, id,
|
|
OUI_QCA,
|
|
QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO);
|
|
return mRADIOModeCommandInstance;
|
|
}
|
|
|
|
/* This function will be the main handler for incoming event.
|
|
* Call the appropriate callback handler after parsing the vendor data.
|
|
*/
|
|
int RADIOModeCommand::handleEvent(WifiEvent &event)
|
|
{
|
|
wifi_error ret = WIFI_SUCCESS;
|
|
int num_of_mac = 0;
|
|
wifi_mac_info mode_info;
|
|
|
|
memset(&mode_info, 0, sizeof(mode_info));
|
|
WifiVendorCommand::handleEvent(event);
|
|
|
|
/* Parse the vendordata and get the attribute */
|
|
switch(mSubcmd)
|
|
{
|
|
case QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO:
|
|
{
|
|
struct nlattr *mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_MAX + 1];
|
|
struct nlattr *modeInfo;
|
|
int rem;
|
|
|
|
nla_parse(mtb_vendor, QCA_WLAN_VENDOR_ATTR_MAC_MAX,
|
|
(struct nlattr *)mVendorData,
|
|
mDataLen, NULL);
|
|
|
|
if (mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO])
|
|
{
|
|
for (modeInfo = (struct nlattr *) nla_data(mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO]),
|
|
rem = nla_len(mtb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_INFO]);
|
|
nla_ok(modeInfo, rem);modeInfo = nla_next(modeInfo, &(rem))) {
|
|
|
|
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX+ 1];
|
|
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX,
|
|
(struct nlattr *) nla_data(modeInfo), nla_len(modeInfo), NULL);
|
|
if (!tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID])
|
|
{
|
|
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID"
|
|
" not found", __FUNCTION__);
|
|
return WIFI_ERROR_INVALID_ARGS;
|
|
}
|
|
mode_info.wlan_mac_id = nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID]);
|
|
ALOGV("mac_id[%d]: %d ", num_of_mac, mode_info.wlan_mac_id);
|
|
|
|
if (!tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND])
|
|
{
|
|
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND"
|
|
" NOT FOUND", __FUNCTION__);
|
|
return WIFI_ERROR_INVALID_ARGS;
|
|
}
|
|
mode_info.mac_band = (wlan_mac_band) nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND]);
|
|
ALOGV("mac_band[%d]: %d ", num_of_mac, mode_info.mac_band);
|
|
|
|
if (tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO])
|
|
{
|
|
int num_of_iface = 0;
|
|
struct nlattr *tb_iface;
|
|
int rem_info;
|
|
|
|
for (tb_iface = (struct nlattr *) nla_data(tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO]),
|
|
rem_info = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO]);
|
|
nla_ok(tb_iface, rem_info);tb_iface = nla_next(tb_iface, &(rem_info))) {
|
|
|
|
struct nlattr *tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX+ 1];
|
|
wifi_iface_info miface_info;
|
|
|
|
nla_parse(tb3, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX,
|
|
(struct nlattr *) nla_data(tb_iface), nla_len(tb_iface), NULL);
|
|
|
|
if (!tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX])
|
|
{
|
|
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX"
|
|
" NOT FOUND", __FUNCTION__);
|
|
return WIFI_ERROR_INVALID_ARGS;
|
|
}
|
|
if (if_indextoname(nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX]),
|
|
miface_info.iface_name) == NULL)
|
|
{
|
|
ALOGE("%s: Failed to convert %d IFINDEX to IFNAME", __FUNCTION__,
|
|
nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX]));
|
|
}
|
|
ALOGV("ifname[%d]: %s ", num_of_iface, miface_info.iface_name);
|
|
|
|
if (!tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ])
|
|
{
|
|
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ"
|
|
" NOT FOUND", __FUNCTION__);
|
|
return WIFI_ERROR_INVALID_ARGS;
|
|
}
|
|
miface_info.channel = nla_get_u32(tb3[QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ]);
|
|
ALOGV("channel[%d]: %d ", num_of_iface, miface_info.channel);
|
|
|
|
if (!num_of_iface)
|
|
mode_info.iface_info = (wifi_iface_info *)
|
|
malloc(sizeof(wifi_iface_info));
|
|
else
|
|
mode_info.iface_info = (wifi_iface_info *)
|
|
realloc(mode_info.iface_info, (num_of_iface + 1) * sizeof(wifi_iface_info));
|
|
|
|
memcpy(&mode_info.iface_info[num_of_iface], &miface_info, sizeof(wifi_iface_info));
|
|
num_of_iface++;
|
|
mode_info.num_iface = num_of_iface;
|
|
}
|
|
}
|
|
if (!num_of_mac)
|
|
mwifi_iface_mac_info = (wifi_mac_info *)
|
|
malloc(sizeof(wifi_mac_info));
|
|
else
|
|
mwifi_iface_mac_info = (wifi_mac_info *)
|
|
realloc(mwifi_iface_mac_info, (num_of_mac + 1) * (sizeof(wifi_mac_info)));
|
|
|
|
memcpy(&mwifi_iface_mac_info[num_of_mac], &mode_info, sizeof(wifi_mac_info));
|
|
num_of_mac++;
|
|
}
|
|
}
|
|
|
|
if (mHandler.on_radio_mode_change && num_of_mac) {
|
|
(*mHandler.on_radio_mode_change)(mreqId, num_of_mac, mwifi_iface_mac_info);
|
|
free(mwifi_iface_mac_info);
|
|
mwifi_iface_mac_info = NULL;
|
|
}
|
|
else {
|
|
ALOGE("No Callback registered: on radio mode change");
|
|
free(mwifi_iface_mac_info);
|
|
mwifi_iface_mac_info = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Error case should not happen print log */
|
|
ALOGE("%s: Wrong subcmd received %d", __FUNCTION__, mSubcmd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
wifi_error wifi_set_radio_mode_change_handler(wifi_request_id id,
|
|
wifi_interface_handle iface,
|
|
wifi_radio_mode_change_handler eh)
|
|
{
|
|
wifi_error ret;
|
|
WifiVendorCommand *vCommand = NULL;
|
|
wifi_handle wifiHandle = getWifiHandle(iface);
|
|
RADIOModeCommand *radiomodeCommand;
|
|
|
|
ret = initialize_vendor_cmd(iface, id,
|
|
QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO,
|
|
&vCommand);
|
|
if (ret != WIFI_SUCCESS) {
|
|
ALOGE("%s: Initialization failed", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
radiomodeCommand = RADIOModeCommand::instance(wifiHandle, id);
|
|
if (radiomodeCommand == NULL) {
|
|
ALOGE("%s: Error RadioModeCommand NULL", __FUNCTION__);
|
|
ret = WIFI_ERROR_OUT_OF_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
radiomodeCommand->setCallbackHandler(eh);
|
|
radiomodeCommand->setReqId(id);
|
|
|
|
cleanup:
|
|
delete vCommand;
|
|
return mapKernelErrortoWifiHalError(ret);
|
|
}
|