qcacld-3.0: Add support of request peer stats info(part 3)

qcacld-2.0 to qcacld-3.0 propagation

FW has limitation to support old WMI_REQUEST_STATS_CMDID and
WMI_UPDATE_STATS_EVENTID interface on AP vDev. FW suggest to use new
WMI_REQUEST_PEER_STATS_INFO_CMDID and WMI_PEER_STATS_INFO_EVENTID to
get tx/rx rate.

This checkin is about HDD layer, provide APIs/callbacks to upper layer
to query rssi/tx/rx statistics of peer station associated with SAP,
and provide a default disabled ini setting and checking FW's
WMI_SERVICE_PEER_STATS_INFO capability to control this get peer info
interface. Add/change iwpriv/QCA_NL80211_VENDOR_SUBCMD_GET_STATION/
cfg80211_ops.get_station interfaces to upper layer.

Change-Id: Ib1571219faa16e3bc8c3eb1260137ae08b8b6016
CRs-Fixed: 2059507
This commit is contained in:
Will Huang 2017-07-11 16:38:50 +08:00 • committed by snandini
commit 496b36c936
16 changed files with 1992 additions and 194 deletions

View file

@ -141,6 +141,7 @@ struct channel_list_info {
#define QCSAP_SET_WLAN_RESUME 6
#define MAX_VAR_ARGS 7
#define QCSAP_IOCTL_PRIV_GET_RSSI (SIOCIWFIRSTPRIV + 29)
#define QCSAP_IOCTL_PRIV_GET_SOFTAP_LINK_SPEED (SIOCIWFIRSTPRIV + 31)
#define QCSAP_IOCTL_MAX_STR_LEN 1024

View file

@ -5935,6 +5935,24 @@ enum hdd_link_speed_rpt_type {
#define CFG_MAX_HT_MCS_FOR_TX_DATA_MAX (WNI_CFG_MAX_HT_MCS_TX_DATA_STAMAX)
#define CFG_MAX_HT_MCS_FOR_TX_DATA_DEFAULT (WNI_CFG_MAX_HT_MCS_TX_DATA_STADEF)
/*
* <ini>
* gSapGetPeerInfo - Enable/Disable remote peer info query support
* @Min: 0 - Disable remote peer info query support
* @Max: 1 - Enable remote peer info query support
* @Default: 0
*
* This ini is used to enable/disable remote peer info query support
*
* Usage: External
*
* </ini>
*/
#define CFG_SAP_GET_PEER_INFO "gSapGetPeerInfo"
#define CFG_SAP_GET_PEER_INFO_MIN (0)
#define CFG_SAP_GET_PEER_INFO_MAX (1)
#define CFG_SAP_GET_PEER_INFO_DEFAULT (0)
/*
* <ini>
* gDisableABGRateForTxData - disable abg rate for tx data
@ -12003,6 +12021,7 @@ struct hdd_config {
bool gEnableOverLapCh;
bool fRegChangeDefCountry;
uint16_t max_ht_mcs_txdata;
bool sap_get_peer_info;
bool disable_abg_rate_txdata;
uint8_t rate_for_tx_mgmt;
uint8_t rate_for_tx_mgmt_2g;

View file

@ -774,42 +774,118 @@ enum bss_stop_reason {
BSS_STOP_DUE_TO_MCC_SCC_SWITCH = 1,
};
/*
* Per station structure kept in HDD for multiple station support for SoftAP
/**
* struct hdd_rate_info - rate_info in HDD
* @rate: tx/rx rate (kbps)
* @mode: 0->11abg legacy, 1->HT, 2->VHT (refer to sir_sme_phy_mode)
* @nss: number of streams
* @mcs: mcs index for HT/VHT mode
* @rate_flags: rate flags for last tx/rx
*
* rate info in HDD
*/
struct hdd_rate_info {
uint32_t rate;
uint8_t mode;
uint8_t nss;
uint8_t mcs;
uint8_t rate_flags;
};
/**
* struct hdd_fw_txrx_stats - fw txrx status in HDD
* (refer to station_info struct in Kernel)
* @tx_packets: packets transmitted to this station
* @tx_bytes: bytes transmitted to this station
* @rx_packets: packets received from this station
* @rx_bytes: bytes received from this station
* @rx_retries: cumulative retry counts
* @tx_failed: number of failed transmissions
* @rssi: The signal strength (dbm)
* @tx_rate: last used tx rate info
* @rx_rate: last used rx rate info
*
* fw txrx status in HDD
*/
struct hdd_fw_txrx_stats {
uint32_t tx_packets;
uint64_t tx_bytes;
uint32_t rx_packets;
uint64_t rx_bytes;
uint32_t tx_retries;
uint32_t tx_failed;
int8_t rssi;
struct hdd_rate_info tx_rate;
struct hdd_rate_info rx_rate;
};
/**
* typedef struct hdd_station_info_t - Per station structure kept in HDD for
* multiple station support for SoftAP
* @isUsed: The station entry is used or not
* @ucSTAId: Station ID reported back from HAL (through SAP).
* Broadcast uses station ID zero by default.
* @staType: Type of station i.e. p2p client or infrastructure station
* @macAddrSTA: MAC address of the station
* @tlSTAState: Current Station state so HDD knows how to deal with packet
* queue. Most recent states used to change TLSHIM STA state.
* @isQosEnabled: Track QoS status of station
* @isDeauthInProgress: The station entry for which Deauth is in progress
* @nss: Number of spatial streams supported
* @rate_flags: Rate Flags for this connection
* @ecsa_capable: Extended CSA capabilities
* @max_phy_rate: Calcuated maximum phy rate based on mode, nss, mcs etc.
* @tx_packets: Packets send to current station
* @tx_bytes: Bytes send to current station
* @rx_packets: Packets received from current station
* @rx_bytes: Bytes received from current station
* @last_tx_rx_ts: Last tx/rx timestamp with current station
* @assoc_ts: Current station association timestamp
* @tx_rate: Tx rate with current station reported from F/W
* @rx_rate: Rx rate with current station reported from F/W
* @ampdu: Ampdu enable or not of the station
* @sgi_enable: Short GI enable or not of the station
* @tx_stbc: Tx Space-time block coding enable/disable
* @rx_stbc: Rx Space-time block coding enable/disable
* @ch_width: Channel Width of the connection
* @mode: Mode of the connection
* @max_supp_idx: Max supported rate index of the station
* @max_ext_idx: Max extended supported rate index of the station
* @max_mcs_idx: Max supported mcs index of the station
* @rx_mcs_map: VHT Rx mcs map
* @tx_mcs_map: VHT Tx mcs map
*/
typedef struct {
/** The station entry is used or not */
bool isUsed;
/* Station ID reported back from HAL (through SAP).
* Broadcast uses station ID zero by default
*/
uint8_t ucSTAId;
/* type of station i.e. p2p client or infrastructure station */
eStationType staType;
/** MAC address of the station */
struct qdf_mac_addr macAddrSTA;
/** Current Station state so HDD knows how to deal with packet
* queue. Most recent states used to change TLSHIM STA state
*/
enum ol_txrx_peer_state tlSTAState;
/** Track QoS status of station */
bool isQosEnabled;
/** The station entry for which Deauth is in progress */
bool isDeauthInProgress;
/** Number of spatial streams supported */
uint8_t nss;
/** Rate Flags for this connection */
uint32_t rate_flags;
/** Extended CSA */
uint8_t ecsa_capable;
uint32_t max_phy_rate;
uint32_t tx_packets;
uint64_t tx_bytes;
uint32_t rx_packets;
uint64_t rx_bytes;
qdf_time_t last_tx_rx_ts;
qdf_time_t assoc_ts;
uint32_t tx_rate;
uint32_t rx_rate;
bool ampdu;
bool sgi_enable;
bool tx_stbc;
bool rx_stbc;
uint8_t ch_width;
uint8_t mode;
uint8_t max_supp_idx;
uint8_t max_ext_idx;
uint8_t max_mcs_idx;
uint8_t rx_mcs_map;
uint8_t tx_mcs_map;
} hdd_station_info_t;
struct hdd_ap_ctx {
@ -866,6 +942,9 @@ struct hdd_ap_ctx {
bool vendor_acs_timer_initialized;
enum bss_stop_reason bss_stop_reason;
/* Fw txrx stats info */
struct hdd_fw_txrx_stats txrx_stats;
};
/**

View file

@ -476,4 +476,32 @@ int hdd_check_standard_wext_control(struct hdd_context *hdd_ctx,
int hdd_check_private_wext_control(struct hdd_context *hdd_ctx,
struct iw_request_info *info);
/**
* wlan_hdd_get_peer_rssi() - get station's rssi
* @adapter: hostapd interface
* @macaddress: peer sta mac address or ff:ff:ff:ff:ff:ff to query all peer
* @peer_sta_info: output pointer which will fill by peer sta info
*
* This function will call sme_get_peer_info to get rssi
*
* Return: 0 on success, otherwise error value
*/
int wlan_hdd_get_peer_rssi(hdd_adapter_t *adapter,
struct qdf_mac_addr *macaddress,
struct sir_peer_sta_info *peer_sta_info);
/**
* wlan_hdd_get_peer_info() - get peer info
* @adapter: hostapd interface
* @macaddress: request peer mac address
* @peer_info_ext: one peer extended info retrieved
*
* This function will call sme_get_peer_info_ext to get peer info
*
* Return: 0 on success, otherwise error value
*/
int wlan_hdd_get_peer_info(hdd_adapter_t *adapter,
struct qdf_mac_addr macaddress,
struct sir_peer_info_ext *peer_info_ext);
#endif /* __WEXT_IW_H__ */

View file

@ -2178,6 +2178,13 @@ struct reg_table_entry g_registry_table[] = {
CFG_MAX_HT_MCS_FOR_TX_DATA_MIN,
CFG_MAX_HT_MCS_FOR_TX_DATA_MAX),
REG_VARIABLE(CFG_SAP_GET_PEER_INFO, WLAN_PARAM_Integer,
struct hdd_config, sap_get_peer_info,
VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
CFG_SAP_GET_PEER_INFO_DEFAULT,
CFG_SAP_GET_PEER_INFO_MIN,
CFG_SAP_GET_PEER_INFO_MAX),
REG_VARIABLE(CFG_DISABLE_ABG_RATE_FOR_TX_DATA, WLAN_PARAM_Integer,
struct hdd_config, disable_abg_rate_txdata,
VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,

View file

@ -112,44 +112,6 @@
#include "wlan_utility.h"
#include "wlan_reg_ucfg_api.h"
/* define short names for get station info attributes */
#ifndef LINK_INFO_STANDARD_NL80211_ATTR
#define LINK_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_LINK_STANDARD_NL80211_ATTR
#endif
#ifndef AP_INFO_STANDARD_NL80211_ATTR
#define AP_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_STANDARD_NL80211_ATTR
#endif
#ifndef INFO_ROAM_COUNT
#define INFO_ROAM_COUNT \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ROAM_COUNT
#endif
#ifndef INFO_AKM
#define INFO_AKM \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AKM
#endif
#ifndef WLAN802_11_MODE
#define WLAN802_11_MODE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_802_11_MODE
#endif
#ifndef AP_INFO_HS20_INDICATION
#define AP_INFO_HS20_INDICATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_HS20_INDICATION
#endif
#ifndef HT_OPERATION
#define HT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HT_OPERATION
#endif
#ifndef VHT_OPERATION
#define VHT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_VHT_OPERATION
#endif
#ifndef INFO_ASSOC_FAIL_REASON
#define INFO_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_FAIL_REASON
#endif
#define g_mode_rates_size (12)
#define a_mode_rates_size (8)
#define GET_IE_LEN_IN_BSS_DESC(lenInBss) (lenInBss + sizeof(lenInBss) - \
@ -4149,13 +4111,68 @@ static int wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy,
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO
#define STATION_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_ASSOC_FAIL_REASON
#define STATION_REMOTE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_REMOTE
#define STATION_MAX \
QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX
/* define short names for get station info attributes */
#define LINK_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_LINK_STANDARD_NL80211_ATTR
#define AP_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_STANDARD_NL80211_ATTR
#define INFO_ROAM_COUNT \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ROAM_COUNT
#define INFO_AKM \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AKM
#define WLAN802_11_MODE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_802_11_MODE
#define AP_INFO_HS20_INDICATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_HS20_INDICATION
#define HT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HT_OPERATION
#define VHT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_VHT_OPERATION
#define INFO_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_FAIL_REASON
#define REMOTE_MAX_PHY_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_MAX_PHY_RATE
#define REMOTE_TX_PACKETS \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_PACKETS
#define REMOTE_TX_BYTES \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_BYTES
#define REMOTE_RX_PACKETS \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_PACKETS
#define REMOTE_RX_BYTES \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BYTES
#define REMOTE_LAST_TX_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_TX_RATE
#define REMOTE_LAST_RX_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_RX_RATE
#define REMOTE_WMM \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_WMM
#define REMOTE_SUPPORTED_MODE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SUPPORTED_MODE
#define REMOTE_AMPDU \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AMPDU
#define REMOTE_TX_STBC \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_STBC
#define REMOTE_RX_STBC \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_STBC
#define REMOTE_CH_WIDTH\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_CH_WIDTH
#define REMOTE_SGI_ENABLE\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SGI_ENABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#define REMOTE_PAD\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_PAD
#endif
static const struct nla_policy
hdd_get_station_policy[STATION_MAX + 1] = {
[STATION_INFO] = {.type = NLA_FLAG},
[STATION_ASSOC_FAIL_REASON] = {.type = NLA_FLAG},
[STATION_REMOTE] = {.type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE},
};
/**
@ -4583,6 +4600,169 @@ fail:
return -EINVAL;
}
/**
* hdd_get_stainfo() - get stainfo for the specified peer
* @adapter: hostapd interface
* @mac_addr: mac address of requested peer
*
* This function find the stainfo for the peer with mac_addr
*
* Return: stainfo if found, NULL if not found
*/
static hdd_station_info_t *hdd_get_stainfo(hdd_adapter_t *adapter,
struct qdf_mac_addr mac_addr)
{
hdd_station_info_t *stainfo = NULL;
int i;
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
if (!qdf_mem_cmp(&adapter->aStaInfo[i].macAddrSTA,
&mac_addr,
QDF_MAC_ADDR_SIZE))
stainfo = &adapter->aStaInfo[i];
}
return stainfo;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
static inline int32_t remote_station_put_u64(struct sk_buff *skb,
int32_t attrtype,
uint64_t value)
{
return nla_put_u64_64bit(skb, attrtype, value, REMOTE_PAD);
}
#else
static inline int32_t remote_station_put_u64(struct sk_buff *skb,
int32_t attrtype,
uint64_t value)
{
return nla_put_u64(skb, attrtype, value);
}
#endif
/**
* hdd_get_station_remote() - get remote peer's info
* @hdd_ctx: hdd context
* @adapter: hostapd interface
* @mac_addr: mac address of requested peer
*
* This function collect and indicate the remote peer's info
*
* Return: 0 on success, otherwise error value
*/
static int hdd_get_station_remote(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
struct qdf_mac_addr mac_addr)
{
hdd_station_info_t *stainfo = hdd_get_stainfo(adapter, mac_addr);
struct sk_buff *skb = NULL;
struct sir_peer_info_ext peer_info;
uint32_t nl_buf_len;
bool txrx_rate = true;
if (!stainfo) {
hdd_err("peer " MAC_ADDRESS_STR " not found",
MAC_ADDR_ARRAY(mac_addr.bytes));
goto fail;
}
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(stainfo->max_phy_rate) + NLA_HDRLEN) +
(sizeof(stainfo->tx_packets) + NLA_HDRLEN) +
(sizeof(stainfo->tx_bytes) + NLA_HDRLEN) +
(sizeof(stainfo->rx_packets) + NLA_HDRLEN) +
(sizeof(stainfo->rx_bytes) + NLA_HDRLEN) +
(sizeof(stainfo->isQosEnabled) + NLA_HDRLEN) +
(sizeof(stainfo->mode) + NLA_HDRLEN);
if (!hdd_ctx->config->sap_get_peer_info ||
wlan_hdd_get_peer_info(adapter, mac_addr, &peer_info)) {
hdd_err("fail to get tx/rx rate");
txrx_rate = false;
} else {
stainfo->tx_rate = peer_info.tx_rate;
stainfo->rx_rate = peer_info.rx_rate;
nl_buf_len += (sizeof(stainfo->tx_rate) + NLA_HDRLEN) +
(sizeof(stainfo->rx_rate) + NLA_HDRLEN);
}
/* below info is only valid for HT/VHT mode */
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY)
nl_buf_len += (sizeof(stainfo->ampdu) + NLA_HDRLEN) +
(sizeof(stainfo->tx_stbc) + NLA_HDRLEN) +
(sizeof(stainfo->rx_stbc) + NLA_HDRLEN) +
(sizeof(stainfo->ch_width) + NLA_HDRLEN) +
(sizeof(stainfo->sgi_enable) + NLA_HDRLEN);
hdd_info("buflen %d hdrlen %d", nl_buf_len, NLMSG_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
nl_buf_len);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
goto fail;
}
hdd_info("stainfo");
hdd_info("maxrate %x tx_pkts %x tx_bytes %llx",
stainfo->max_phy_rate, stainfo->tx_packets,
stainfo->tx_bytes);
hdd_info("rx_pkts %x rx_bytes %llx mode %x",
stainfo->rx_packets, stainfo->rx_bytes,
stainfo->mode);
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) {
hdd_info("ampdu %d tx_stbc %d rx_stbc %d",
stainfo->ampdu, stainfo->tx_stbc,
stainfo->rx_stbc);
hdd_info("wmm %d chwidth %d sgi %d",
stainfo->isQosEnabled,
stainfo->ch_width,
stainfo->sgi_enable);
}
if (nla_put_u32(skb, REMOTE_MAX_PHY_RATE, stainfo->max_phy_rate) ||
nla_put_u32(skb, REMOTE_TX_PACKETS, stainfo->tx_packets) ||
remote_station_put_u64(skb, REMOTE_TX_BYTES, stainfo->tx_bytes) ||
nla_put_u32(skb, REMOTE_RX_PACKETS, stainfo->rx_packets) ||
remote_station_put_u64(skb, REMOTE_RX_BYTES, stainfo->rx_bytes) ||
nla_put_u8(skb, REMOTE_WMM, stainfo->isQosEnabled) ||
nla_put_u8(skb, REMOTE_SUPPORTED_MODE, stainfo->mode)) {
hdd_err("put fail");
goto fail;
}
if (txrx_rate) {
if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate) ||
nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate)) {
hdd_err("put fail");
goto fail;
} else {
hdd_info("tx_rate %x rx_rate %x",
stainfo->tx_rate, stainfo->rx_rate);
}
}
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) {
if (nla_put_u8(skb, REMOTE_AMPDU, stainfo->ampdu) ||
nla_put_u8(skb, REMOTE_TX_STBC, stainfo->tx_stbc) ||
nla_put_u8(skb, REMOTE_RX_STBC, stainfo->rx_stbc) ||
nla_put_u8(skb, REMOTE_CH_WIDTH, stainfo->ch_width) ||
nla_put_u8(skb, REMOTE_SGI_ENABLE, stainfo->sgi_enable)) {
hdd_err("put fail");
goto fail;
}
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd
* @wiphy: corestack handler
@ -4631,6 +4811,23 @@ __hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
status = hdd_get_station_info(hdd_ctx, adapter);
} else if (tb[STATION_ASSOC_FAIL_REASON]) {
status = hdd_get_station_assoc_fail(hdd_ctx, adapter);
} else if (tb[STATION_REMOTE]) {
struct qdf_mac_addr mac_addr;
if (adapter->device_mode != QDF_SAP_MODE &&
adapter->device_mode != QDF_P2P_GO_MODE) {
hdd_err("invalid device_mode:%d", adapter->device_mode);
status = -EINVAL;
goto out;
}
nla_memcpy(mac_addr.bytes, tb[STATION_REMOTE],
QDF_MAC_ADDR_SIZE);
hdd_debug("STATION_REMOTE " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(mac_addr.bytes));
status = hdd_get_station_remote(hdd_ctx, adapter, mac_addr);
} else {
hdd_err("get station info cmd type failed");
status = -EINVAL;
@ -4675,7 +4872,34 @@ hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
#undef STATION_INVALID
#undef STATION_INFO
#undef STATION_ASSOC_FAIL_REASON
#undef STATION_REMOTE
#undef STATION_MAX
#undef LINK_INFO_STANDARD_NL80211_ATTR
#undef AP_INFO_STANDARD_NL80211_ATTR
#undef INFO_ROAM_COUNT
#undef INFO_AKM
#undef WLAN802_11_MODE
#undef AP_INFO_HS20_INDICATION
#undef HT_OPERATION
#undef VHT_OPERATION
#undef INFO_ASSOC_FAIL_REASON
#undef REMOTE_MAX_PHY_RATE
#undef REMOTE_TX_PACKETS
#undef REMOTE_TX_BYTES
#undef REMOTE_RX_PACKETS
#undef REMOTE_RX_BYTES
#undef REMOTE_LAST_TX_RATE
#undef REMOTE_LAST_RX_RATE
#undef REMOTE_WMM
#undef REMOTE_SUPPORTED_MODE
#undef REMOTE_AMPDU
#undef REMOTE_TX_STBC
#undef REMOTE_RX_STBC
#undef REMOTE_CH_WIDTH
#undef REMOTE_SGI_ENABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#undef REMOTE_PAD
#endif
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
/**

View file

@ -105,6 +105,91 @@
#define SAP_24GHZ_CH_COUNT (14)
#define ACS_SCAN_EXPIRY_TIMEOUT_S 4
/*
* 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 index_data_rate_type 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 const 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 const 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} }
};
/* MCS Based VHT rate table */
/* MCS parameters with Nss = 1*/
static const 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 const 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} }
};
/* Function definitions */
@ -1044,6 +1129,205 @@ static QDF_STATUS hdd_handle_acs_scan_event(tpSap_Event sap_event,
}
#endif
/**
* get_max_rate_vht() - calculate max rate for VHT mode
* @nss: num of streams
* @ch_width: channel width
* @sgi: short gi
* @vht_mcs_map: vht mcs map
*
* This function calculate max rate for VHT mode
*
* Return: max rate
*/
static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map)
{
const struct index_vht_data_rate_type *supported_vht_mcs_rate;
enum data_rate_11ac_max_mcs vht_max_mcs;
int maxrate = 0;
int maxidx;
if (nss == 1) {
supported_vht_mcs_rate = supported_vht_mcs_rate_nss1;
} else if (nss == 2) {
supported_vht_mcs_rate = supported_vht_mcs_rate_nss2;
} else {
/* Not Supported */
hdd_err("nss %d not supported", nss);
return maxrate;
}
vht_max_mcs =
(enum data_rate_11ac_max_mcs)
(vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) {
maxidx = 7;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) {
maxidx = 8;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) {
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ)
/* MCS9 is not valid for VHT20 when nss=1,2 */
maxidx = 8;
else
maxidx = 9;
} else {
hdd_err("vht mcs map %x not supported",
vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
return maxrate;
}
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT20_rate[sgi];
} else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT40_rate[sgi];
} else if (ch_width == eHT_CHANNEL_WIDTH_80MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT80_rate[sgi];
} else {
hdd_err("ch_width %d not supported", ch_width);
return maxrate;
}
return maxrate;
}
/**
* calculate_max_phy_rate() - calcuate maximum phy rate (100kbps)
* @mode: phymode: Legacy, 11a/b/g, HT, VHT
* @nss: num of stream (maximum num is 2)
* @ch_width: channel width
* @sgi: short gi enabled or not
* @supp_idx: max supported idx
* @ext_idx: max extended idx
* @ht_mcs_idx: max mcs index for HT
* @vht_mcs_map: mcs map for VHT
*
* return: maximum phy rate in 100kbps
*/
static int calcuate_max_phy_rate(int mode, int nss, int ch_width,
int sgi, int supp_idx, int ext_idx,
int ht_mcs_idx, int vht_mcs_map)
{
const struct index_data_rate_type *supported_mcs_rate;
int maxidx = 12; /*default 6M mode*/
int maxrate = 0, tmprate;
int i;
/* check supported rates */
if (supp_idx != 0xff && maxidx < supp_idx)
maxidx = supp_idx;
/* check extended rates */
if (ext_idx != 0xff && maxidx < ext_idx)
maxidx = ext_idx;
for (i = 0; i < QDF_ARRAY_SIZE(supported_data_rate); i++) {
if (supported_data_rate[i].beacon_rate_index == maxidx)
maxrate = supported_data_rate[i].supported_rate[0];
}
if (mode == SIR_SME_PHY_MODE_HT) {
/* check for HT Mode */
maxidx = ht_mcs_idx;
if (nss == 1) {
supported_mcs_rate = supported_mcs_rate_nss1;
} else if (nss == 2) {
supported_mcs_rate = supported_mcs_rate_nss2;
} else {
/* Not Supported */
hdd_err("nss %d not supported", nss);
return maxrate;
}
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) {
tmprate = sgi ?
supported_mcs_rate[maxidx].supported_rate[2] :
supported_mcs_rate[maxidx].supported_rate[0];
} else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) {
tmprate = sgi ?
supported_mcs_rate[maxidx].supported_rate[3] :
supported_mcs_rate[maxidx].supported_rate[1];
} else {
hdd_err("invalid mode %d ch_width %d",
mode, ch_width);
return maxrate;
}
if (maxrate < tmprate)
maxrate = tmprate;
}
if (mode == SIR_SME_PHY_MODE_VHT) {
/* check for VHT Mode */
tmprate = get_max_rate_vht(nss, ch_width, sgi, vht_mcs_map);
if (maxrate < tmprate)
maxrate = tmprate;
}
return maxrate;
}
/**
* hdd_fill_station_info() - fill stainfo once connected
* @stainfo: peer stainfo associate to SAP
* @event: associate/reassociate event received
*
* The function is to update rate stats to stainfo
*
* Return: None.
*/
static void hdd_fill_station_info(hdd_station_info_t *stainfo,
tSap_StationAssocReassocCompleteEvent *event)
{
stainfo->staType = event->staType;
stainfo->nss = event->chan_info.nss;
stainfo->rate_flags = event->chan_info.rate_flags;
stainfo->ampdu = event->ampdu;
stainfo->sgi_enable = event->sgi_enable;
stainfo->tx_stbc = event->tx_stbc;
stainfo->rx_stbc = event->rx_stbc;
stainfo->ch_width = event->ch_width;
stainfo->mode = event->mode;
stainfo->max_supp_idx = event->max_supp_idx;
stainfo->max_ext_idx = event->max_ext_idx;
stainfo->max_mcs_idx = event->max_mcs_idx;
stainfo->rx_mcs_map = event->rx_mcs_map;
stainfo->tx_mcs_map = event->tx_mcs_map;
stainfo->assoc_ts = qdf_system_ticks();
stainfo->max_phy_rate =
calcuate_max_phy_rate(stainfo->mode,
stainfo->nss,
stainfo->ch_width,
stainfo->sgi_enable,
stainfo->max_supp_idx,
stainfo->max_ext_idx,
stainfo->max_mcs_idx,
stainfo->rx_mcs_map);
/* expect max_phy_rate report in kbps */
stainfo->max_phy_rate *= 100;
hdd_debug("cap %d %d %d %d %d %d %d %d %d %x %d",
stainfo->ampdu,
stainfo->sgi_enable,
stainfo->tx_stbc,
stainfo->rx_stbc,
stainfo->isQosEnabled,
stainfo->ch_width,
stainfo->mode,
event->wmmEnabled,
event->chan_info.nss,
event->chan_info.rate_flags,
stainfo->max_phy_rate);
hdd_debug("rate info %d %d %d %d %d",
stainfo->max_supp_idx,
stainfo->max_ext_idx,
stainfo->max_mcs_idx,
stainfo->rx_mcs_map,
stainfo->tx_mcs_map);
}
QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
void *usrDataForCallback)
{
@ -1075,6 +1359,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
hdd_adapter_t *con_sap_adapter;
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct hdd_chan_change_params chan_change;
tSap_StationAssocReassocCompleteEvent *event;
int ret = 0;
struct ch_params sap_ch_param = {0};
eCsrPhyMode phy_mode;
@ -1533,10 +1818,10 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
case eSAP_STA_ASSOC_EVENT:
case eSAP_STA_REASSOC_EVENT:
event = &pSapEvent->sapevt.sapStationAssocReassocCompleteEvent;
wrqu.addr.sa_family = ARPHRD_ETHER;
memcpy(wrqu.addr.sa_data,
&pSapEvent->sapevt.sapStationAssocReassocCompleteEvent.
staMac, QDF_MAC_ADDR_SIZE);
&event->staMac, QDF_MAC_ADDR_SIZE);
hdd_notice(" associated " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(wrqu.addr.sa_data));
we_event = IWEVREGISTERED;
@ -1558,14 +1843,10 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
pHostapdAdapter,
true,
pHddApCtx->uPrivacy,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staId, 0, 0,
event->staId, 0, 0,
(struct qdf_mac_addr *)
wrqu.addr.sa_data,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
wmmEnabled);
event->wmmEnabled);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("Failed to register STA %d "
MAC_ADDRESS_STR "", qdf_status,
@ -1575,47 +1856,29 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
pHostapdAdapter,
false,
pHddApCtx->uPrivacy,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staId, 0, 0,
event->staId, 0, 0,
(struct qdf_mac_addr *)
wrqu.addr.sa_data,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
wmmEnabled);
event->wmmEnabled);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("Failed to register STA %d "
MAC_ADDRESS_STR "", qdf_status,
MAC_ADDR_ARRAY(wrqu.addr.sa_data));
}
staId =
pSapEvent->sapevt.sapStationAssocReassocCompleteEvent.staId;
staId = event->staId;
if (QDF_IS_STATUS_SUCCESS(qdf_status)) {
pHostapdAdapter->aStaInfo[staId].nss =
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
chan_info.nss;
pHostapdAdapter->aStaInfo[staId].rate_flags =
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
chan_info.rate_flags;
pHostapdAdapter->aStaInfo[staId].staType =
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staType;
hdd_fill_station_info(
&pHostapdAdapter->aStaInfo[staId],
event);
hdd_debug("hdd_hostapd_sap_event_cb, StaID: %d, StaType: %d",
staId, pHostapdAdapter->aStaInfo[staId].staType);
}
if (hdd_ipa_is_enabled(pHddCtx)) {
status = hdd_ipa_wlan_evt(pHostapdAdapter,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staId, HDD_IPA_CLIENT_CONNECT_EX,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staMac.bytes);
event->staId, HDD_IPA_CLIENT_CONNECT_EX,
event->staMac.bytes);
if (status) {
hdd_err("WLAN_CLIENT_CONNECT_EX event failed");
goto stopbss;
@ -1666,9 +1929,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
HDD_SAP_WAKE_LOCK_DURATION);
{
struct station_info *sta_info;
uint16_t iesLen =
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.iesLen;
uint16_t iesLen = event->iesLen;
sta_info = qdf_mem_malloc(sizeof(*sta_info));
if (!sta_info) {
@ -1677,8 +1938,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
}
if (iesLen <= MAX_ASSOC_IND_IE_LEN) {
sta_info->assoc_req_ies =
(const u8 *)&pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.ies[0];
(const u8 *)&event->ies[0];
sta_info->assoc_req_ies_len = iesLen;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
/*
@ -1690,10 +1950,8 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
sta_info->filled |= STATION_INFO_ASSOC_REQ_IES;
#endif
cfg80211_new_sta(dev,
(const u8 *)&pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staMac.bytes[0], sta_info,
GFP_KERNEL);
(const u8 *)&event->staMac.bytes[0],
sta_info, GFP_KERNEL);
} else {
hdd_err("Assoc Ie length is too long");
}
@ -1707,31 +1965,24 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
}
if (pHostapdAdapter->device_mode == QDF_P2P_GO_MODE) {
/* send peer status indication to oem app */
hdd_send_peer_status_ind_to_app(&pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staMac, ePeerConnected,
pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
timingMeasCap,
pHostapdAdapter->sessionId,
&pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
chan_info,
pHostapdAdapter->device_mode);
hdd_send_peer_status_ind_to_app(
&event->staMac,
ePeerConnected,
event->timingMeasCap,
pHostapdAdapter->sessionId,
&event->chan_info,
pHostapdAdapter->device_mode);
}
ret = hdd_objmgr_add_peer_object(
pHostapdAdapter->hdd_vdev,
pHostapdAdapter->device_mode,
pSapEvent->sapevt.sapStationAssocReassocCompleteEvent.
staMac.bytes,
(pHostapdAdapter->aStaInfo[staId].staType
event->staMac.bytes,
(pHostapdAdapter->aStaInfo[event->staId].staType
== eSTA_TYPE_P2P_CLI));
if (ret)
hdd_err("Peer object "MAC_ADDRESS_STR" add fails!",
MAC_ADDR_ARRAY(pSapEvent->sapevt.
sapStationAssocReassocCompleteEvent.
staMac.bytes));
MAC_ADDR_ARRAY(event->staMac.bytes));
hdd_green_ap_add_sta(pHddCtx);
break;
@ -5397,6 +5648,120 @@ iw_get_softap_linkspeed(struct net_device *dev,
return ret;
}
/**
* __iw_get_peer_rssi() - get station's rssi
* @dev: net device
* @info: iwpriv request information
* @wrqu: iwpriv command parameter
* @extra
*
* This function will call wlan_hdd_get_peer_rssi
* to get rssi
*
* Return: 0 on success, otherwise error value
*/
static int
__iw_get_peer_rssi(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
hdd_adapter_t *adapter = netdev_priv(dev);
hdd_context_t *hddctx;
char macaddrarray[MAC_ADDRESS_STR_LEN];
struct qdf_mac_addr macaddress = QDF_MAC_ADDR_BROADCAST_INITIALIZER;
int ret;
char *rssi_info_output = extra;
struct sir_peer_sta_info peer_sta_info;
struct sir_peer_info *rssi_info;
int i;
int buf;
int length;
ENTER();
hddctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hddctx);
if (ret != 0)
return ret;
ret = hdd_check_private_wext_control(hddctx, info);
if (0 != ret)
return ret;
hdd_debug("wrqu->data.length= %d", wrqu->data.length);
if (wrqu->data.length >= MAC_ADDRESS_STR_LEN - 1) {
if (copy_from_user(macaddrarray,
wrqu->data.pointer,
MAC_ADDRESS_STR_LEN - 1)) {
hdd_info("failed to copy data from user buffer");
return -EFAULT;
}
macaddrarray[MAC_ADDRESS_STR_LEN - 1] = '\0';
hdd_debug("%s", macaddrarray);
if (!mac_pton(macaddrarray, macaddress.bytes))
hdd_err("String to Hex conversion Failed");
}
ret = wlan_hdd_get_peer_rssi(adapter, &macaddress, &peer_sta_info);
if (ret) {
hdd_err("Unable to retrieve peer rssi: %d", ret);
return ret;
}
/*
* The iwpriv tool default print is before mac addr and rssi.
* Add '\n' before first rssi item to align the first rssi item
* with others
*
* wlan getRSSI:
* [macaddr1] [rssi1]
* [macaddr2] [rssi2]
* [macaddr3] [rssi3]
*/
length = scnprintf(rssi_info_output, WE_MAX_STR_LEN, "\n");
rssi_info = &peer_sta_info.info[0];
for (i = 0; i < peer_sta_info.sta_num; i++) {
buf = scnprintf
(
rssi_info_output + length, WE_MAX_STR_LEN - length,
"[%pM] [%d]\n",
rssi_info[i].peer_macaddr.bytes,
rssi_info[i].rssi
);
length += buf;
}
wrqu->data.length = length + 1;
EXIT();
return 0;
}
/**
* iw_get_peer_rssi() - get station's rssi
* @dev: net device
* @info: iwpriv request information
* @wrqu: iwpriv command parameter
* @extra
*
* This function will call __iw_get_peer_rssi
*
* Return: 0 on success, otherwise error value
*/
static int
iw_get_peer_rssi(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
int ret;
cds_ssr_protect(__func__);
ret = __iw_get_peer_rssi(dev, info, wrqu, extra);
cds_ssr_unprotect(__func__);
return ret;
}
static const iw_handler hostapd_handler[] = {
(iw_handler) NULL, /* SIOCSIWCOMMIT */
(iw_handler) NULL, /* SIOCGIWNAME */
@ -5804,6 +6169,11 @@ static const struct iw_priv_args hostapd_private_args[] = {
IW_PRIV_TYPE_CHAR | 18,
IW_PRIV_TYPE_CHAR | 5, "getLinkSpeed"
}
, {
QCSAP_IOCTL_PRIV_GET_RSSI,
IW_PRIV_TYPE_CHAR | 18,
IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN, "getRSSI"
}
, {
QCSAP_IOCTL_PRIV_SET_THREE_INT_GET_NONE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, ""
@ -6013,6 +6383,8 @@ static const iw_handler hostapd_private[] = {
[QCSAP_IOCTL_PRIV_GET_SOFTAP_LINK_SPEED -
SIOCIWFIRSTPRIV] =
iw_get_softap_linkspeed,
[QCSAP_IOCTL_PRIV_GET_RSSI - SIOCIWFIRSTPRIV] =
iw_get_peer_rssi,
[QCSAP_IOCTL_SET_TX_POWER - SIOCIWFIRSTPRIV] =
iw_softap_set_tx_power,
[QCSAP_IOCTL_SET_MAX_TX_POWER - SIOCIWFIRSTPRIV] =

View file

@ -960,6 +960,7 @@ static void hdd_update_tgt_services(hdd_context_t *hdd_ctx,
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
config->isRoamOffloadEnabled &= cfg->en_roam_offload;
#endif
config->sap_get_peer_info &= cfg->get_peer_info_enabled;
sme_update_tgt_services(hdd_ctx->hHal, cfg);
}
@ -9823,6 +9824,13 @@ int hdd_wlan_startup(struct device *dev)
set_value, PDEV_CMD);
}
if (hdd_ctx->config->sap_get_peer_info) {
set_value = hdd_ctx->config->sap_get_peer_info;
sme_cli_set_command(0,
(int)WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE,
set_value, PDEV_CMD);
}
num_11b_tx_chains = hdd_ctx->config->num_11b_tx_chains;
num_11ag_tx_chains = hdd_ctx->config->num_11ag_tx_chains;
if (!hdd_ctx->config->enable2x2) {

View file

@ -256,6 +256,7 @@ static int __hdd_softap_hard_start_xmit(struct sk_buff *skb,
hdd_ap_ctx_t *pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
struct qdf_mac_addr *pDestMacAddress;
uint8_t STAId;
uint32_t num_seg;
++pAdapter->hdd_stats.hddTxRxStats.txXmitCalled;
/* Prevent this function from being called during SSR since TL
@ -395,11 +396,17 @@ static int __hdd_softap_hard_start_xmit(struct sk_buff *skb,
qdf_net_buf_debug_acquire_skb(skb, __FILE__, __LINE__);
pAdapter->stats.tx_bytes += skb->len;
pAdapter->aStaInfo[STAId].tx_bytes += skb->len;
if (qdf_nbuf_is_tso(skb))
pAdapter->stats.tx_packets += qdf_nbuf_get_tso_num_seg(skb);
else
if (qdf_nbuf_is_tso(skb)) {
num_seg = qdf_nbuf_get_tso_num_seg(skb);
pAdapter->stats.tx_packets += num_seg;
pAdapter->aStaInfo[STAId].tx_packets += num_seg;
} else {
++pAdapter->stats.tx_packets;
pAdapter->aStaInfo[STAId].tx_packets++;
}
pAdapter->aStaInfo[STAId].last_tx_rx_ts = qdf_system_ticks();
hdd_event_eapol_log(skb, QDF_TX);
qdf_dp_trace_log_pkt(pAdapter->sessionId, skb, QDF_TX,
@ -686,6 +693,8 @@ QDF_STATUS hdd_softap_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
struct sk_buff *skb = NULL;
struct sk_buff *next = NULL;
hdd_context_t *pHddCtx = NULL;
struct qdf_mac_addr src_mac;
uint8_t staid;
/* Sanity check on inputs */
if (unlikely((NULL == context) || (NULL == rxBuf))) {
@ -737,6 +746,17 @@ QDF_STATUS hdd_softap_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
++pAdapter->stats.rx_packets;
pAdapter->stats.rx_bytes += skb->len;
qdf_mem_copy(&src_mac, skb->data + QDF_NBUF_SRC_MAC_OFFSET,
sizeof(src_mac));
if (QDF_STATUS_SUCCESS ==
hdd_softap_get_sta_id(pAdapter, &src_mac, &staid)) {
if (staid < WLAN_MAX_STA_COUNT) {
pAdapter->aStaInfo[staid].rx_packets++;
pAdapter->aStaInfo[staid].rx_bytes += skb->len;
pAdapter->aStaInfo[staid].last_tx_rx_ts =
qdf_system_ticks();
}
}
hdd_event_eapol_log(skb, QDF_RX);
DPTRACE(qdf_dp_trace(skb,
QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD,

View file

@ -45,68 +45,22 @@
* 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
}
}
static const struct index_data_rate_type 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[] = {
@ -3083,6 +3037,840 @@ wlan_hdd_get_sap_stats(hdd_adapter_t *adapter, struct station_info *info)
return 0;
}
/**
* hdd_get_max_rate_legacy() - get max rate for legacy mode
* @stainfo: stainfo pointer
* @rssidx: rssi index
*
* This function will get max rate for legacy mode
*
* Return: max rate on success, otherwise 0
*/
static uint32_t hdd_get_max_rate_legacy(hdd_station_info_t *stainfo,
uint8_t rssidx)
{
uint32_t maxrate = 0;
/*Minimum max rate, 6Mbps*/
int maxidx = 12;
int i;
/* check supported rates */
if (stainfo->max_supp_idx != 0xff &&
maxidx < stainfo->max_supp_idx)
maxidx = stainfo->max_supp_idx;
/* check extended rates */
if (stainfo->max_ext_idx != 0xff &&
maxidx < stainfo->max_ext_idx)
maxidx = stainfo->max_ext_idx;
for (i = 0; QDF_ARRAY_SIZE(supported_data_rate); i++) {
if (supported_data_rate[i].beacon_rate_index == maxidx)
maxrate =
supported_data_rate[i].supported_rate[rssidx];
}
hdd_debug("maxrate %d", maxrate);
return maxrate;
}
/**
* hdd_get_max_rate_ht() - get max rate for ht mode
* @stainfo: stainfo pointer
* @stats: fw txrx status pointer
* @rate_flags: rate flags
* @nss: number of streams
* @maxrate: returned max rate buffer pointer
* @max_mcs_idx: max mcs idx
* @report_max: report max rate or actual rate
*
* This function will get max rate for ht mode
*
* Return: None
*/
static void hdd_get_max_rate_ht(hdd_station_info_t *stainfo,
struct hdd_fw_txrx_stats *stats,
uint32_t rate_flags,
uint8_t nss,
uint32_t *maxrate,
uint8_t *max_mcs_idx,
bool report_max)
{
struct index_data_rate_type *supported_mcs_rate;
uint32_t tmprate;
uint8_t flag = 0, mcsidx;
int8_t rssi = stats->rssi;
int mode;
int i;
if (rate_flags & eHAL_TX_RATE_HT40)
mode = 1;
else
mode = 0;
if (rate_flags & eHAL_TX_RATE_HT40)
flag |= 1;
if (rate_flags & eHAL_TX_RATE_SGI)
flag |= 2;
supported_mcs_rate = (struct index_data_rate_type *)
((nss == 1) ? &supported_mcs_rate_nss1 :
&supported_mcs_rate_nss2);
if (stainfo->max_mcs_idx == 0xff) {
hdd_err("invalid max_mcs_idx");
/* report real mcs idx */
mcsidx = stats->tx_rate.mcs;
} else {
mcsidx = stainfo->max_mcs_idx;
}
if (!report_max) {
for (i = 0; i < mcsidx; i++) {
if (rssi <= rssi_mcs_tbl[mode][i]) {
mcsidx = i;
break;
}
}
if (mcsidx < stats->tx_rate.mcs)
mcsidx = stats->tx_rate.mcs;
}
tmprate = supported_mcs_rate[mcsidx].supported_rate[flag];
hdd_debug("tmprate %d mcsidx %d", tmprate, mcsidx);
*maxrate = tmprate;
*max_mcs_idx = mcsidx;
}
/**
* hdd_get_max_rate_vht() - get max rate for vht mode
* @stainfo: stainfo pointer
* @stats: fw txrx status pointer
* @rate_flags: rate flags
* @nss: number of streams
* @maxrate: returned max rate buffer pointer
* @max_mcs_idx: max mcs idx
* @report_max: report max rate or actual rate
*
* This function will get max rate for vht mode
*
* Return: None
*/
static void hdd_get_max_rate_vht(hdd_station_info_t *stainfo,
struct hdd_fw_txrx_stats *stats,
uint32_t rate_flags,
uint8_t nss,
uint32_t *maxrate,
uint8_t *max_mcs_idx,
bool report_max)
{
struct index_vht_data_rate_type *supported_vht_mcs_rate;
uint32_t tmprate = 0;
uint32_t vht_max_mcs;
uint8_t flag = 0, mcsidx = INVALID_MCS_IDX;
int8_t rssi = stats->rssi;
int mode;
int i;
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)
mode = 1;
else
mode = 0;
if (rate_flags &
(eHAL_TX_RATE_VHT20 | eHAL_TX_RATE_VHT40 | eHAL_TX_RATE_VHT80)) {
vht_max_mcs =
(enum data_rate_11ac_max_mcs)
(stainfo->tx_mcs_map & DATA_RATE_11AC_MCS_MASK);
if (rate_flags & eHAL_TX_RATE_SGI)
flag |= 1;
if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) {
mcsidx = 7;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) {
mcsidx = 8;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) {
/*
* 'IEEE_P802.11ac_2013.pdf' page 325, 326
* - MCS9 is valid for VHT20 when Nss = 3 or Nss = 6
* - MCS9 is not valid for VHT20 when Nss = 1,2,4,5,7,8
*/
if ((rate_flags & eHAL_TX_RATE_VHT20) &&
(nss != 3 && nss != 6))
mcsidx = 8;
else
mcsidx = 9;
} else {
hdd_err("invalid vht_max_mcs");
/* report real mcs idx */
mcsidx = stats->tx_rate.mcs;
}
if (!report_max) {
for (i = 0; i <= mcsidx; i++) {
if (rssi <= rssi_mcs_tbl[mode][i]) {
mcsidx = i;
break;
}
}
if (mcsidx < stats->tx_rate.mcs)
mcsidx = stats->tx_rate.mcs;
}
if (rate_flags & eHAL_TX_RATE_VHT80)
tmprate =
supported_vht_mcs_rate[mcsidx].supported_VHT80_rate[flag];
else if (rate_flags & eHAL_TX_RATE_VHT40)
tmprate =
supported_vht_mcs_rate[mcsidx].supported_VHT40_rate[flag];
else if (rate_flags & eHAL_TX_RATE_VHT20)
tmprate =
supported_vht_mcs_rate[mcsidx].supported_VHT20_rate[flag];
}
hdd_debug("tmprate %d mcsidx %d", tmprate, mcsidx);
*maxrate = tmprate;
*max_mcs_idx = mcsidx;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
/**
* hdd_fill_bw_mcs() - fill ch width and mcs flags
* @stainfo: stainfo pointer
* @rate_flags: HDD rate flags
* @mcsidx: mcs index
* @nss: number of streams
* @vht: vht mode or not
*
* This function will fill ch width and mcs flags
*
* Return: None
*/
static void hdd_fill_bw_mcs(struct station_info *sinfo,
uint8_t rate_flags,
uint8_t mcsidx,
uint8_t nss,
bool vht)
{
if (vht) {
sinfo->txrate.nss = nss;
sinfo->txrate.mcs = mcsidx;
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
if (rate_flags & eHAL_TX_RATE_VHT80)
sinfo->txrate.bw = RATE_INFO_BW_80;
else if (rate_flags & eHAL_TX_RATE_VHT40)
sinfo->txrate.bw = RATE_INFO_BW_40;
else if (rate_flags & eHAL_TX_RATE_VHT20)
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
} else {
sinfo->txrate.mcs = (nss - 1) << 3;
sinfo->txrate.mcs |= mcsidx;
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
if (rate_flags & eHAL_TX_RATE_HT40)
sinfo->txrate.bw = RATE_INFO_BW_40;
}
}
#else
/**
* hdd_fill_bw_mcs() - fill ch width and mcs flags
* @stainfo: stainfo pointer
* @rate_flags: HDD rate flags
* @mcsidx: mcs index
* @nss: number of streams
* @vht: vht mode or not
*
* This function will fill ch width and mcs flags
*
* Return: None
*/
static void hdd_fill_bw_mcs(struct station_info *sinfo,
uint8_t rate_flags,
uint8_t mcsidx,
uint8_t nss,
bool vht)
{
if (vht) {
sinfo->txrate.nss = nss;
sinfo->txrate.mcs = mcsidx;
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;
else if (rate_flags & eHAL_TX_RATE_VHT20)
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
} else {
sinfo->txrate.mcs = (nss - 1) << 3;
sinfo->txrate.mcs |= mcsidx;
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
if (rate_flags & eHAL_TX_RATE_HT40)
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
}
}
#endif
/**
* hdd_fill_bw_mcs_vht() - fill ch width and mcs flags for VHT mode
* @stainfo: stainfo pointer
* @rate_flags: HDD rate flags
* @mcsidx: mcs index
* @nss: number of streams
*
* This function will fill ch width and mcs flags for VHT mode
*
* Return: None
*/
static void hdd_fill_bw_mcs_vht(struct station_info *sinfo,
uint8_t rate_flags,
uint8_t mcsidx,
uint8_t nss)
{
hdd_fill_bw_mcs(sinfo, rate_flags, mcsidx, nss, true);
}
/**
* hdd_fill_sinfo_rate_info() - fill rate info of sinfo struct
* @sinfo: station_info struct pointer
* @rate_flags: HDD rate flags
* @mcsidx: mcs index
* @nss: number of streams
* @maxrate: data rate (kbps)
*
* This function will fill rate info of sinfo struct
*
* Return: None
*/
static void hdd_fill_sinfo_rate_info(struct station_info *sinfo,
uint32_t rate_flags,
uint8_t mcsidx,
uint8_t nss,
uint32_t maxrate)
{
if (rate_flags & eHAL_TX_RATE_LEGACY) {
/* provide to the UI in units of 100kbps */
sinfo->txrate.legacy = maxrate;
} else {
/* must be MCS */
if (rate_flags &
(eHAL_TX_RATE_VHT80 |
eHAL_TX_RATE_VHT40 |
eHAL_TX_RATE_VHT20))
hdd_fill_bw_mcs_vht(sinfo, rate_flags, mcsidx, nss);
if (rate_flags & (eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40))
hdd_fill_bw_mcs(sinfo, rate_flags, mcsidx, nss, false);
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;
}
}
hdd_info("flag %x mcs %d legacy %d nss %d",
sinfo->txrate.flags,
sinfo->txrate.mcs,
sinfo->txrate.legacy,
sinfo->txrate.nss);
}
/**
* hdd_fill_station_info_flags() - fill flags of sinfo struct
* @sinfo: station_info struct pointer
*
* This function will fill flags of sinfo struct
*
* Return: None
*/
static void hdd_fill_station_info_flags(struct station_info *sinfo)
{
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) |
BIT(NL80211_STA_INFO_TX_BYTES) |
BIT(NL80211_STA_INFO_TX_BYTES64) |
BIT(NL80211_STA_INFO_TX_BITRATE) |
BIT(NL80211_STA_INFO_TX_PACKETS) |
BIT(NL80211_STA_INFO_TX_RETRIES) |
BIT(NL80211_STA_INFO_TX_FAILED) |
BIT(NL80211_STA_INFO_RX_BYTES) |
BIT(NL80211_STA_INFO_RX_BYTES64) |
BIT(NL80211_STA_INFO_RX_PACKETS) |
BIT(NL80211_STA_INFO_INACTIVE_TIME) |
BIT(NL80211_STA_INFO_CONNECTED_TIME);
}
/**
* hdd_fill_rate_info() - fill rate info of sinfo
* @sinfo: station_info struct pointer
* @stainfo: stainfo pointer
* @stats: fw txrx status pointer
* @cfg: hdd config pointer
*
* This function will fill rate info of sinfo
*
* Return: None
*/
static void hdd_fill_rate_info(struct station_info *sinfo,
hdd_station_info_t *stainfo,
struct hdd_fw_txrx_stats *stats,
struct hdd_config *cfg)
{
uint8_t rate_flags;
uint8_t mcsidx = 0xff;
uint32_t myrate, maxrate, tmprate;
int rssidx;
int nss = 1;
hdd_info("reportMaxLinkSpeed %d", cfg->reportMaxLinkSpeed);
/* convert to 100kbps expected in rate table */
myrate = stats->tx_rate.rate / 100;
rate_flags = stainfo->rate_flags;
if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
nss = stainfo->nss;
if (eHDD_LINK_SPEED_REPORT_ACTUAL == cfg->reportMaxLinkSpeed) {
/* Get current rate flags if report actual */
if (stats->tx_rate.rate_flags)
rate_flags =
stats->tx_rate.rate_flags;
nss = stats->tx_rate.nss;
}
if (stats->tx_rate.mcs == INVALID_MCS_IDX)
rate_flags = eHAL_TX_RATE_LEGACY;
}
if (eHDD_LINK_SPEED_REPORT_ACTUAL != cfg->reportMaxLinkSpeed) {
/* we do not want to necessarily report the current speed */
if (eHDD_LINK_SPEED_REPORT_MAX == cfg->reportMaxLinkSpeed) {
/* report the max possible speed */
rssidx = 0;
} else if (eHDD_LINK_SPEED_REPORT_MAX_SCALED ==
cfg->reportMaxLinkSpeed) {
/* report the max possible speed with RSSI scaling */
if (stats->rssi >= cfg->linkSpeedRssiHigh) {
/* report the max possible speed */
rssidx = 0;
} else if (stats->rssi >=
cfg->linkSpeedRssiMid) {
/* report middle speed */
rssidx = 1;
} else if (stats->rssi >=
cfg->linkSpeedRssiLow) {
/* report middle speed */
rssidx = 2;
} else {
/* report actual speed */
rssidx = 3;
}
} else {
/* unknown, treat as eHDD_LINK_SPEED_REPORT_MAX */
hdd_err("Invalid value for reportMaxLinkSpeed: %u",
cfg->reportMaxLinkSpeed);
rssidx = 0;
}
maxrate = hdd_get_max_rate_legacy(stainfo, rssidx);
/*
* Get MCS Rate Set --
* Only if we are connected in non legacy mode and not
* reporting actual speed
*/
if ((rssidx != 3) &&
!(rate_flags & eHAL_TX_RATE_LEGACY)) {
hdd_get_max_rate_vht(stainfo,
stats,
rate_flags,
nss,
&tmprate,
&mcsidx,
rssidx == 0);
if (maxrate < tmprate &&
mcsidx != INVALID_MCS_IDX)
maxrate = tmprate;
if (mcsidx == INVALID_MCS_IDX)
hdd_get_max_rate_ht(stainfo,
stats,
rate_flags,
nss,
&tmprate,
&mcsidx,
rssidx == 0);
if (maxrate < tmprate &&
mcsidx != INVALID_MCS_IDX)
maxrate = tmprate;
} else if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
maxrate = myrate;
mcsidx = stats->tx_rate.mcs;
}
/*
* make sure we report a value at least as big as our
* current rate
*/
if ((maxrate < myrate) || (maxrate == 0)) {
maxrate = myrate;
if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
mcsidx = stats->tx_rate.mcs;
/*
* 'IEEE_P802.11ac_2013.pdf' page 325, 326
* - MCS9 is valid for VHT20 when Nss = 3 or
* Nss = 6
* - MCS9 is not valid for VHT20 when
* Nss = 1,2,4,5,7,8
*/
if ((rate_flags & eHAL_TX_RATE_VHT20) &&
(mcsidx > 8) &&
(nss != 3 && nss != 6))
mcsidx = 8;
}
}
} else {
/* report current rate instead of max rate */
maxrate = myrate;
if (!(rate_flags & eHAL_TX_RATE_LEGACY))
mcsidx = stats->tx_rate.mcs;
}
hdd_fill_sinfo_rate_info(sinfo,
rate_flags,
mcsidx,
nss,
maxrate);
}
/**
* wlan_hdd_fill_station_info() - fill station_info struct
* @sinfo: station_info struct pointer
* @stainfo: stainfo pointer
* @stats: fw txrx status pointer
* @cfg: hdd config pointer
*
* This function will fill station_info struct
*
* Return: None
*/
static void wlan_hdd_fill_station_info(struct station_info *sinfo,
hdd_station_info_t *stainfo,
struct hdd_fw_txrx_stats *stats,
struct hdd_config *cfg)
{
qdf_time_t curr_time, dur;
curr_time = qdf_system_ticks();
dur = curr_time - stainfo->assoc_ts;
sinfo->connected_time = qdf_system_ticks_to_msecs(dur) / 1000;
dur = curr_time - stainfo->last_tx_rx_ts;
sinfo->inactive_time = qdf_system_ticks_to_msecs(dur);
sinfo->signal = stats->rssi;
sinfo->tx_bytes = stats->tx_bytes;
sinfo->tx_packets = stats->tx_packets;
sinfo->rx_bytes = stats->rx_bytes;
sinfo->rx_packets = stats->rx_packets;
sinfo->tx_failed = stats->tx_failed;
sinfo->tx_retries = stats->tx_retries;
/* tx rate info */
hdd_fill_rate_info(sinfo, stainfo, stats, cfg);
hdd_fill_station_info_flags(sinfo);
/* dump sta info*/
hdd_info("dump stainfo");
hdd_info("con_time %d inact_time %d tx_pkts %d rx_pkts %d",
sinfo->connected_time, sinfo->inactive_time,
sinfo->tx_packets, sinfo->rx_packets);
hdd_info("failed %d retries %d tx_bytes %lld rx_bytes %lld",
sinfo->tx_failed, sinfo->tx_retries,
sinfo->tx_bytes, sinfo->rx_bytes);
hdd_info("rssi %d mcs %d legacy %d nss %d flags %x",
sinfo->signal, sinfo->txrate.mcs,
sinfo->txrate.legacy, sinfo->txrate.nss,
sinfo->txrate.flags);
}
/**
* hdd_get_rate_flags_ht() - get HT rate flags based on rate, nss and mcs
* @rate: Data rate (100 kbps)
* @nss: Number of streams
* @mcs: HT mcs index
*
* This function is used to construct HT rate flag with rate, nss and mcs
*
* Return: rate flags for success, 0 on failure.
*/
static uint8_t hdd_get_rate_flags_ht(uint32_t rate,
uint8_t nss,
uint8_t mcs)
{
struct index_data_rate_type *mcs_rate;
uint8_t flags = 0;
mcs_rate = (struct index_data_rate_type *)
((nss == 1) ? &supported_mcs_rate_nss1 :
&supported_mcs_rate_nss2);
if (rate == mcs_rate[mcs].supported_rate[0]) {
flags |= eHAL_TX_RATE_HT20;
} else if (rate == mcs_rate[mcs].supported_rate[1]) {
flags |= eHAL_TX_RATE_HT40;
} else if (rate == mcs_rate[mcs].supported_rate[2]) {
flags |= eHAL_TX_RATE_HT20;
flags |= eHAL_TX_RATE_SGI;
} else if (rate == mcs_rate[mcs].supported_rate[3]) {
flags |= eHAL_TX_RATE_HT40;
flags |= eHAL_TX_RATE_SGI;
} else {
hdd_err("invalid params rate %d nss %d mcs %d",
rate, nss, mcs);
}
return flags;
}
/**
* hdd_get_rate_flags_vht() - get VHT rate flags based on rate, nss and mcs
* @rate: Data rate (100 kbps)
* @nss: Number of streams
* @mcs: VHT mcs index
*
* This function is used to construct VHT rate flag with rate, nss and mcs
*
* Return: rate flags for success, 0 on failure.
*/
static uint8_t hdd_get_rate_flags_vht(uint32_t rate,
uint8_t nss,
uint8_t mcs)
{
struct index_vht_data_rate_type *mcs_rate;
uint8_t flags = 0;
mcs_rate = (struct index_vht_data_rate_type *)
((nss == 1) ?
&supported_vht_mcs_rate_nss1 :
&supported_vht_mcs_rate_nss2);
if (rate == mcs_rate[mcs].supported_VHT80_rate[0]) {
flags |= eHAL_TX_RATE_VHT80;
} else if (rate == mcs_rate[mcs].supported_VHT80_rate[1]) {
flags |= eHAL_TX_RATE_VHT80;
flags |= eHAL_TX_RATE_SGI;
} else if (rate == mcs_rate[mcs].supported_VHT40_rate[0]) {
flags |= eHAL_TX_RATE_VHT40;
} else if (rate == mcs_rate[mcs].supported_VHT40_rate[1]) {
flags |= eHAL_TX_RATE_VHT40;
flags |= eHAL_TX_RATE_SGI;
} else if (rate == mcs_rate[mcs].supported_VHT20_rate[0]) {
flags |= eHAL_TX_RATE_VHT20;
} else if (rate == mcs_rate[mcs].supported_VHT20_rate[1]) {
flags |= eHAL_TX_RATE_VHT20;
flags |= eHAL_TX_RATE_SGI;
} else {
hdd_err("invalid params rate %d nss %d mcs %d",
rate, nss, mcs);
}
return flags;
}
/**
* hdd_get_rate_flags() - get HT/VHT rate flags based on rate, nss and mcs
* @rate: Data rate (100 kbps)
* @mode: Tx/Rx mode
* @nss: Number of streams
* @mcs: Mcs index
*
* This function is used to construct rate flag with rate, nss and mcs
*
* Return: rate flags for success, 0 on failure.
*/
static uint8_t hdd_get_rate_flags(uint32_t rate,
uint8_t mode,
uint8_t nss,
uint8_t mcs)
{
uint8_t flags = 0;
if (mode == SIR_SME_PHY_MODE_HT)
flags = hdd_get_rate_flags_ht(rate, nss, mcs);
else if (mode == SIR_SME_PHY_MODE_VHT)
flags = hdd_get_rate_flags_vht(rate, nss, mcs);
else
hdd_err("invalid mode param %d", mode);
return flags;
}
/**
* wlan_hdd_fill_rate_info() - fill HDD rate info from SIR peer info
* @ap_ctx: AP Context
* @peer_info: SIR peer info pointer
*
* This function is used to fill HDD rate info rom SIR peer info
*
* Return: None
*/
static void wlan_hdd_fill_rate_info(hdd_ap_ctx_t *ap_ctx,
struct sir_peer_info_ext *peer_info)
{
uint8_t flags;
uint32_t rate_code;
/* tx rate info */
ap_ctx->txrx_stats.tx_rate.rate = peer_info->tx_rate;
rate_code = peer_info->tx_rate_code;
if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) ==
WMI_RATE_PREAMBLE_HT)
ap_ctx->txrx_stats.tx_rate.mode = SIR_SME_PHY_MODE_HT;
else if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) ==
WMI_RATE_PREAMBLE_VHT)
ap_ctx->txrx_stats.tx_rate.mode = SIR_SME_PHY_MODE_VHT;
else
ap_ctx->txrx_stats.tx_rate.mode = SIR_SME_PHY_MODE_LEGACY;
ap_ctx->txrx_stats.tx_rate.nss =
WMI_GET_HW_RATECODE_NSS_V1(rate_code) + 1;
ap_ctx->txrx_stats.tx_rate.mcs =
WMI_GET_HW_RATECODE_RATE_V1(rate_code);
flags = hdd_get_rate_flags(ap_ctx->txrx_stats.tx_rate.rate / 100,
ap_ctx->txrx_stats.tx_rate.mode,
ap_ctx->txrx_stats.tx_rate.nss,
ap_ctx->txrx_stats.tx_rate.mcs);
ap_ctx->txrx_stats.tx_rate.rate_flags = flags;
hdd_debug("tx: mode %d nss %d mcs %d rate_flags %x flags %x",
ap_ctx->txrx_stats.tx_rate.mode,
ap_ctx->txrx_stats.tx_rate.nss,
ap_ctx->txrx_stats.tx_rate.mcs,
ap_ctx->txrx_stats.tx_rate.rate_flags,
flags);
/* rx rate info */
ap_ctx->txrx_stats.rx_rate.rate = peer_info->rx_rate;
rate_code = peer_info->rx_rate_code;
if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) ==
WMI_RATE_PREAMBLE_HT)
ap_ctx->txrx_stats.rx_rate.mode = SIR_SME_PHY_MODE_HT;
else if ((WMI_GET_HW_RATECODE_PREAM_V1(rate_code)) ==
WMI_RATE_PREAMBLE_VHT)
ap_ctx->txrx_stats.rx_rate.mode = SIR_SME_PHY_MODE_VHT;
else
ap_ctx->txrx_stats.rx_rate.mode = SIR_SME_PHY_MODE_LEGACY;
ap_ctx->txrx_stats.rx_rate.nss =
WMI_GET_HW_RATECODE_NSS_V1(rate_code) + 1;
ap_ctx->txrx_stats.rx_rate.mcs =
WMI_GET_HW_RATECODE_RATE_V1(rate_code);
flags = hdd_get_rate_flags(ap_ctx->txrx_stats.rx_rate.rate / 100,
ap_ctx->txrx_stats.rx_rate.mode,
ap_ctx->txrx_stats.rx_rate.nss,
ap_ctx->txrx_stats.rx_rate.mcs);
ap_ctx->txrx_stats.rx_rate.rate_flags = flags;
hdd_info("rx: mode %d nss %d mcs %d rate_flags %x flags %x",
ap_ctx->txrx_stats.rx_rate.mode,
ap_ctx->txrx_stats.rx_rate.nss,
ap_ctx->txrx_stats.rx_rate.mcs,
ap_ctx->txrx_stats.rx_rate.rate_flags,
flags);
}
int wlan_hdd_get_station_remote(struct wiphy *wiphy,
struct net_device *dev,
const u8 *mac,
struct station_info *sinfo);
/**
* wlan_hdd_get_station_remote() - NL80211_CMD_GET_STATION handler for SoftAP
* @wiphy: pointer to wiphy
* @dev: pointer to net_device structure
* @mac: request peer mac address
* @sinfo: pointer to station_info struct
*
* This function will get remote peer info from fw and fill sinfo struct
*
* Return: 0 on success, otherwise error value
*/
int wlan_hdd_get_station_remote(struct wiphy *wiphy,
struct net_device *dev,
const u8 *mac,
struct station_info *sinfo)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hddctx = (hdd_context_t *)wiphy_priv(wiphy);
hdd_ap_ctx_t *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
hdd_station_info_t *stainfo = NULL;
struct hdd_config *cfg;
struct qdf_mac_addr macaddr;
struct sir_peer_info_ext peer_info;
int status;
int i;
status = wlan_hdd_validate_context(hddctx);
if (status != 0)
return status;
cfg = hddctx->config;
hdd_debug("get peer %pM info", mac);
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
if (!qdf_mem_cmp(adapter->aStaInfo[i].macAddrSTA.bytes,
mac,
QDF_MAC_ADDR_SIZE)) {
stainfo = &adapter->aStaInfo[i];
break;
}
}
if (!stainfo) {
hdd_err("peer %pM not found", mac);
return -EINVAL;
}
qdf_mem_copy(macaddr.bytes, mac, QDF_MAC_ADDR_SIZE);
status = wlan_hdd_get_peer_info(adapter, macaddr, &peer_info);
if (status) {
hdd_err("fail to get peer info from fw");
return -EPERM;
}
qdf_mem_zero(&ap_ctx->txrx_stats, sizeof(ap_ctx->txrx_stats));
ap_ctx->txrx_stats.tx_packets = peer_info.tx_packets;
ap_ctx->txrx_stats.tx_bytes = peer_info.tx_bytes;
ap_ctx->txrx_stats.rx_packets = peer_info.rx_packets;
ap_ctx->txrx_stats.rx_bytes = peer_info.rx_bytes;
ap_ctx->txrx_stats.tx_retries = peer_info.tx_retries;
ap_ctx->txrx_stats.tx_failed = peer_info.tx_failed;
ap_ctx->txrx_stats.rssi =
peer_info.rssi + WLAN_HDD_TGT_NOISE_FLOOR_DBM;
wlan_hdd_fill_rate_info(ap_ctx, &peer_info);
wlan_hdd_fill_station_info(sinfo, stainfo, &ap_ctx->txrx_stats, cfg);
return status;
}
/**
* __wlan_hdd_cfg80211_get_station() - get station statistics
* @wiphy: Pointer to wiphy
@ -3129,7 +3917,7 @@ static int __wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
#endif
uint32_t vht_mcs_map;
enum eDataRate11ACMaxMcs vhtMaxMcs;
enum data_rate_11ac_max_mcs vht_max_mcs;
ENTER_DEV(dev);
@ -3356,16 +4144,16 @@ static int __wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
sme_cfg_get_int(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_VHT_TX_MCS_MAP,
&vht_mcs_map);
vhtMaxMcs = (enum eDataRate11ACMaxMcs)
vht_max_mcs = (enum data_rate_11ac_max_mcs)
(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)
if (DATA_RATE_11AC_MAX_MCS_7 == vht_max_mcs)
maxMCSIdx = 7;
else if (DATA_RATE_11AC_MAX_MCS_8 == vhtMaxMcs)
else if (DATA_RATE_11AC_MAX_MCS_8 == vht_max_mcs)
maxMCSIdx = 8;
else if (DATA_RATE_11AC_MAX_MCS_9 == vhtMaxMcs)
else if (DATA_RATE_11AC_MAX_MCS_9 == vht_max_mcs)
maxMCSIdx = 9;
if (rssidx != 0) {

View file

@ -57,13 +57,13 @@ struct index_vht_data_rate_type {
};
/**
* enum - eDataRate11ACMaxMcs
* enum - data_rate_11ac_max_mcs
* @DATA_RATE_11AC_MAX_MCS_7: MCS7 rate
* @DATA_RATE_11AC_MAX_MCS_8: MCS8 rate
* @DATA_RATE_11AC_MAX_MCS_9: MCS9 rate
* @DATA_RATE_11AC_MAX_MCS_NA:i Not applicable
*/
enum eDataRate11ACMaxMcs {
enum data_rate_11ac_max_mcs {
DATA_RATE_11AC_MAX_MCS_7,
DATA_RATE_11AC_MAX_MCS_8,
DATA_RATE_11AC_MAX_MCS_9,

View file

@ -3889,6 +3889,226 @@ int wlan_hdd_get_link_speed(hdd_adapter_t *sta_adapter, uint32_t *link_speed)
return 0;
}
struct peer_rssi_priv {
struct sir_peer_sta_info peer_sta_info;
};
/**
* hdd_get_peer_rssi_cb() - get peer station's rssi callback
* @sta_rssi: pointer of peer information
* @context: get rssi callback context
*
* This function will fill rssi information to rssi priv
* adapter
*
*/
static void hdd_get_peer_rssi_cb(struct sir_peer_info_resp *sta_rssi,
void *context)
{
struct hdd_request *request;
struct peer_rssi_priv *priv;
struct sir_peer_info *rssi_info;
uint8_t peer_num;
if ((!sta_rssi) || (!context)) {
hdd_err("Bad param, sta_rssi [%p] context [%p]",
sta_rssi, context);
return;
}
request = hdd_request_get(context);
if (!request) {
hdd_err("Obsolete request");
return;
}
priv = hdd_request_priv(request);
peer_num = sta_rssi->count;
rssi_info = sta_rssi->info;
hdd_debug("%d peers", peer_num);
if (peer_num > MAX_PEER_STA) {
hdd_warn("Exceed max peer sta to handle one time %d", peer_num);
peer_num = MAX_PEER_STA;
}
qdf_mem_copy(priv->peer_sta_info.info, rssi_info,
peer_num * sizeof(*rssi_info));
priv->peer_sta_info.sta_num = peer_num;
hdd_request_complete(request);
hdd_request_put(request);
}
int wlan_hdd_get_peer_rssi(hdd_adapter_t *adapter,
struct qdf_mac_addr *macaddress,
struct sir_peer_sta_info *peer_sta_info)
{
QDF_STATUS status;
void *cookie;
int ret;
struct sir_peer_info_req rssi_req;
struct hdd_request *request;
struct peer_rssi_priv *priv;
static const struct hdd_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
if (!adapter || !macaddress || !peer_sta_info) {
hdd_err("pAdapter [%p], macaddress [%p], peer_sta_info[%p]",
adapter, macaddress, peer_sta_info);
return -EFAULT;
}
request = hdd_request_alloc(&params);
if (!request) {
hdd_err("Request allocation failure");
return -ENOMEM;
}
cookie = hdd_request_cookie(request);
priv = hdd_request_priv(request);
qdf_mem_copy(&rssi_req.peer_macaddr, macaddress,
QDF_MAC_ADDR_SIZE);
rssi_req.sessionid = adapter->sessionId;
status = sme_get_peer_info(WLAN_HDD_GET_HAL_CTX(adapter),
rssi_req,
cookie,
hdd_get_peer_rssi_cb);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Unable to retrieve statistics for rssi");
ret = -EFAULT;
} else {
ret = hdd_request_wait_for_response(request);
if (ret) {
hdd_err("SME timed out while retrieving rssi");
ret = -EFAULT;
} else {
*peer_sta_info = priv->peer_sta_info;
ret = 0;
}
}
hdd_request_put(request);
return ret;
}
struct peer_info_priv {
struct sir_peer_sta_ext_info peer_sta_ext_info;
};
/**
* wlan_hdd_get_peer_info_cb() - get peer info callback
* @sta_info: pointer of peer information
* @context: get peer info callback context
*
* This function will fill stats info to peer info priv
*
*/
static void wlan_hdd_get_peer_info_cb(struct sir_peer_info_ext_resp *sta_info,
void *context)
{
struct hdd_request *request;
struct peer_info_priv *priv;
uint8_t sta_num;
if ((!sta_info) || (!context)) {
hdd_err("Bad param, sta_info [%p] context [%p]",
sta_info, context);
return;
}
if (!sta_info->count) {
hdd_err("Fail to get remote peer info");
return;
}
if (sta_info->count > MAX_PEER_STA) {
hdd_warn("Exceed max peer number %d", sta_info->count);
sta_num = MAX_PEER_STA;
} else {
sta_num = sta_info->count;
}
request = hdd_request_get(context);
if (!request) {
hdd_err("Obsolete request");
return;
}
priv = hdd_request_priv(request);
priv->peer_sta_ext_info.sta_num = sta_num;
qdf_mem_copy(&priv->peer_sta_ext_info.info,
sta_info->info,
sta_num * sizeof(sta_info->info[0]));
hdd_request_complete(request);
hdd_request_put(request);
}
int wlan_hdd_get_peer_info(hdd_adapter_t *adapter,
struct qdf_mac_addr macaddress,
struct sir_peer_info_ext *peer_info_ext)
{
QDF_STATUS status;
void *cookie;
int ret;
struct sir_peer_info_ext_req peer_info_req;
struct hdd_request *request;
struct peer_info_priv *priv;
static const struct hdd_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
if (!adapter) {
hdd_err("pAdapter is NULL");
return -EFAULT;
}
request = hdd_request_alloc(&params);
if (!request) {
hdd_err("Request allocation failure");
return -ENOMEM;
}
cookie = hdd_request_cookie(request);
priv = hdd_request_priv(request);
qdf_mem_copy(&peer_info_req.peer_macaddr, &macaddress,
QDF_MAC_ADDR_SIZE);
peer_info_req.sessionid = adapter->sessionId;
peer_info_req.reset_after_request = 0;
status = sme_get_peer_info_ext(WLAN_HDD_GET_HAL_CTX(adapter),
&peer_info_req,
cookie,
wlan_hdd_get_peer_info_cb);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Unable to retrieve statistics for peer info");
ret = -EFAULT;
} else {
ret = hdd_request_wait_for_response(request);
if (ret) {
hdd_err("SME timed out while retrieving peer info");
ret = -EFAULT;
} else {
/* only support one peer by now */
*peer_info_ext = priv->peer_sta_ext_info.info[0];
ret = 0;
}
}
hdd_request_put(request);
return ret;
}
/**
* hdd_statistics_cb() - "Get statistics" callback function
* @pStats: statistics payload

View file

@ -127,6 +127,9 @@ typedef uint8_t tSirVersionString[SIR_VERSION_STRING_LEN];
/* Cache ID length */
#define CACHE_ID_LEN 2
/* Maximum peer station number query one time */
#define MAX_PEER_STA 12
/**
* enum sir_conn_update_reason: Reason for conc connection update
* @SIR_UPDATE_REASON_SET_OPER_CHAN: Set probable operating channel
@ -3807,6 +3810,28 @@ struct sir_peer_info_ext_resp {
struct sir_peer_info_ext info[0];
};
/**
* @sta_num: number of peer station which has valid info
* @info: peer information
*
* all SAP peer station's information retrieved
*/
struct sir_peer_sta_info {
uint8_t sta_num;
struct sir_peer_info info[MAX_PEER_STA];
};
/**
* @sta_num: number of peer station which has valid info
* @info: peer extended information
*
* all SAP peer station's extended information retrieved
*/
struct sir_peer_sta_ext_info {
uint8_t sta_num;
struct sir_peer_info_ext info[MAX_PEER_STA];
};
typedef struct sSirAddPeriodicTxPtrn {
/* MAC Address for the adapter */
struct qdf_mac_addr mac_address;

View file

@ -69,6 +69,7 @@ struct wma_tgt_services {
bool en_roam_offload;
#endif /* WLAN_FEATURE_ROAM_OFFLOAD */
bool en_11ax;
bool get_peer_info_enabled;
};
/**

View file

@ -754,6 +754,8 @@ QDF_STATUS wma_get_peer_info(WMA_HANDLE handle,
cmd->stats_id = WMI_REQUEST_PEER_STAT;
cmd->vdev_id = peer_info_req->sessionid;
WMI_CHAR_ARRAY_TO_MAC_ADDR(peer_info_req->peer_macaddr.bytes,
&cmd->peer_macaddr);
wma_handle->get_sta_peer_info = true;
if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len,

View file

@ -4059,6 +4059,10 @@ static inline void wma_update_target_services(tp_wma_handle wh,
}
wma_he_update_tgt_services(wh, cfg);
cfg->get_peer_info_enabled =
WMI_SERVICE_IS_ENABLED(wh->wmi_service_bitmap,
WMI_SERVICE_PEER_STATS_INFO);
}
/**