blob: a08f133213a28abe91bbf8a861163135cb39fad1 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC : wlan_hdd_stats.c
*
* WLAN Host Device Driver statistics related implementation
*
*/
#include "wlan_hdd_stats.h"
#include "sme_api.h"
#include "cds_sched.h"
#include "wlan_hdd_trace.h"
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
/**
* struct hdd_ll_stats_context - hdd link layer stats context
*
* @request_id: userspace-assigned link layer stats request id
* @request_bitmap: userspace-assigned link layer stats request bitmap
* @response_event: LL stats request wait event
*/
struct hdd_ll_stats_context {
uint32_t request_id;
uint32_t request_bitmap;
struct completion response_event;
spinlock_t context_lock;
};
static struct hdd_ll_stats_context ll_stats_context;
#endif /* End of WLAN_FEATURE_LINK_LAYER_STATS */
/* 11B, 11G Rate table include Basic rate and Extended rate
* The IDX field is the rate index
* The HI field is the rate when RSSI is strong or being ignored
* (in this case we report actual rate)
* The MID field is the rate when RSSI is moderate
* (in this case we cap 11b rates at 5.5 and 11g rates at 24)
* The LO field is the rate when RSSI is low
* (in this case we don't report rates, actual current rate used)
*/
static const struct {
uint8_t beacon_rate_index;
uint16_t supported_rate[4];
} supported_data_rate[] = {
/* IDX HI HM LM LO (RSSI-based index */
{
2, {
10, 10, 10, 0
}
}, {
4, {
20, 20, 10, 0
}
}, {
11, {
55, 20, 10, 0
}
}, {
12, {
60, 55, 20, 0
}
}, {
18, {
90, 55, 20, 0
}
}, {
22, {
110, 55, 20, 0
}
}, {
24, {
120, 90, 60, 0
}
}, {
36, {
180, 120, 60, 0
}
}, {
44, {
220, 180, 60, 0
}
}, {
48, {
240, 180, 90, 0
}
}, {
66, {
330, 180, 90, 0
}
}, {
72, {
360, 240, 90, 0
}
}, {
96, {
480, 240, 120, 0
}
}, {
108, {
540, 240, 120, 0
}
}
};
/* MCS Based rate table HT MCS parameters with Nss = 1 */
static struct index_data_rate_type supported_mcs_rate_nss1[] = {
/* MCS L20 L40 S20 S40 */
{0, {65, 135, 72, 150} },
{1, {130, 270, 144, 300} },
{2, {195, 405, 217, 450} },
{3, {260, 540, 289, 600} },
{4, {390, 810, 433, 900} },
{5, {520, 1080, 578, 1200} },
{6, {585, 1215, 650, 1350} },
{7, {650, 1350, 722, 1500} }
};
/* HT MCS parameters with Nss = 2 */
static struct index_data_rate_type supported_mcs_rate_nss2[] = {
/* MCS L20 L40 S20 S40 */
{0, {130, 270, 144, 300} },
{1, {260, 540, 289, 600} },
{2, {390, 810, 433, 900} },
{3, {520, 1080, 578, 1200} },
{4, {780, 1620, 867, 1800} },
{5, {1040, 2160, 1156, 2400} },
{6, {1170, 2430, 1300, 2700} },
{7, {1300, 2700, 1444, 3000} }
};
#ifdef WLAN_FEATURE_11AC
/* MCS Based VHT rate table MCS parameters with Nss = 1*/
static struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = {
/* MCS L80 S80 L40 S40 L20 S40*/
{0, {293, 325}, {135, 150}, {65, 72} },
{1, {585, 650}, {270, 300}, {130, 144} },
{2, {878, 975}, {405, 450}, {195, 217} },
{3, {1170, 1300}, {540, 600}, {260, 289} },
{4, {1755, 1950}, {810, 900}, {390, 433} },
{5, {2340, 2600}, {1080, 1200}, {520, 578} },
{6, {2633, 2925}, {1215, 1350}, {585, 650} },
{7, {2925, 3250}, {1350, 1500}, {650, 722} },
{8, {3510, 3900}, {1620, 1800}, {780, 867} },
{9, {3900, 4333}, {1800, 2000}, {780, 867} }
};
/*MCS parameters with Nss = 2*/
static struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = {
/* MCS L80 S80 L40 S40 L20 S40*/
{0, {585, 650}, {270, 300}, {130, 144} },
{1, {1170, 1300}, {540, 600}, {260, 289} },
{2, {1755, 1950}, {810, 900}, {390, 433} },
{3, {2340, 2600}, {1080, 1200}, {520, 578} },
{4, {3510, 3900}, {1620, 1800}, {780, 867} },
{5, {4680, 5200}, {2160, 2400}, {1040, 1156} },
{6, {5265, 5850}, {2430, 2700}, {1170, 1300} },
{7, {5850, 6500}, {2700, 3000}, {1300, 1444} },
{8, {7020, 7800}, {3240, 3600}, {1560, 1733} },
{9, {7800, 8667}, {3600, 4000}, {1560, 1733} }
};
#endif /* End of WLAN_FEATURE_11AC */
/*array index ponints to MCS and array value points respective rssi*/
static int rssi_mcs_tbl[][10] = {
/*MCS 0 1 2 3 4 5 6 7 8 9*/
{-82, -79, -77, -74, -70, -66, -65, -64, -59, -57}, /* 20 */
{-79, -76, -74, -71, -67, -63, -62, -61, -56, -54}, /* 40 */
{-76, -73, -71, -68, -64, -60, -59, -58, -53, -51} /* 80 */
};
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
/**
* put_wifi_rate_stat() - put wifi rate stats
* @stats: Pointer to stats context
* @vendor_event: Pointer to vendor event
*
* Return: bool
*/
static bool put_wifi_rate_stat(tpSirWifiRateStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE,
stats->rate.preamble) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS,
stats->rate.nss) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW,
stats->rate.bw) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX,
stats->rate.rateMcsIdx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE,
stats->rate.bitrate) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU,
stats->txMpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU,
stats->rxMpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST,
stats->mpduLost) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES,
stats->retries) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT,
stats->retriesShort) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG,
stats->retriesLong)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
return true;
}
/**
* put_wifi_peer_info() - put wifi peer info
* @stats: Pointer to stats context
* @vendor_event: Pointer to vendor event
*
* Return: bool
*/
static bool put_wifi_peer_info(tpSirWifiPeerInfo stats,
struct sk_buff *vendor_event)
{
u32 i = 0;
tpSirWifiRateStat pRateStats;
if (nla_put_u32
(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE,
stats->type) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS,
CDF_MAC_ADDR_SIZE, &stats->peerMacAddress.bytes[0]) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES,
stats->capabilities) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES,
stats->numRate)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
goto error;
}
if (stats->numRate) {
struct nlattr *rateInfo;
struct nlattr *rates;
rateInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO);
if (rateInfo == NULL)
goto error;
for (i = 0; i < stats->numRate; i++) {
pRateStats = (tpSirWifiRateStat) ((uint8_t *)
stats->rateStats +
(i *
sizeof
(tSirWifiRateStat)));
rates = nla_nest_start(vendor_event, i);
if (rates == NULL)
goto error;
if (false ==
put_wifi_rate_stat(pRateStats, vendor_event)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
nla_nest_end(vendor_event, rates);
}
nla_nest_end(vendor_event, rateInfo);
}
return true;
error:
return false;
}
/**
* put_wifi_wmm_ac_stat() - put wifi wmm ac stats
* @stats: Pointer to stats context
* @vendor_event: Pointer to vendor event
*
* Return: bool
*/
static bool put_wifi_wmm_ac_stat(tpSirWifiWmmAcStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC,
stats->ac) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU,
stats->txMpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU,
stats->rxMpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST,
stats->txMcast) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST,
stats->rxMcast) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU,
stats->rxAmpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU,
stats->txAmpdu) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST,
stats->mpduLost) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES,
stats->retries) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT,
stats->retriesShort) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG,
stats->retriesLong) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN,
stats->contentionTimeMin) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX,
stats->contentionTimeMax) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG,
stats->contentionTimeAvg) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES,
stats->contentionNumSamples)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
return true;
}
/**
* put_wifi_interface_info() - put wifi interface info
* @stats: Pointer to stats context
* @vendor_event: Pointer to vendor event
*
* Return: bool
*/
static bool put_wifi_interface_info(tpSirWifiInterfaceInfo stats,
struct sk_buff *vendor_event)
{
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE,
stats->mode) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR,
CDF_MAC_ADDR_SIZE, stats->macAddr.bytes) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE,
stats->state) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING,
stats->roaming) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES,
stats->capabilities) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID,
strlen(stats->ssid), stats->ssid) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID,
CDF_MAC_ADDR_SIZE, stats->bssid.bytes) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->apCountryStr) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->countryStr)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
return true;
}
/**
* put_wifi_iface_stats() - put wifi interface stats
* @pWifiIfaceStat: Pointer to interface stats context
* @num_peer: Number of peers
* @vendor_event: Pointer to vendor event
*
* Return: bool
*/
static bool put_wifi_iface_stats(tpSirWifiIfaceStat pWifiIfaceStat,
u32 num_peers, struct sk_buff *vendor_event)
{
int i = 0;
struct nlattr *wmmInfo;
struct nlattr *wmmStats;
u64 average_tsf_offset;
if (false == put_wifi_interface_info(&pWifiIfaceStat->info,
vendor_event)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
average_tsf_offset = pWifiIfaceStat->avg_bcn_spread_offset_high;
average_tsf_offset = (average_tsf_offset << 32) |
pWifiIfaceStat->avg_bcn_spread_offset_low ;
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_IFACE) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
num_peers) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX,
pWifiIfaceStat->beaconRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX,
pWifiIfaceStat->mgmtRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX,
pWifiIfaceStat->mgmtActionRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX,
pWifiIfaceStat->mgmtActionTx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT,
pWifiIfaceStat->rssiMgmt) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA,
pWifiIfaceStat->rssiData) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK,
pWifiIfaceStat->rssiAck) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED,
pWifiIfaceStat->is_leaky_ap) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED,
pWifiIfaceStat->avg_rx_frms_leaked) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME,
pWifiIfaceStat->rx_leak_window) ||
nla_put_u64(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET,
average_tsf_offset)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return false;
}
wmmInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO);
if (wmmInfo == NULL)
return false;
for (i = 0; i < WIFI_AC_MAX; i++) {
wmmStats = nla_nest_start(vendor_event, i);
if (wmmStats == NULL)
return false;
if (false ==
put_wifi_wmm_ac_stat(&pWifiIfaceStat->AccessclassStats[i],
vendor_event)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("put_wifi_wmm_ac_stat Fail"));
return false;
}
nla_nest_end(vendor_event, wmmStats);
}
nla_nest_end(vendor_event, wmmInfo);
return true;
}
/**
* hdd_map_device_to_ll_iface_mode() - map device to link layer interface mode
* @deviceMode: Device mode
*
* Return: interface mode
*/
static tSirWifiInterfaceMode hdd_map_device_to_ll_iface_mode(int deviceMode)
{
switch (deviceMode) {
case WLAN_HDD_INFRA_STATION:
return WIFI_INTERFACE_STA;
case WLAN_HDD_SOFTAP:
return WIFI_INTERFACE_SOFTAP;
case WLAN_HDD_P2P_CLIENT:
return WIFI_INTERFACE_P2P_CLIENT;
case WLAN_HDD_P2P_GO:
return WIFI_INTERFACE_P2P_GO;
case WLAN_HDD_IBSS:
return WIFI_INTERFACE_IBSS;
default:
/* Return Interface Mode as STA for all the unsupported modes */
return WIFI_INTERFACE_STA;
}
}
/**
* hdd_get_interface_info() - get interface info
* @pAdapter: Pointer to device adapter
* @pInfo: Pointer to interface info
*
* Return: bool
*/
static bool hdd_get_interface_info(hdd_adapter_t *pAdapter,
tpSirWifiInterfaceInfo pInfo)
{
uint8_t *staMac = NULL;
hdd_station_ctx_t *pHddStaCtx;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
pInfo->mode = hdd_map_device_to_ll_iface_mode(pAdapter->device_mode);
cdf_copy_macaddr(&pInfo->macAddr, &pAdapter->macAddressCurrent);
if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode))) {
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_NotConnected ==
pHddStaCtx->conn_info.connState) {
pInfo->state = WIFI_DISCONNECTED;
}
if (eConnectionState_Connecting ==
pHddStaCtx->conn_info.connState) {
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s: Session ID %d, Connection is in progress",
__func__, pAdapter->sessionId);
pInfo->state = WIFI_ASSOCIATING;
}
if ((eConnectionState_Associated ==
pHddStaCtx->conn_info.connState)
&& (false == pHddStaCtx->conn_info.uIsAuthenticated)) {
staMac =
(uint8_t *) &(pAdapter->macAddressCurrent.
bytes[0]);
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s: client " MAC_ADDRESS_STR
" is in the middle of WPS/EAPOL exchange.",
__func__, MAC_ADDR_ARRAY(staMac));
pInfo->state = WIFI_AUTHENTICATING;
}
if (eConnectionState_Associated ==
pHddStaCtx->conn_info.connState) {
pInfo->state = WIFI_ASSOCIATED;
cdf_copy_macaddr(&pInfo->bssid,
&pHddStaCtx->conn_info.bssId);
cdf_mem_copy(pInfo->ssid,
pHddStaCtx->conn_info.SSID.SSID.ssId,
pHddStaCtx->conn_info.SSID.SSID.length);
/*
* NULL Terminate the string
*/
pInfo->ssid[pHddStaCtx->conn_info.SSID.SSID.length] = 0;
}
}
cdf_mem_copy(pInfo->countryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
cdf_mem_copy(pInfo->apCountryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
return true;
}
/**
* hdd_link_layer_process_peer_stats() - This function is called after
* @pAdapter: Pointer to device adapter
* @more_data: More data
* @pData: Pointer to stats data
*
* Receiving Link Layer Peer statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*
* Return: None
*/
static void hdd_link_layer_process_peer_stats(hdd_adapter_t *pAdapter,
u32 more_data,
tpSirWifiPeerStat pData)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tpSirWifiRateStat pWifiRateStat;
tpSirWifiPeerStat pWifiPeerStat;
tpSirWifiPeerInfo pWifiPeerInfo;
struct sk_buff *vendor_event;
int status, i, j;
struct nlattr *peers;
int numRate;
pWifiPeerStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return;
}
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_PEER_ALL : numPeers %u, more data = %u",
pWifiPeerStat->numPeers, more_data);
for (i = 0; i < pWifiPeerStat->numPeers; i++) {
pWifiPeerInfo = (tpSirWifiPeerInfo)
((uint8_t *) pWifiPeerStat->peerInfo +
(i * sizeof(tSirWifiPeerInfo)));
if (WLAN_HDD_INFRA_STATION == pAdapter->device_mode)
pWifiPeerInfo->type = WIFI_PEER_AP;
if (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode)
pWifiPeerInfo->type = WIFI_PEER_P2P_GO;
hddLog(CDF_TRACE_LEVEL_INFO,
" %d) LL_STATS Channel Stats "
" Peer Type %u "
" peerMacAddress %pM "
" capabilities 0x%x "
" numRate %u ",
i,
pWifiPeerInfo->type,
pWifiPeerInfo->peerMacAddress.bytes,
pWifiPeerInfo->capabilities, pWifiPeerInfo->numRate);
{
for (j = 0; j < pWifiPeerInfo->numRate; j++) {
pWifiRateStat = (tpSirWifiRateStat)
((uint8_t *) pWifiPeerInfo->rateStats +
(j * sizeof(tSirWifiRateStat)));
hddLog(CDF_TRACE_LEVEL_INFO,
" peer Rate Stats "
" preamble %u "
" nss %u "
" bw %u "
" rateMcsIdx %u "
" reserved %u "
" bitrate %u "
" txMpdu %u "
" rxMpdu %u "
" mpduLost %u "
" retries %u "
" retriesShort %u "
" retriesLong %u",
pWifiRateStat->rate.preamble,
pWifiRateStat->rate.nss,
pWifiRateStat->rate.bw,
pWifiRateStat->rate.rateMcsIdx,
pWifiRateStat->rate.reserved,
pWifiRateStat->rate.bitrate,
pWifiRateStat->txMpdu,
pWifiRateStat->rxMpdu,
pWifiRateStat->mpduLost,
pWifiRateStat->retries,
pWifiRateStat->retriesShort,
pWifiRateStat->retriesLong);
}
}
}
/*
* Allocate a size of 4096 for the peer stats comprising
* each of size = sizeof (tSirWifiPeerInfo) + numRate *
* sizeof (tSirWifiRateStat).Each field is put with an
* NL attribute.The size of 4096 is considered assuming
* that number of rates shall not exceed beyond 50 with
* the sizeof (tSirWifiRateStat) being 32.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event) {
hddLog(LOGE,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_PEER) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
more_data) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
pWifiPeerStat->numPeers)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR put fail", __func__);
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8_t *)
pWifiPeerStat->peerInfo);
if (pWifiPeerStat->numPeers) {
struct nlattr *peerInfo;
peerInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO);
if (peerInfo == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
for (i = 1; i <= pWifiPeerStat->numPeers; i++) {
peers = nla_nest_start(vendor_event, i);
if (peers == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
numRate = pWifiPeerInfo->numRate;
if (false ==
put_wifi_peer_info(pWifiPeerInfo, vendor_event)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("put_wifi_peer_info fail"));
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8_t *)
pWifiPeerStat->
peerInfo +
(i *
sizeof
(tSirWifiPeerInfo))
+
(numRate *
sizeof
(tSirWifiRateStat)));
nla_nest_end(vendor_event, peers);
}
nla_nest_end(vendor_event, peerInfo);
}
cfg80211_vendor_cmd_reply(vendor_event);
return;
}
/**
* hdd_link_layer_process_iface_stats() - This function is called after
* @pAdapter: Pointer to device adapter
* @pData: Pointer to stats data
* @num_peers: Number of peers
*
* Receiving Link Layer Interface statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*
* Return: None
*/
static void hdd_link_layer_process_iface_stats(hdd_adapter_t *pAdapter,
tpSirWifiIfaceStat pData,
u32 num_peers)
{
tpSirWifiIfaceStat pWifiIfaceStat;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status;
int i;
pWifiIfaceStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return;
}
/*
* Allocate a size of 4096 for the interface stats comprising
* sizeof (tpSirWifiIfaceStat).The size of 4096 is considered
* assuming that all these fit with in the limit.Please take
* a call on the limit based on the data requirements on
* interface statistics.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return;
}
hddLog(CDF_TRACE_LEVEL_INFO, "WMI_LINK_STATS_IFACE Data");
if (false == hdd_get_interface_info(pAdapter, &pWifiIfaceStat->info)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("hdd_get_interface_info get fail"));
kfree_skb(vendor_event);
return;
}
hddLog(CDF_TRACE_LEVEL_INFO,
" Num peers %u "
"LL_STATS_IFACE: "
" Mode %u "
" MAC %pM "
" State %u "
" Roaming %u "
" capabilities 0x%x "
" SSID %s "
" BSSID %pM",
num_peers,
pWifiIfaceStat->info.mode,
pWifiIfaceStat->info.macAddr.bytes,
pWifiIfaceStat->info.state,
pWifiIfaceStat->info.roaming,
pWifiIfaceStat->info.capabilities,
pWifiIfaceStat->info.ssid, pWifiIfaceStat->info.bssid.bytes);
hddLog(CDF_TRACE_LEVEL_INFO,
" AP country str: %c%c%c",
pWifiIfaceStat->info.apCountryStr[0],
pWifiIfaceStat->info.apCountryStr[1],
pWifiIfaceStat->info.apCountryStr[2]);
hddLog(CDF_TRACE_LEVEL_INFO,
" Country Str Association: %c%c%c",
pWifiIfaceStat->info.countryStr[0],
pWifiIfaceStat->info.countryStr[1],
pWifiIfaceStat->info.countryStr[2]);
hddLog(CDF_TRACE_LEVEL_INFO,
" beaconRx %u "
" mgmtRx %u "
" mgmtActionRx %u "
" mgmtActionTx %u "
" rssiMgmt %u "
" rssiData %u "
" rssiAck %u "
" avg_bcn_spread_offset_high %u"
" avg_bcn_spread_offset_low %u"
" is leaky_ap %u"
" avg_rx_frms_leaked %u"
" rx_leak_window %u",
pWifiIfaceStat->beaconRx,
pWifiIfaceStat->mgmtRx,
pWifiIfaceStat->mgmtActionRx,
pWifiIfaceStat->mgmtActionTx,
pWifiIfaceStat->rssiMgmt,
pWifiIfaceStat->rssiData,
pWifiIfaceStat->rssiAck,
pWifiIfaceStat->avg_bcn_spread_offset_high,
pWifiIfaceStat->avg_bcn_spread_offset_low,
pWifiIfaceStat->is_leaky_ap,
pWifiIfaceStat->avg_rx_frms_leaked,
pWifiIfaceStat->rx_leak_window);
for (i = 0; i < WIFI_AC_MAX; i++) {
hddLog(CDF_TRACE_LEVEL_INFO,
" %d) LL_STATS IFACE: "
" ac: %u txMpdu: %u "
" rxMpdu: %u txMcast: %u "
" rxMcast: %u rxAmpdu: %u "
" txAmpdu: %u mpduLost: %u "
" retries: %u retriesShort: %u "
" retriesLong: %u contentionTimeMin: %u "
" contentionTimeMax: %u contentionTimeAvg: %u "
" contentionNumSamples: %u",
i,
pWifiIfaceStat->AccessclassStats[i].ac,
pWifiIfaceStat->AccessclassStats[i].txMpdu,
pWifiIfaceStat->AccessclassStats[i].rxMpdu,
pWifiIfaceStat->AccessclassStats[i].txMcast,
pWifiIfaceStat->AccessclassStats[i].rxMcast,
pWifiIfaceStat->AccessclassStats[i].rxAmpdu,
pWifiIfaceStat->AccessclassStats[i].txAmpdu,
pWifiIfaceStat->AccessclassStats[i].mpduLost,
pWifiIfaceStat->AccessclassStats[i].retries,
pWifiIfaceStat->AccessclassStats[i].retriesShort,
pWifiIfaceStat->AccessclassStats[i].retriesLong,
pWifiIfaceStat->AccessclassStats[i].contentionTimeMin,
pWifiIfaceStat->AccessclassStats[i].contentionTimeMax,
pWifiIfaceStat->AccessclassStats[i].contentionTimeAvg,
pWifiIfaceStat->AccessclassStats[i].
contentionNumSamples);
}
if (false ==
put_wifi_iface_stats(pWifiIfaceStat, num_peers, vendor_event)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("put_wifi_iface_stats fail"));
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_cmd_reply(vendor_event);
return;
}
/**
* hdd_link_layer_process_radio_stats() - This function is called after
* @pAdapter: Pointer to device adapter
* @more_data: More data
* @pData: Pointer to stats data
* @num_radios: Number of radios
*
* Receiving Link Layer Radio statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*
* Return: None
*/
static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter,
u32 more_data,
tpSirWifiRadioStat pData,
u32 num_radio)
{
int status, i;
tpSirWifiRadioStat pWifiRadioStat;
tpSirWifiChannelStats pWifiChannelStats;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
pWifiRadioStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return;
}
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_RADIO"
" number of radios = %u"
" radio is %d onTime is %u"
" txTime is %u rxTime is %u"
" onTimeScan is %u onTimeNbd is %u"
" onTimeGscan is %u onTimeRoamScan is %u"
" onTimePnoScan is %u onTimeHs20 is %u"
" numChannels is %u",
num_radio,
pWifiRadioStat->radio,
pWifiRadioStat->onTime,
pWifiRadioStat->txTime,
pWifiRadioStat->rxTime,
pWifiRadioStat->onTimeScan,
pWifiRadioStat->onTimeNbd,
pWifiRadioStat->onTimeGscan,
pWifiRadioStat->onTimeRoamScan,
pWifiRadioStat->onTimePnoScan,
pWifiRadioStat->onTimeHs20, pWifiRadioStat->numChannels);
/*
* Allocate a size of 4096 for the Radio stats comprising
* sizeof (tSirWifiRadioStat) + numChannels * sizeof
* (tSirWifiChannelStats).Each channel data is put with an
* NL attribute.The size of 4096 is considered assuming that
* number of channels shall not exceed beyond 60 with the
* sizeof (tSirWifiChannelStats) being 24 bytes.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event) {
hddLog(LOGE,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
more_data) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
num_radio) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
pWifiRadioStat->radio) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
pWifiRadioStat->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
pWifiRadioStat->txTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
pWifiRadioStat->rxTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
pWifiRadioStat->onTimeScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
pWifiRadioStat->onTimeNbd) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
pWifiRadioStat->onTimeGscan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
pWifiRadioStat->onTimeRoamScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
pWifiRadioStat->onTimePnoScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
pWifiRadioStat->onTimeHs20) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
pWifiRadioStat->numChannels)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
kfree_skb(vendor_event);
return;
}
if (pWifiRadioStat->numChannels) {
struct nlattr *chList;
struct nlattr *chInfo;
chList = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
if (chList == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
for (i = 0; i < pWifiRadioStat->numChannels; i++) {
pWifiChannelStats = (tpSirWifiChannelStats) ((uint8_t *)
pWifiRadioStat->
channels +
(i *
sizeof
(tSirWifiChannelStats)));
hddLog(CDF_TRACE_LEVEL_INFO,
" %d) Channel Info"
" width is %u "
" CenterFreq %u "
" CenterFreq0 %u "
" CenterFreq1 %u "
" onTime %u "
" ccaBusyTime %u",
i,
pWifiChannelStats->channel.width,
pWifiChannelStats->channel.centerFreq,
pWifiChannelStats->channel.centerFreq0,
pWifiChannelStats->channel.centerFreq1,
pWifiChannelStats->onTime,
pWifiChannelStats->ccaBusyTime);
chInfo = nla_nest_start(vendor_event, i);
if (chInfo == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
pWifiChannelStats->channel.width) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
pWifiChannelStats->channel.centerFreq) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
pWifiChannelStats->channel.
centerFreq0) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
pWifiChannelStats->channel.
centerFreq1) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
pWifiChannelStats->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
pWifiChannelStats->ccaBusyTime)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("nla_put failed"));
kfree_skb(vendor_event);
return;
}
nla_nest_end(vendor_event, chInfo);
}
nla_nest_end(vendor_event, chList);
}
cfg80211_vendor_cmd_reply(vendor_event);
return;
}
/**
* wlan_hdd_cfg80211_link_layer_stats_callback() - This function is called
* @ctx: Pointer to hdd context
* @indType: Indication type
* @pRsp: Pointer to response
*
* After receiving Link Layer indications from FW.This callback converts the
* firmware data to the NL data and send the same to the kernel/upper layers.
*
* Return: None
*/
static void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx,
int indType, void *pRsp)
{
hdd_context_t *pHddCtx = (hdd_context_t *) ctx;
struct hdd_ll_stats_context *context;
hdd_adapter_t *pAdapter = NULL;
tpSirLLStatsResults linkLayerStatsResults = (tpSirLLStatsResults) pRsp;
int status;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return;
}
pAdapter = hdd_get_adapter_by_vdev(pHddCtx,
linkLayerStatsResults->ifaceId);
if (NULL == pAdapter) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: vdev_id %d does not exist with host",
__func__, linkLayerStatsResults->ifaceId);
return;
}
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"%s: Link Layer Indication indType: %d", __func__, indType);
switch (indType) {
case SIR_HAL_LL_STATS_RESULTS_RSP:
{
hddLog(CDF_TRACE_LEVEL_INFO,
FL("RESPONSE SIR_HAL_LL_STATS_RESULTS_RSP"));
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE paramID = 0x%x",
linkLayerStatsResults->paramId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE ifaceId = %u",
linkLayerStatsResults->ifaceId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE respId = %u",
linkLayerStatsResults->rspId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE more data = %u",
linkLayerStatsResults->moreResultToFollow);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE num radio = %u",
linkLayerStatsResults->num_radio);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS RESULTS RESPONSE result = %p",
linkLayerStatsResults->results);
context = &ll_stats_context;
spin_lock(&context->context_lock);
/* validate response received from target */
if ((context->request_id != linkLayerStatsResults->rspId) ||
!(context->request_bitmap & linkLayerStatsResults->paramId)) {
spin_unlock(&context->context_lock);
hddLog(LOGE,
FL("Error : Request id %d response id %d request bitmap 0x%x response bitmap 0x%x"),
context->request_id, linkLayerStatsResults->rspId,
context->request_bitmap, linkLayerStatsResults->paramId);
return;
}
spin_unlock(&context->context_lock);
if (linkLayerStatsResults->
paramId & WMI_LINK_STATS_RADIO) {
hdd_link_layer_process_radio_stats(pAdapter,
linkLayerStatsResults->moreResultToFollow,
(tpSirWifiRadioStat)linkLayerStatsResults->results,
linkLayerStatsResults->num_radio);
spin_lock(&context->context_lock);
if (!linkLayerStatsResults->moreResultToFollow)
context->request_bitmap &= ~(WMI_LINK_STATS_RADIO);
spin_unlock(&context->context_lock);
} else if (linkLayerStatsResults->
paramId & WMI_LINK_STATS_IFACE) {
hdd_link_layer_process_iface_stats(pAdapter,
(tpSirWifiIfaceStat)linkLayerStatsResults->results,
linkLayerStatsResults->num_peers);
spin_lock(&context->context_lock);
context->request_bitmap &= ~(WMI_LINK_STATS_IFACE);
spin_unlock(&context->context_lock);
} else if (linkLayerStatsResults->
paramId & WMI_LINK_STATS_ALL_PEER) {
hdd_link_layer_process_peer_stats(pAdapter,
linkLayerStatsResults->moreResultToFollow,
(tpSirWifiPeerStat)linkLayerStatsResults->results);
spin_lock(&context->context_lock);
if (!linkLayerStatsResults->moreResultToFollow)
context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER);
spin_unlock(&context->context_lock);
} else {
hddLog(LOGE,
FL("INVALID LL_STATS_NOTIFY RESPONSE"));
}
spin_lock(&context->context_lock);
/* complete response event if all requests are completed */
if (0 == context->request_bitmap)
complete(&context->response_event);
spin_unlock(&context->context_lock);
break;
}
default:
hddLog(CDF_TRACE_LEVEL_ERROR, "invalid event type %d", indType);
break;
}
return;
}
/**
* wlan_hdd_cfg80211_link_layer_stats_init() - initialize link layer stats
* @pHddCtx: Pointer to hdd context
*
* Return: None
*/
void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx)
{
sme_set_link_layer_stats_ind_cb(pHddCtx->hHal,
wlan_hdd_cfg80211_link_layer_stats_callback);
}
const struct
nla_policy
qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] = {
.type = NLA_U32},
};
/**
* __wlan_hdd_cfg80211_ll_stats_set() - set link layer stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int
__wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1];
tSirLLStatsSetReq LinkLayerStatsSetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_set_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("maximum attribute not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("MPDU size Not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("Stats Gathering Not Present"));
return -EINVAL;
}
/* Shall take the request Id if the Upper layers pass. 1 For now. */
LinkLayerStatsSetReq.reqId = 1;
LinkLayerStatsSetReq.mpduSizeThreshold =
nla_get_u32(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]);
LinkLayerStatsSetReq.aggressiveStatisticsGathering =
nla_get_u32(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]);
LinkLayerStatsSetReq.staId = pAdapter->sessionId;
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_SET reqId = %d", LinkLayerStatsSetReq.reqId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_SET staId = %d", LinkLayerStatsSetReq.staId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_SET mpduSizeThreshold = %d",
LinkLayerStatsSetReq.mpduSizeThreshold);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_SET aggressive Statistics Gathering = %d",
LinkLayerStatsSetReq.aggressiveStatisticsGathering);
if (CDF_STATUS_SUCCESS != sme_ll_stats_set_req(pHddCtx->hHal,
&LinkLayerStatsSetReq)) {
hddLog(CDF_TRACE_LEVEL_ERROR, "%s:"
"sme_ll_stats_set_req Failed", __func__);
return -EINVAL;
}
pAdapter->isLinkLayerStatsSet = 1;
return 0;
}
/**
* wlan_hdd_cfg80211_ll_stats_set() - set ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_set(wiphy, wdev, data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1] = {
/* Unsigned 32bit value provided by the caller issuing the GET stats
* command. When reporting
* the stats results, the driver uses the same value to indicate
* which GET request the results
* correspond to.
*/
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = {.type = NLA_U32},
/* Unsigned 32bit value . bit mask to identify what statistics are
requested for retrieval */
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = {.type = NLA_U32}
};
/**
* __wlan_hdd_cfg80211_ll_stats_get() - get link layer stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int
__wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
unsigned long rc;
struct hdd_ll_stats_context *context;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1];
tSirLLStatsGetReq LinkLayerStatsGetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
int status;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (!pAdapter->isLinkLayerStatsSet) {
hddLog(CDF_TRACE_LEVEL_FATAL,
"%s: isLinkLayerStatsSet : %d",
__func__, pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_get_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("max attribute not present"));
return -EINVAL;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("Request Id Not present"));
return -EINVAL;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("Req Mask Not present"));
return -EINVAL;
}
LinkLayerStatsGetReq.reqId =
nla_get_u32(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]);
LinkLayerStatsGetReq.paramIdMask =
nla_get_u32(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]);
LinkLayerStatsGetReq.staId = pAdapter->sessionId;
hddLog(LOG1,
FL("LL_STATS_GET reqId = %d"), LinkLayerStatsGetReq.reqId);
hddLog(LOG1,
FL("LL_STATS_GET staId = %d"), LinkLayerStatsGetReq.staId);
hddLog(LOG1,
FL("LL_STATS_GET paramIdMask = %d"),
LinkLayerStatsGetReq.paramIdMask);
context = &ll_stats_context;
spin_lock(&context->context_lock);
context->request_id = LinkLayerStatsGetReq.reqId;
context->request_bitmap = LinkLayerStatsGetReq.paramIdMask;
INIT_COMPLETION(context->response_event);
spin_unlock(&context->context_lock);
if (CDF_STATUS_SUCCESS != sme_ll_stats_get_req(pHddCtx->hHal,
&LinkLayerStatsGetReq)) {
hddLog(CDF_TRACE_LEVEL_ERROR, "%s:"
"sme_ll_stats_get_req Failed", __func__);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS));
if (!rc) {
hddLog(LOGE,
FL("Target response timed out request id %d request bitmap 0x%x"),
context->request_id, context->request_bitmap);
return -ETIMEDOUT;
}
return 0;
}
/**
* wlan_hdd_cfg80211_ll_stats_get() - get ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_get(wiphy, wdev, data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8},
};
/**
* __wlan_hdd_cfg80211_ll_stats_clear() - clear link layer stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int
__wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1];
tSirLLStatsClearReq LinkLayerStatsClearReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
u32 statsClearReqMask;
u8 stopReq;
int status;
struct sk_buff *temp_skbuff;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (!pAdapter->isLinkLayerStatsSet) {
hddLog(CDF_TRACE_LEVEL_FATAL,
"%s: isLinkLayerStatsSet : %d",
__func__, pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_clr_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("STATS_CLR_MAX is not present"));
return -EINVAL;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] ||
!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("Error in LL_STATS CLR CONFIG PARA"));
return -EINVAL;
}
statsClearReqMask = LinkLayerStatsClearReq.statsClearReqMask =
nla_get_u32(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]);
stopReq = LinkLayerStatsClearReq.stopReq =
nla_get_u8(tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]);
/*
* Shall take the request Id if the Upper layers pass. 1 For now.
*/
LinkLayerStatsClearReq.reqId = 1;
LinkLayerStatsClearReq.staId = pAdapter->sessionId;
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR reqId = %d", LinkLayerStatsClearReq.reqId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR staId = %d", LinkLayerStatsClearReq.staId);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR statsClearReqMask = 0x%X",
LinkLayerStatsClearReq.statsClearReqMask);
hddLog(CDF_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR stopReq = %d", LinkLayerStatsClearReq.stopReq);
if (CDF_STATUS_SUCCESS == sme_ll_stats_clear_req(pHddCtx->hHal,
&LinkLayerStatsClearReq)) {
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
2 *
sizeof(u32) +
2 *
NLMSG_HDRLEN);
if (temp_skbuff != NULL) {
if (nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK,
statsClearReqMask) ||
nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP,
stopReq)) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("LL_STATS_CLR put fail"));
kfree_skb(temp_skbuff);
return -EINVAL;
}
/* If the ask is to stop the stats collection as part of clear
* (stopReq = 1) , ensure that no further requests of get
* go to the firmware by having isLinkLayerStatsSet set to 0.
* However it the stopReq as part of the clear request is 0 ,
* the request to get the statistics are honoured as in this
* case the firmware is just asked to clear the statistics.
*/
if (stopReq == 1)
pAdapter->isLinkLayerStatsSet = 0;
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
return -ENOMEM;
}
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_ll_stats_clear() - clear ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_clear(wiphy, wdev, data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
#ifdef WLAN_FEATURE_STATS_EXT
/**
* __wlan_hdd_cfg80211_stats_ext_request() - ext stats request
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int __wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tStatsExtRequestReq stats_ext_req;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
int ret_val;
CDF_STATUS status;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
stats_ext_req.request_data_len = data_len;
stats_ext_req.request_data = (void *)data;
status = sme_stats_ext_request(pAdapter->sessionId, &stats_ext_req);
if (CDF_STATUS_SUCCESS != status)
ret_val = -EINVAL;
return ret_val;
}
/**
* wlan_hdd_cfg80211_stats_ext_request() - ext stats request
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
int wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_stats_ext_request(wiphy, wdev,
data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_cfg80211_stats_ext_callback() - ext stats callback
* @ctx: Pointer to HDD context
* @msg: Message received
*
* Return: nothing
*/
static void wlan_hdd_cfg80211_stats_ext_callback(void *ctx,
tStatsExtEvent *msg)
{
hdd_context_t *pHddCtx = (hdd_context_t *) ctx;
struct sk_buff *vendor_event;
int status;
int ret_val;
tStatsExtEvent *data = msg;
hdd_adapter_t *pAdapter = NULL;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: HDD context is not valid", __func__);
return;
}
pAdapter = hdd_get_adapter_by_vdev(pHddCtx, data->vdev_id);
if (NULL == pAdapter) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: vdev_id %d does not exist with host",
__func__, data->vdev_id);
return;
}
vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
NULL,
data->event_data_len +
sizeof(uint32_t) +
NLMSG_HDRLEN + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX,
GFP_KERNEL);
if (!vendor_event) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_event_alloc failed", __func__);
return;
}
ret_val = nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_IFINDEX,
pAdapter->dev->ifindex);
if (ret_val) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_IFINDEX put fail",
__func__);
kfree_skb(vendor_event);
return;
}
ret_val = nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_STATS_EXT,
data->event_data_len, data->event_data);
if (ret_val) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_STATS_EXT put fail",
__func__);
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
}
/**
* wlan_hdd_cfg80211_stats_ext_init() - ext stats init
* @ctx: Pointer to HDD context
*
* Return: nothing
*/
void wlan_hdd_cfg80211_stats_ext_init(hdd_context_t *pHddCtx)
{
sme_stats_ext_register_callback(pHddCtx->hHal,
wlan_hdd_cfg80211_stats_ext_callback);
}
#endif /* End of WLAN_FEATURE_STATS_EXT */
/**
* __wlan_hdd_cfg80211_get_station() - get station statistics
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @mac: Pointer to mac
* @sinfo: Pointer to station info
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *mac,
struct station_info *sinfo)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
int ssidlen = pHddStaCtx->conn_info.SSID.SSID.length;
uint8_t rate_flags;
hdd_context_t *pHddCtx = (hdd_context_t *) wiphy_priv(wiphy);
struct hdd_config *pCfg = pHddCtx->config;
uint8_t OperationalRates[CSR_DOT11_SUPPORTED_RATES_MAX];
uint32_t ORLeng = CSR_DOT11_SUPPORTED_RATES_MAX;
uint8_t ExtendedRates[CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX];
uint32_t ERLeng = CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX;
uint8_t MCSRates[SIZE_OF_BASIC_MCS_SET];
uint32_t MCSLeng = SIZE_OF_BASIC_MCS_SET;
uint16_t maxRate = 0;
uint16_t myRate;
uint16_t currentRate = 0;
uint8_t maxSpeedMCS = 0;
uint8_t maxMCSIdx = 0;
uint8_t rateFlag = 1;
uint8_t i, j, rssidx;
uint8_t nss = 1;
int status, mode = 0, maxHtIdx;
struct index_vht_data_rate_type *supported_vht_mcs_rate;
struct index_data_rate_type *supported_mcs_rate;
#ifdef WLAN_FEATURE_11AC
uint32_t vht_mcs_map;
enum eDataRate11ACMaxMcs vhtMaxMcs;
#endif /* WLAN_FEATURE_11AC */
ENTER();
if (CDF_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if ((eConnectionState_Associated != pHddStaCtx->conn_info.connState) ||
(0 == ssidlen)) {
hddLog(LOG1, FL("Not associated or Invalid ssidlen, %d"),
ssidlen);
/*To keep GUI happy */
return 0;
}
if (true == pHddStaCtx->hdd_ReassocScenario) {
hddLog(LOG1,
FL("Roaming is in progress, cannot continue with this request"));
return 0;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(LOGE, FL("HDD context is not valid"));
return status;
}
wlan_hdd_get_rssi(pAdapter, &sinfo->signal);
sinfo->filled |= STATION_INFO_SIGNAL;
#ifdef WLAN_FEATURE_LPSS
if (!pAdapter->rssi_send) {
pAdapter->rssi_send = true;
wlan_hdd_send_status_pkg(pAdapter, pHddStaCtx, 1, 1);
}
#endif
wlan_hdd_get_station_stats(pAdapter);
rate_flags = pAdapter->hdd_stats.ClassA_stat.tx_rate_flags;
/* convert to the UI units of 100kbps */
myRate = pAdapter->hdd_stats.ClassA_stat.tx_rate * 5;
if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
nss = pAdapter->hdd_stats.ClassA_stat.rx_frag_cnt;
if (eHDD_LINK_SPEED_REPORT_ACTUAL == pCfg->reportMaxLinkSpeed) {
/* Get current rate flags if report actual */
rate_flags =
pAdapter->hdd_stats.ClassA_stat.
promiscuous_rx_frag_cnt;
}
if (pAdapter->hdd_stats.ClassA_stat.mcs_index ==
INVALID_MCS_IDX) {
rate_flags = eHAL_TX_RATE_LEGACY;
pAdapter->hdd_stats.ClassA_stat.mcs_index = 0;
}
}
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("RSSI %d, RLMS %u, rate %d, rssi high %d, rssi mid %d, rssi low %d, rate_flags 0x%x, MCS %d\n",
sinfo->signal, pCfg->reportMaxLinkSpeed, myRate,
(int)pCfg->linkSpeedRssiHigh, (int)pCfg->linkSpeedRssiMid,
(int)pCfg->linkSpeedRssiLow, (int)rate_flags,
(int)pAdapter->hdd_stats.ClassA_stat.mcs_index);
#endif /* LINKSPEED_DEBUG_ENABLED */
if (eHDD_LINK_SPEED_REPORT_ACTUAL != pCfg->reportMaxLinkSpeed) {
/* we do not want to necessarily report the current speed */
if (eHDD_LINK_SPEED_REPORT_MAX == pCfg->reportMaxLinkSpeed) {
/* report the max possible speed */
rssidx = 0;
} else if (eHDD_LINK_SPEED_REPORT_MAX_SCALED ==
pCfg->reportMaxLinkSpeed) {
/* report the max possible speed with RSSI scaling */
if (sinfo->signal >= pCfg->linkSpeedRssiHigh) {
/* report the max possible speed */
rssidx = 0;
} else if (sinfo->signal >= pCfg->linkSpeedRssiMid) {
/* report middle speed */
rssidx = 1;
} else if (sinfo->signal >= pCfg->linkSpeedRssiLow) {
/* report middle speed */
rssidx = 2;
} else {
/* report actual speed */
rssidx = 3;
}
} else {
/* unknown, treat as eHDD_LINK_SPEED_REPORT_MAX */
hddLog(LOGE,
FL("Invalid value for reportMaxLinkSpeed: %u"),
pCfg->reportMaxLinkSpeed);
rssidx = 0;
}
maxRate = 0;
/* Get Basic Rate Set */
if (0 !=
sme_cfg_get_str(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_OPERATIONAL_RATE_SET,
OperationalRates,
&ORLeng)) {
hddLog(LOGE, FL("cfg get returned failure"));
/*To keep GUI happy */
return 0;
}
for (i = 0; i < ORLeng; i++) {
for (j = 0;
j < ARRAY_SIZE(supported_data_rate); j++) {
/* Validate Rate Set */
if (supported_data_rate[j].beacon_rate_index ==
(OperationalRates[i] & 0x7F)) {
currentRate =
supported_data_rate[j].
supported_rate[rssidx];
break;
}
}
/* Update MAX rate */
maxRate =
(currentRate > maxRate) ? currentRate : maxRate;
}
/* Get Extended Rate Set */
if (0 !=
sme_cfg_get_str(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_EXTENDED_OPERATIONAL_RATE_SET,
ExtendedRates, &ERLeng)) {
hddLog(LOGE, FL("cfg get returned failure"));
/*To keep GUI happy */
return 0;
}
for (i = 0; i < ERLeng; i++) {
for (j = 0;
j < ARRAY_SIZE(supported_data_rate); j++) {
if (supported_data_rate[j].beacon_rate_index ==
(ExtendedRates[i] & 0x7F)) {
currentRate =
supported_data_rate[j].
supported_rate[rssidx];
break;
}
}
/* Update MAX rate */
maxRate =
(currentRate > maxRate) ? currentRate : maxRate;
}
/* Get MCS Rate Set --
Only if we are connected in non legacy mode and not reporting
actual speed */
if ((3 != rssidx) && !(rate_flags & eHAL_TX_RATE_LEGACY)) {
if (0 !=
sme_cfg_get_str(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_CURRENT_MCS_SET, MCSRates,
&MCSLeng)) {
hddLog(LOGE, FL("cfg get returned failure"));
/*To keep GUI happy */
return 0;
}
rateFlag = 0;
#ifdef WLAN_FEATURE_11AC
supported_vht_mcs_rate =
(struct index_vht_data_rate_type *)
((nss ==
1) ? &supported_vht_mcs_rate_nss1 :
&supported_vht_mcs_rate_nss2);
if (rate_flags & eHAL_TX_RATE_VHT80)
mode = 2;
else if ((rate_flags & eHAL_TX_RATE_VHT40) ||
(rate_flags & eHAL_TX_RATE_HT40))
mode = 1;
else
mode = 0;
/* VHT80 rate has seperate rate table */
if (rate_flags &
(eHAL_TX_RATE_VHT20 | eHAL_TX_RATE_VHT40 |
eHAL_TX_RATE_VHT80)) {
sme_cfg_get_int(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_VHT_TX_MCS_MAP,
&vht_mcs_map);
vhtMaxMcs = (enum eDataRate11ACMaxMcs)
(vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
if (rate_flags & eHAL_TX_RATE_SGI)
rateFlag |= 1;
if (DATA_RATE_11AC_MAX_MCS_7 == vhtMaxMcs)
maxMCSIdx = 7;
else if (DATA_RATE_11AC_MAX_MCS_8 ==
vhtMaxMcs)
maxMCSIdx = 8;
else if (DATA_RATE_11AC_MAX_MCS_9 ==
vhtMaxMcs) {
/* VHT20 is supporting 0~8 */
if (rate_flags & eHAL_TX_RATE_VHT20)
maxMCSIdx = 8;
else
maxMCSIdx = 9;
}
if (rssidx != 0) {
for (i = 0; i <= maxMCSIdx; i++) {
if (sinfo->signal <=
rssi_mcs_tbl[mode][i]) {
maxMCSIdx = i;
break;
}
}
}
if (rate_flags & eHAL_TX_RATE_VHT80) {
currentRate =
supported_vht_mcs_rate[pAdapter->
hdd_stats.ClassA_stat.mcs_index].
supported_VHT80_rate[rateFlag];
maxRate =
supported_vht_mcs_rate[maxMCSIdx].
supported_VHT80_rate[rateFlag];
} else if (rate_flags & eHAL_TX_RATE_VHT40) {
currentRate =
supported_vht_mcs_rate[pAdapter->
hdd_stats.ClassA_stat.mcs_index].
supported_VHT40_rate[rateFlag];
maxRate =
supported_vht_mcs_rate[maxMCSIdx].
supported_VHT40_rate[rateFlag];
} else if (rate_flags & eHAL_TX_RATE_VHT20) {
currentRate =
supported_vht_mcs_rate[pAdapter->
hdd_stats.ClassA_stat.mcs_index].
supported_VHT20_rate[rateFlag];
maxRate =
supported_vht_mcs_rate[maxMCSIdx].
supported_VHT20_rate[rateFlag];
}
maxSpeedMCS = 1;
if (currentRate > maxRate)
maxRate = currentRate;
} else
#endif /* WLAN_FEATURE_11AC */
{
if (rate_flags & eHAL_TX_RATE_HT40)
rateFlag |= 1;
if (rate_flags & eHAL_TX_RATE_SGI)
rateFlag |= 2;
supported_mcs_rate =
(struct index_data_rate_type *)
((nss ==
1) ? &supported_mcs_rate_nss1 :
&supported_mcs_rate_nss2);
maxHtIdx = MAX_HT_MCS_IDX;
if (rssidx != 0) {
for (i = 0; i < MAX_HT_MCS_IDX; i++) {
if (sinfo->signal <=
rssi_mcs_tbl[mode][i]) {
maxHtIdx = i + 1;
break;
}
}
}
for (i = 0; i < MCSLeng; i++) {
for (j = 0; j < maxHtIdx; j++) {
if (supported_mcs_rate[j].
beacon_rate_index ==
MCSRates[i]) {
currentRate =
supported_mcs_rate[j].
supported_rate
[rateFlag];
break;
}
}
if ((j < MAX_HT_MCS_IDX)
&& (currentRate > maxRate)) {
maxRate = currentRate;
maxSpeedMCS = 1;
maxMCSIdx =
supported_mcs_rate[j].
beacon_rate_index;
}
}
}
}
else if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
maxRate = myRate;
maxSpeedMCS = 1;
maxMCSIdx = pAdapter->hdd_stats.ClassA_stat.mcs_index;
}
/* report a value at least as big as current rate */
if ((maxRate < myRate) || (0 == maxRate)) {
maxRate = myRate;
if (rate_flags & eHAL_TX_RATE_LEGACY) {
maxSpeedMCS = 0;
} else {
maxSpeedMCS = 1;
maxMCSIdx =
pAdapter->hdd_stats.ClassA_stat.mcs_index;
}
}
if (rate_flags & eHAL_TX_RATE_LEGACY) {
sinfo->txrate.legacy = maxRate;
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting legacy rate %d\n",
sinfo->txrate.legacy);
#endif /* LINKSPEED_DEBUG_ENABLED */
} else {
sinfo->txrate.mcs = maxMCSIdx;
#ifdef WLAN_FEATURE_11AC
sinfo->txrate.nss = nss;
if (rate_flags & eHAL_TX_RATE_VHT80) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
sinfo->txrate.flags |=
RATE_INFO_FLAGS_80_MHZ_WIDTH;
} else if (rate_flags & eHAL_TX_RATE_VHT40) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
} else if (rate_flags & eHAL_TX_RATE_VHT20) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
} else
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
#endif /* WLAN_FEATURE_11AC */
if (rate_flags &
(eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40)) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
if (rate_flags & eHAL_TX_RATE_HT40) {
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
}
}
if (rate_flags & eHAL_TX_RATE_SGI) {
if (!
(sinfo->txrate.
flags & RATE_INFO_FLAGS_VHT_MCS))
sinfo->txrate.flags |=
RATE_INFO_FLAGS_MCS;
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
}
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting MCS rate %d flags %x\n",
sinfo->txrate.mcs, sinfo->txrate.flags);
#endif /* LINKSPEED_DEBUG_ENABLED */
}
} else {
/* report current rate instead of max rate */
if (rate_flags & eHAL_TX_RATE_LEGACY) {
/* provide to the UI in units of 100kbps */
sinfo->txrate.legacy = myRate;
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting actual legacy rate %d\n",
sinfo->txrate.legacy);
#endif /* LINKSPEED_DEBUG_ENABLED */
} else {
/* must be MCS */
sinfo->txrate.mcs =
pAdapter->hdd_stats.ClassA_stat.mcs_index;
#ifdef WLAN_FEATURE_11AC
sinfo->txrate.nss = nss;
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
if (rate_flags & eHAL_TX_RATE_VHT80) {
sinfo->txrate.flags |=
RATE_INFO_FLAGS_80_MHZ_WIDTH;
} else if (rate_flags & eHAL_TX_RATE_VHT40) {
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
}
#endif /* WLAN_FEATURE_11AC */
if (rate_flags &
(eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40)) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
if (rate_flags & eHAL_TX_RATE_HT40) {
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
}
}
if (rate_flags & eHAL_TX_RATE_SGI) {
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
}
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting actual MCS rate %d flags %x\n",
sinfo->txrate.mcs, sinfo->txrate.flags);
#endif /* LINKSPEED_DEBUG_ENABLED */
}
}
sinfo->filled |= STATION_INFO_TX_BITRATE;
sinfo->tx_bytes = pAdapter->stats.tx_bytes;
sinfo->filled |= STATION_INFO_TX_BYTES;
sinfo->tx_packets =
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[0] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[1] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[2] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[3];
sinfo->tx_retries =
pAdapter->hdd_stats.summary_stat.retry_cnt[0] +
pAdapter->hdd_stats.summary_stat.retry_cnt[1] +
pAdapter->hdd_stats.summary_stat.retry_cnt[2] +
pAdapter->hdd_stats.summary_stat.retry_cnt[3];
sinfo->tx_failed =
pAdapter->hdd_stats.summary_stat.fail_cnt[0] +
pAdapter->hdd_stats.summary_stat.fail_cnt[1] +
pAdapter->hdd_stats.summary_stat.fail_cnt[2] +
pAdapter->hdd_stats.summary_stat.fail_cnt[3];
sinfo->filled |=
STATION_INFO_TX_PACKETS |
STATION_INFO_TX_RETRIES | STATION_INFO_TX_FAILED;
sinfo->rx_bytes = pAdapter->stats.rx_bytes;
sinfo->filled |= STATION_INFO_RX_BYTES;
sinfo->rx_packets = pAdapter->stats.rx_packets;
sinfo->filled |= STATION_INFO_RX_PACKETS;
MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_STA,
pAdapter->sessionId, maxRate));
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_get_station() - get station statistics
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @mac: Pointer to mac
* @sinfo: Pointer to station info
*
* Return: 0 for success, non-zero for failure
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *dev, const uint8_t *mac,
struct station_info *sinfo)
#else
int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *dev, uint8_t *mac,
struct station_info *sinfo)
#endif
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_get_stats() - Function to retrieve interface statistics
* @dev: pointer to network device
*
* This function is the ndo_get_stats method for all netdevs
* registered with the kernel
*
* Return: pointer to net_device_stats structure
*/
struct net_device_stats *hdd_get_stats(struct net_device *dev)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
return &adapter->stats;
}
/**
* __wlan_hdd_cfg80211_dump_survey() - get survey related info
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @idx: Index
* @survey: Pointer to survey info
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev,
int idx, struct survey_info *survey)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
hdd_station_ctx_t *pHddStaCtx;
tHalHandle halHandle;
uint32_t channel = 0, freq = 0; /* Initialization Required */
int8_t snr, rssi;
int status, i, j, filled = 0;
ENTER();
if (CDF_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(LOGE, FL("HDD context is not valid"));
return status;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (0 == pHddCtx->config->fEnableSNRMonitoring ||
0 != pAdapter->survey_idx ||
eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
/* The survey dump ops when implemented completely is expected
* to return a survey of all channels and the ops is called by
* the kernel with incremental values of the argument 'idx'
* till it returns -ENONET. But we can only support the survey
* for the operating channel for now. survey_idx is used to
* track that the ops is called only once and then return
* -ENONET for the next iteration
*/
pAdapter->survey_idx = 0;
return -ENONET;
}
if (!pHddStaCtx->hdd_ReassocScenario) {
hdd_err("Roaming in progress, hence return");
return -ENONET;
}
halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
wlan_hdd_get_snr(pAdapter, &snr);
wlan_hdd_get_rssi(pAdapter, &rssi);
sme_get_operation_channel(halHandle, &channel, pAdapter->sessionId);
hdd_wlan_get_freq(channel, &freq);
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
if (NULL == wiphy->bands[i]) {
hddLog(LOG1, FL("wiphy->bands[i] is NULL, i = %d"), i);
continue;
}
for (j = 0; j < wiphy->bands[i]->n_channels; j++) {
struct ieee80211_supported_band *band = wiphy->bands[i];
if (band->channels[j].center_freq == (uint16_t) freq) {
survey->channel = &band->channels[j];
/* The Rx BDs contain SNR values in dB for the
* received frames while the supplicant expects
* noise. So we calculate and return the value
* of noise (dBm)
* SNR (dB) = RSSI (dBm) - NOISE (dBm)
*/
survey->noise = rssi - snr;
survey->filled = SURVEY_INFO_NOISE_DBM;
filled = 1;
}
}
}
if (filled)
pAdapter->survey_idx = 1;
else {
pAdapter->survey_idx = 0;
return -ENONET;
}
return 0;
}
/**
* wlan_hdd_cfg80211_dump_survey() - get survey related info
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @idx: Index
* @survey: Pointer to survey info
*
* Return: 0 for success, non-zero for failure
*/
int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev,
int idx, struct survey_info *survey)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_dump_survey(wiphy, dev, idx, survey);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_init_ll_stats_ctx() - initialize link layer stats context
*
* Return: none
*/
inline void hdd_init_ll_stats_ctx(void)
{
spin_lock_init(&ll_stats_context.context_lock);
init_completion(&ll_stats_context.response_event);
ll_stats_context.request_bitmap = 0;
return;
}