From 496b36c936229eabfa3b84a4e8695cc8b6a4b68a Mon Sep 17 00:00:00 2001 From: Will Huang Date: Tue, 11 Jul 2017 16:38:50 +0800 Subject: [PATCH] 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 --- core/hdd/inc/qc_sap_ioctl.h | 1 + core/hdd/inc/wlan_hdd_cfg.h | 19 + core/hdd/inc/wlan_hdd_main.h | 127 +++- core/hdd/inc/wlan_hdd_wext.h | 28 + core/hdd/src/wlan_hdd_cfg.c | 7 + core/hdd/src/wlan_hdd_cfg80211.c | 300 +++++++-- core/hdd/src/wlan_hdd_hostapd.c | 492 ++++++++++++-- core/hdd/src/wlan_hdd_main.c | 8 + core/hdd/src/wlan_hdd_softap_tx_rx.c | 26 +- core/hdd/src/wlan_hdd_stats.c | 922 +++++++++++++++++++++++++-- core/hdd/src/wlan_hdd_stats.h | 4 +- core/hdd/src/wlan_hdd_wext.c | 220 +++++++ core/mac/inc/sir_api.h | 25 + core/wma/inc/wma_tgt_cfg.h | 1 + core/wma/src/wma_features.c | 2 + core/wma/src/wma_main.c | 4 + 16 files changed, 1992 insertions(+), 194 deletions(-) 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); } /**