diff --git a/core/hdd/inc/qc_sap_ioctl.h b/core/hdd/inc/qc_sap_ioctl.h
index 0d8f46848cf6..a58fe767f426 100644
--- a/core/hdd/inc/qc_sap_ioctl.h
+++ b/core/hdd/inc/qc_sap_ioctl.h
@@ -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
diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h
index 982dfc096633..868b03cede45 100644
--- a/core/hdd/inc/wlan_hdd_cfg.h
+++ b/core/hdd/inc/wlan_hdd_cfg.h
@@ -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)
+/*
+ *
+ * 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
+ *
+ *
+ */
+#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)
+
/*
*
* 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;
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 281a58f8b8ce..a1b94458af84 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -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;
};
/**
diff --git a/core/hdd/inc/wlan_hdd_wext.h b/core/hdd/inc/wlan_hdd_wext.h
index 29706db7c0f7..a04e0450c541 100644
--- a/core/hdd/inc/wlan_hdd_wext.h
+++ b/core/hdd/inc/wlan_hdd_wext.h
@@ -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__ */
diff --git a/core/hdd/src/wlan_hdd_cfg.c b/core/hdd/src/wlan_hdd_cfg.c
index f898db0cba3a..980243ea2fc9 100644
--- a/core/hdd/src/wlan_hdd_cfg.c
+++ b/core/hdd/src/wlan_hdd_cfg.c
@@ -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,
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index c7ee32859d03..6da60f90b44a 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -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
/**
diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c
index 00a0fde944ea..088f0a1ec319 100644
--- a/core/hdd/src/wlan_hdd_hostapd.c
+++ b/core/hdd/src/wlan_hdd_hostapd.c
@@ -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] =
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index f1a4c48aa8ec..ae63a920a9ea 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -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) {
diff --git a/core/hdd/src/wlan_hdd_softap_tx_rx.c b/core/hdd/src/wlan_hdd_softap_tx_rx.c
index 8c298e6a0908..17b1f23f86e0 100644
--- a/core/hdd/src/wlan_hdd_softap_tx_rx.c
+++ b/core/hdd/src/wlan_hdd_softap_tx_rx.c
@@ -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,
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index fbcd9c1f3e88..e4e34beeef36 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -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) {
diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h
index 6a34cb12d787..363c3adf632f 100644
--- a/core/hdd/src/wlan_hdd_stats.h
+++ b/core/hdd/src/wlan_hdd_stats.h
@@ -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,
diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c
index a450e2ede6f3..20649a124100 100644
--- a/core/hdd/src/wlan_hdd_wext.c
+++ b/core/hdd/src/wlan_hdd_wext.c
@@ -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(¶ms);
+ 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(¶ms);
+ 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
diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h
index 75e2c302953a..b4a693fd794f 100644
--- a/core/mac/inc/sir_api.h
+++ b/core/mac/inc/sir_api.h
@@ -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;
diff --git a/core/wma/inc/wma_tgt_cfg.h b/core/wma/inc/wma_tgt_cfg.h
index 09280a7df8d5..5f7e64ebdf8c 100644
--- a/core/wma/inc/wma_tgt_cfg.h
+++ b/core/wma/inc/wma_tgt_cfg.h
@@ -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;
};
/**
diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c
index 771cd3419db4..d7b6e49e7596 100644
--- a/core/wma/src/wma_features.c
+++ b/core/wma/src/wma_features.c
@@ -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,
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index 7ef65a9affb6..83083bd380f2 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -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);
}
/**