/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sync.h"
#define LOG_TAG "WifiHAL"
#include <utils/Log.h>
#include "wifi_hal.h"
#include "common.h"
#include "cpp_bindings.h"
#include "llstatscommand.h"
#include "vendor_definitions.h"
//Singleton Static Instance
LLStatsCommand* LLStatsCommand::mLLStatsCommandInstance = NULL;
// This function implements creation of Vendor command
// For LLStats just call base Vendor command create
int LLStatsCommand::create() {
int ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
if (ret < 0) {
return ret;
}
// insert the oui in the msg
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
if (ret < 0)
goto out;
// insert the subcmd in the msg
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
if (ret < 0)
goto out;
out:
return ret;
}
LLStatsCommand::LLStatsCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd)
: WifiVendorCommand(handle, id, vendor_id, subcmd)
{
memset(&mClearRspParams, 0,sizeof(LLStatsClearRspParams));
memset(&mResultsParams, 0,sizeof(LLStatsResultsParams));
memset(&mHandler, 0,sizeof(mHandler));
mRadioStatsSize = 0;
mNumRadios = 0;
}
LLStatsCommand::~LLStatsCommand()
{
mLLStatsCommandInstance = NULL;
}
LLStatsCommand* LLStatsCommand::instance(wifi_handle handle)
{
if (handle == NULL) {
ALOGE("Interface Handle is invalid");
return NULL;
}
if (mLLStatsCommandInstance == NULL) {
mLLStatsCommandInstance = new LLStatsCommand(handle, 0,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET);
return mLLStatsCommandInstance;
}
else
{
if (handle != getWifiHandle(mLLStatsCommandInstance->mInfo))
{
/* upper layer must have cleaned up the handle and reinitialized,
so we need to update the same */
ALOGE("Handle different, update the handle");
mLLStatsCommandInstance->mInfo = (hal_info *)handle;
}
}
return mLLStatsCommandInstance;
}
void LLStatsCommand::initGetContext(u32 reqId)
{
mRequestId = reqId;
memset(&mHandler, 0,sizeof(mHandler));
}
void LLStatsCommand::setSubCmd(u32 subcmd)
{
mSubcmd = subcmd;
}
void LLStatsCommand::setHandler(wifi_stats_result_handler handler)
{
mHandler = handler;
}
static wifi_error get_wifi_interface_info(wifi_interface_link_layer_info *stats,
struct nlattr **tb_vendor)
{
u32 len = 0;
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mode = (wifi_interface_mode)nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR]);
len = ((sizeof(stats->mac_addr) <= len) ? sizeof(stats->mac_addr) : len);
memcpy(&stats->mac_addr[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR]), len);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->state = (wifi_connection_state)nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->roaming = (wifi_roam_state)nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->capabilities = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID]);
len = ((sizeof(stats->ssid) <= len) ? sizeof(stats->ssid) : len);
memcpy(&stats->ssid[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID]), len);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID]);
len = ((sizeof(stats->bssid) <= len) ? sizeof(stats->bssid) : len);
memcpy(&stats->bssid[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID]), len);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR]);
len = ((sizeof(stats->ap_country_str) <= len) ? sizeof(stats->ap_country_str) : len);
memcpy(&stats->ap_country_str[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR]),
len);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR]);
len = ((sizeof(stats->country_str) < len) ? sizeof(stats->country_str) : len);
memcpy(&stats->country_str[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR]),
len);
#if QC_HAL_DEBUG
ALOGV("Mode : %d\n"
"Mac addr : "
MAC_ADDR_STR
"\nState : %d\n"
"Roaming : %d\n"
"capabilities : %0x\n"
"SSID :%s\n"
"BSSID : "
MAC_ADDR_STR
"\nAP country str : %c%c%c\n"
"Country String for this Association : %c%c%c",
stats->mode,
MAC_ADDR_ARRAY(stats->mac_addr),
stats->state,
stats->roaming,
stats->capabilities,
stats->ssid,
MAC_ADDR_ARRAY(stats->bssid),
stats->ap_country_str[0],
stats->ap_country_str[1],
stats->ap_country_str[2],
stats->country_str[0],
stats->country_str[1],
stats->country_str[2]);
#endif
return WIFI_SUCCESS;
}
static wifi_error get_wifi_wmm_ac_stat(wifi_wmm_ac_stat *stats,
struct nlattr **tb_vendor)
{
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->ac = (wifi_traffic_ac)nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_mpdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rx_mpdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_mcast = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rx_mcast = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rx_ampdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_ampdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mpdu_lost = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries_short = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries_long = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->contention_time_min = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->contention_time_max = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->contention_time_avg = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->contention_num_samples = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES]);
#ifdef QC_HAL_DEBUG
ALOGV("%4u | %6u | %6u | %7u | %7u | %7u |"
" %7u | %8u | %7u | %12u |"
" %11u | %17u | %17u |"
" %17u | %20u",
stats->ac,
stats->tx_mpdu,
stats->rx_mpdu,
stats->tx_mcast,
stats->rx_mcast,
stats->rx_ampdu,
stats->tx_ampdu,
stats->mpdu_lost,
stats->retries,
stats->retries_short,
stats->retries_long,
stats->contention_time_min,
stats->contention_time_max,
stats->contention_time_avg,
stats->contention_num_samples);
#endif
return WIFI_SUCCESS;
}
static wifi_error get_wifi_rate_stat(wifi_rate_stat *stats,
struct nlattr **tb_vendor)
{
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rate.preamble = nla_get_u8(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rate.nss = nla_get_u8(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rate.bw = nla_get_u8(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rate.rateMcsIdx = nla_get_u8(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rate.bitrate = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_mpdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rx_mpdu = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mpdu_lost = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries_short = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->retries_long = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG]);
#ifdef QC_HAL_DEBUG
ALOGV("%8u | %3u | %2u | %10u | %7u | %6u | %6u | %8u | %7u | %12u | %11u",
stats->rate.preamble,
stats->rate.nss,
stats->rate.bw,
stats->rate.rateMcsIdx,
stats->rate.bitrate,
stats->tx_mpdu,
stats->rx_mpdu,
stats->mpdu_lost,
stats->retries,
stats->retries_short,
stats->retries_long);
#endif
return WIFI_SUCCESS;
}
static wifi_error get_wifi_peer_info(wifi_peer_info *stats,
struct nlattr **tb_vendor)
{
u32 i = 0, len = 0;
int rem;
wifi_rate_stat * pRateStats;
struct nlattr *rateInfo;
wifi_error ret = WIFI_SUCCESS;
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->type = (wifi_peer_type)nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS]);
len = ((sizeof(stats->peer_mac_address) <= len) ? sizeof(stats->peer_mac_address) : len);
memcpy((void *)&stats->peer_mac_address[0], nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS]),
len);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->capabilities = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->num_rate = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES]);
#ifdef QC_HAL_DEBUG
ALOGV("numPeers %u Peer MAC addr :" MAC_ADDR_STR " capabilities %0x numRate %u",
stats->type, MAC_ADDR_ARRAY(stats->peer_mac_address),
stats->capabilities, stats->num_rate);
#endif
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
#ifdef QC_HAL_DEBUG
ALOGV("%8s | %3s | %2s | %10s | %7s | %6s | %6s | %8s | %7s | %12s | %11s",
"preamble", "nss", "bw", "rateMcsIdx", "bitrate", "txMpdu", "rxMpdu", "mpduLost", "retries", "retriesShort", "retriesLong");
#endif
for (rateInfo = (struct nlattr *) nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO]), rem = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO]);
nla_ok(rateInfo, rem);
rateInfo = nla_next(rateInfo, &(rem)))
{
struct nlattr *tb2[ QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX+ 1];
pRateStats = (wifi_rate_stat *) ((u8 *)stats->rate_stats + (i++ * sizeof(wifi_rate_stat)));
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX, (struct nlattr *) nla_data(rateInfo), nla_len(rateInfo), NULL);
ret = get_wifi_rate_stat(pRateStats, tb2);
if(ret != WIFI_SUCCESS)
{
return ret;
}
}
return WIFI_SUCCESS;
}
wifi_error LLStatsCommand::get_wifi_iface_stats(wifi_iface_stat *stats,
struct nlattr **tb_vendor)
{
struct nlattr *wmmInfo;
wifi_wmm_ac_stat *pWmmStats;
int i=0, rem;
wifi_error ret = WIFI_SUCCESS;
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX"
"not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->beacon_rx = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET])
{
stats->average_tsf_offset = 0;
} else {
stats->average_tsf_offset = nla_get_u64(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET]);
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED])
{
stats->leaky_ap_detected = 0;
} else {
stats->leaky_ap_detected = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED]);
}
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED])
{
stats->leaky_ap_avg_num_frames_leaked = 0;
} else {
stats->leaky_ap_avg_num_frames_leaked = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED]);
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME])
{
stats->leaky_ap_guard_time = 0;
} else {
stats->leaky_ap_guard_time = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME]);
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mgmt_rx = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX]);
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX])
{
ALOGE("%s: "
"QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mgmt_action_rx = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX]);
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX])
{
ALOGE("%s: "
"QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->mgmt_action_tx = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rssi_mgmt = get_s32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rssi_data = get_s32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rssi_ack = get_s32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK]);
#ifdef QC_HAL_DEBUG
ALOGV("WMM STATS");
ALOGV("beaconRx : %u "
"mgmtRx : %u "
"mgmtActionRx : %u "
"mgmtActionTx : %u "
"rssiMgmt : %d "
"rssiData : %d "
"rssiAck : %d ",
stats->beacon_rx,
stats->mgmt_rx,
stats->mgmt_action_rx,
stats->mgmt_action_tx,
stats->rssi_mgmt,
stats->rssi_data,
stats->rssi_ack);
#endif
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
#ifdef QC_HAL_DEBUG
ALOGV("%4s | %6s | %6s | %7s | %7s | %7s |"
" %7s | %8s | %7s | %12s |"
" %11s | %17s | %17s |"
" %17s | %20s",
"ac","txMpdu", "rxMpdu", "txMcast", "rxMcast", "rxAmpdu",
"txAmpdu", "mpduLost", "retries", "retriesShort",
"retriesLong", "contentionTimeMin", "contentionTimeMax",
"contentionTimeAvg", "contentionNumSamples");
#endif
for (wmmInfo = (struct nlattr *) nla_data(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO]),
rem = nla_len(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO]);
nla_ok(wmmInfo, rem);
wmmInfo = nla_next(wmmInfo, &(rem)))
{
struct nlattr *tb2[ QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX+ 1];
pWmmStats = (wifi_wmm_ac_stat *) ((u8 *)stats->ac
+ (i++ * sizeof(wifi_wmm_ac_stat)));
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX,
(struct nlattr *) nla_data(wmmInfo),
nla_len(wmmInfo), NULL);
ret = get_wifi_wmm_ac_stat(pWmmStats, tb2);
if(ret != WIFI_SUCCESS)
{
return ret;
}
}
return WIFI_SUCCESS;
}
static wifi_error get_wifi_radio_stats(wifi_radio_stat *stats,
struct nlattr **tb_vendor)
{
u32 i = 0;
struct nlattr *chInfo;
wifi_channel_stat *pChStats;
int rem;
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->radio = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_time = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME]);
if (stats->num_tx_levels) {
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL]) {
ALOGE("%s: num_tx_levels is %u but QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL not found", __func__, stats->num_tx_levels);
stats->num_tx_levels = 0;
return WIFI_ERROR_INVALID_ARGS;
}
stats->tx_time_per_levels =
(u32 *) malloc(sizeof(u32) * stats->num_tx_levels);
if (!stats->tx_time_per_levels) {
ALOGE("%s: radio_stat: tx_time_per_levels malloc Failed", __func__);
stats->num_tx_levels = 0;
return WIFI_ERROR_OUT_OF_MEMORY;
}
nla_memcpy(stats->tx_time_per_levels,
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL],
sizeof(u32) * stats->num_tx_levels);
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->rx_time = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_scan = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_nbd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_gscan = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_roam_scan = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_pno_scan = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->on_time_hs20 = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
stats->num_channels = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS]);
if (stats->num_channels == 0) {
return WIFI_SUCCESS;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
for (chInfo = (struct nlattr *) nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO]), rem = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO]);
nla_ok(chInfo, rem);
chInfo = nla_next(chInfo, &(rem)))
{
struct nlattr *tb2[ QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX+ 1];
pChStats = (wifi_channel_stat *) ((u8 *)stats->channels + (i++ * (sizeof(wifi_channel_stat))));
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX, (struct nlattr *) nla_data(chInfo), nla_len(chInfo), NULL);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->channel.width = (wifi_channel_width)nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->channel.center_freq = (wifi_channel)nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->channel.center_freq0 = (wifi_channel)nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->channel.center_freq1 = (wifi_channel)nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->on_time = nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
pChStats->cca_busy_time = nla_get_u32(tb2[QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME]);
}
return WIFI_SUCCESS;
}
void LLStatsCommand::getClearRspParams(u32 *stats_clear_rsp_mask, u8 *stop_rsp)
{
*stats_clear_rsp_mask = mClearRspParams.stats_clear_rsp_mask;
*stop_rsp = mClearRspParams.stop_rsp;
}
int LLStatsCommand::requestResponse()
{
return WifiCommand::requestResponse(mMsg);
}
wifi_error LLStatsCommand::notifyResponse()
{
wifi_error ret = WIFI_SUCCESS;
/* Indicate stats to framework only if both radio and iface stats
* are present */
if (mResultsParams.radio_stat && mResultsParams.iface_stat) {
mHandler.on_link_stats_results(mRequestId,
mResultsParams.iface_stat, mNumRadios,
mResultsParams.radio_stat);
} else {
ret = WIFI_ERROR_INVALID_ARGS;
}
clearStats();
return ret;
}
void LLStatsCommand::clearStats()
{
if(mResultsParams.radio_stat)
{
if (mResultsParams.radio_stat->tx_time_per_levels)
{
free(mResultsParams.radio_stat->tx_time_per_levels);
mResultsParams.radio_stat->tx_time_per_levels = NULL;
}
free(mResultsParams.radio_stat);
mResultsParams.radio_stat = NULL;
mRadioStatsSize = 0;
mNumRadios = 0;
}
if(mResultsParams.iface_stat)
{
free(mResultsParams.iface_stat);
mResultsParams.iface_stat = NULL;
}
}
int LLStatsCommand::handleResponse(WifiEvent &reply)
{
unsigned i=0;
int status = WIFI_ERROR_NONE;
WifiVendorCommand::handleResponse(reply);
// Parse the vendordata and get the attribute
switch(mSubcmd)
{
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET:
{
u32 resultsBufSize = 0;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX + 1];
int rem;
wifi_radio_stat *radioStatsBuf;
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX,
(struct nlattr *)mVendorData,
mDataLen, NULL);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE not found",
__FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
switch(nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE]))
{
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO:
{
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS"
" not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
mNumRadios = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS]);
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS
])
{
ALOGE("%s:"
"QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
resultsBufSize += (nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS])
* sizeof(wifi_channel_stat)
+ sizeof(wifi_radio_stat));
radioStatsBuf = (wifi_radio_stat *)realloc(
mResultsParams.radio_stat,
mRadioStatsSize + resultsBufSize);
if (!radioStatsBuf)
{
ALOGE("%s: radio_stat: malloc Failed", __FUNCTION__);
status = WIFI_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
mResultsParams.radio_stat = radioStatsBuf;
//Move the buffer to populate current radio stats
radioStatsBuf = (wifi_radio_stat *)(
(u8 *)mResultsParams.radio_stat
+ mRadioStatsSize);
memset(radioStatsBuf, 0, resultsBufSize);
mRadioStatsSize += resultsBufSize;
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS])
radioStatsBuf->num_tx_levels = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS]);
wifi_channel_stat *pWifiChannelStats;
status = get_wifi_radio_stats(radioStatsBuf,
tb_vendor);
if(status != WIFI_SUCCESS)
{
goto cleanup;
}
#ifdef QC_HAL_DEBUG
ALOGV("radio :%u onTime :%u txTime :%u rxTime :%u"
" onTimeScan :%u onTimeNbd :%u onTimeGscan :%u"
" onTimeRoamScan :%u onTimePnoScan :%u"
" onTimeHs20 :%u numChannels :%u num_tx_levels: %u",
radioStatsBuf->radio,
radioStatsBuf->on_time,
radioStatsBuf->tx_time,
radioStatsBuf->rx_time,
radioStatsBuf->on_time_scan,
radioStatsBuf->on_time_nbd,
radioStatsBuf->on_time_gscan,
radioStatsBuf->on_time_roam_scan,
radioStatsBuf->on_time_pno_scan,
radioStatsBuf->on_time_hs20,
radioStatsBuf->num_channels,
radioStatsBuf->num_tx_levels);
#ifdef QC_HAL_DEBUG
for (i = 0; i < radioStatsBuf->num_tx_levels; i++) {
ALOGV("Power level: %u tx_time: %u", i,
radioStatsBuf->tx_time_per_levels[i]);
}
#endif
ALOGV("%5s | %10s | %11s | %11s | %6s | %11s", "width",
"CenterFreq", "CenterFreq0", "CenterFreq1",
"onTime", "ccaBusyTime");
#endif
for ( i=0; i < radioStatsBuf->num_channels; i++)
{
pWifiChannelStats =
(wifi_channel_stat *) (
(u8 *)radioStatsBuf->channels
+ (i * sizeof(wifi_channel_stat)));
#ifdef QC_HAL_DEBUG
ALOGV("%5u | %10u | %11u | %11u | %6u | %11u",
pWifiChannelStats->channel.width,
pWifiChannelStats->channel.center_freq,
pWifiChannelStats->channel.center_freq0,
pWifiChannelStats->channel.center_freq1,
pWifiChannelStats->on_time,
pWifiChannelStats->cca_busy_time);
#endif
}
}
break;
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE:
{
resultsBufSize = sizeof(wifi_iface_stat);
mResultsParams.iface_stat =
(wifi_iface_stat *) malloc (resultsBufSize);
if (!mResultsParams.iface_stat)
{
ALOGE("%s: iface_stat: malloc Failed", __FUNCTION__);
status = WIFI_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
memset(mResultsParams.iface_stat, 0, resultsBufSize);
status = get_wifi_interface_info(
&mResultsParams.iface_stat->info, tb_vendor);
if(status != WIFI_SUCCESS)
{
goto cleanup;
}
status = get_wifi_iface_stats(mResultsParams.iface_stat,
tb_vendor);
if(status != WIFI_SUCCESS)
{
goto cleanup;
}
/* Driver/firmware might send this attribute when there
* are no peers connected.
* So that, the event
* QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS can be
* avoided.
*/
if (tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS])
{
mResultsParams.iface_stat->num_peers =
nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS]);
#ifdef QC_HAL_DEBUG
ALOGV("%s: numPeers is %u\n", __FUNCTION__,
mResultsParams.iface_stat->num_peers);
#endif
}
}
break;
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS:
{
struct nlattr *peerInfo;
wifi_iface_stat *pIfaceStat = NULL;
u32 numPeers, num_rates = 0;
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS])
{
ALOGE("%s:QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
#ifdef QC_HAL_DEBUG
ALOGV(" numPeers is %u in %s\n",
nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS]),
__FUNCTION__);
#endif
if((numPeers = nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS])) > 0)
{
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
for (peerInfo = (struct nlattr *) nla_data(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO]),
rem = nla_len(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO]);
nla_ok(peerInfo, rem);
peerInfo = nla_next(peerInfo, &(rem)))
{
struct nlattr *tb2[
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX+ 1];
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX,
(struct nlattr *) nla_data(peerInfo),
nla_len(peerInfo), NULL);
if (!tb2[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES])
{
ALOGE("%s:"
"QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
num_rates += nla_get_u32(tb2[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES]);
}
resultsBufSize += (numPeers * sizeof(wifi_peer_info)
+ num_rates * sizeof(wifi_rate_stat)
+ sizeof (wifi_iface_stat));
pIfaceStat = (wifi_iface_stat *) malloc (
resultsBufSize);
if (!pIfaceStat)
{
ALOGE("%s: pIfaceStat: malloc Failed", __FUNCTION__);
status = WIFI_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
memset(pIfaceStat, 0, resultsBufSize);
if(mResultsParams.iface_stat) {
memcpy ( pIfaceStat, mResultsParams.iface_stat,
sizeof(wifi_iface_stat));
free (mResultsParams.iface_stat);
mResultsParams.iface_stat = pIfaceStat;
}
wifi_peer_info *pPeerStats;
pIfaceStat->num_peers = numPeers;
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
for (peerInfo = (struct nlattr *) nla_data(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO]),
rem = nla_len(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO]);
nla_ok(peerInfo, rem);
peerInfo = nla_next(peerInfo, &(rem)))
{
struct nlattr *tb2[
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX+ 1];
pPeerStats = (wifi_peer_info *) (
(u8 *)pIfaceStat->peer_info
+ (i++ * sizeof(wifi_peer_info)));
nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX,
(struct nlattr *) nla_data(peerInfo),
nla_len(peerInfo), NULL);
status = get_wifi_peer_info(pPeerStats, tb2);
if(status != WIFI_SUCCESS)
{
goto cleanup;
}
}
}
}
break;
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID:
default:
//error case should not happen print log
ALOGE("%s: Wrong LLStats subcmd received %d", __FUNCTION__,
mSubcmd);
}
}
break;
case QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR:
{
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1];
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX,
(struct nlattr *)mVendorData,
mDataLen, NULL);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
ALOGI("Resp mask : %d\n", nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK]));
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
ALOGI("STOP resp : %d\n", nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP]));
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
mClearRspParams.stats_clear_rsp_mask = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK]);
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP])
{
ALOGE("%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP not found", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
mClearRspParams.stop_rsp = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP]);
break;
}
default :
ALOGE("%s: Wrong LLStats subcmd received %d", __FUNCTION__, mSubcmd);
}
return NL_SKIP;
cleanup:
clearStats();
return status;
}
//Implementation of the functions exposed in linklayer.h
wifi_error wifi_set_link_stats(wifi_interface_handle iface,
wifi_link_layer_params params)
{
int ret = 0;
LLStatsCommand *LLCommand;
struct nlattr *nl_data;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
ALOGI("mpdu_size_threshold : %u, aggressive_statistics_gathering : %u",
params.mpdu_size_threshold, params.aggressive_statistics_gathering);
LLCommand = LLStatsCommand::instance(handle);
if (LLCommand == NULL) {
ALOGE("%s: Error LLStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_UNKNOWN;
}
LLCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET);
/* create the message */
ret = LLCommand->create();
if (ret < 0)
goto cleanup;
ret = LLCommand->set_iface_id(iinfo->name);
if (ret < 0)
goto cleanup;
/*add the attributes*/
nl_data = LLCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data)
goto cleanup;
/**/
ret = LLCommand->put_u32(QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD,
params.mpdu_size_threshold);
if (ret < 0)
goto cleanup;
/**/
ret = LLCommand->put_u32(
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING,
params.aggressive_statistics_gathering);
if (ret < 0)
goto cleanup;
LLCommand->attr_end(nl_data);
ret = LLCommand->requestResponse();
if (ret != 0) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
}
cleanup:
return (wifi_error)ret;
}
//Implementation of the functions exposed in LLStats.h
wifi_error wifi_get_link_stats(wifi_request_id id,
wifi_interface_handle iface,
wifi_stats_result_handler handler)
{
int ret = 0;
LLStatsCommand *LLCommand;
struct nlattr *nl_data;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
LLCommand = LLStatsCommand::instance(handle);
if (LLCommand == NULL) {
ALOGE("%s: Error LLStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_UNKNOWN;
}
LLCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET);
LLCommand->initGetContext(id);
LLCommand->setHandler(handler);
/* create the message */
ret = LLCommand->create();
if (ret < 0)
goto cleanup;
ret = LLCommand->set_iface_id(iinfo->name);
if (ret < 0)
goto cleanup;
/*add the attributes*/
nl_data = LLCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data)
goto cleanup;
ret = LLCommand->put_u32(QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID,
id);
if (ret < 0)
goto cleanup;
ret = LLCommand->put_u32(QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK,
7);
if (ret < 0)
goto cleanup;
/**/
LLCommand->attr_end(nl_data);
ret = LLCommand->requestResponse();
if (ret != 0) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
}
if (ret < 0) {
LLCommand->clearStats();
goto cleanup;
}
if (ret == 0) {
ret = LLCommand->notifyResponse();
}
cleanup:
return (wifi_error)ret;
}
//Implementation of the functions exposed in LLStats.h
wifi_error wifi_clear_link_stats(wifi_interface_handle iface,
u32 stats_clear_req_mask,
u32 *stats_clear_rsp_mask,
u8 stop_req, u8 *stop_rsp)
{
int ret = 0;
LLStatsCommand *LLCommand;
struct nlattr *nl_data;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
ALOGI("clear_req : %x, stop_req : %u", stats_clear_req_mask, stop_req);
LLCommand = LLStatsCommand::instance(handle);
if (LLCommand == NULL) {
ALOGE("%s: Error LLStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_UNKNOWN;
}
LLCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR);
/* create the message */
ret = LLCommand->create();
if (ret < 0)
goto cleanup;
ret = LLCommand->set_iface_id(iinfo->name);
if (ret < 0)
goto cleanup;
/*add the attributes*/
nl_data = LLCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data)
goto cleanup;
/**/
ret = LLCommand->put_u32(QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK,
stats_clear_req_mask);
if (ret < 0)
goto cleanup;
/**/
ret = LLCommand->put_u8(QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ,
stop_req);
if (ret < 0)
goto cleanup;
LLCommand->attr_end(nl_data);
ret = LLCommand->requestResponse();
if (ret != 0) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
}
LLCommand->getClearRspParams(stats_clear_rsp_mask, stop_rsp);
cleanup:
delete LLCommand;
return (wifi_error)ret;
}