diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index d6e2e5c03d17..e60d3d32202c 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -815,7 +815,7 @@ struct hdd_station_info { bool sgi_enable; bool tx_stbc; bool rx_stbc; - uint8_t ch_width; + tSirMacHTChannelWidth ch_width; uint8_t mode; uint8_t max_supp_idx; uint8_t max_ext_idx; diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index dce8cf3b4363..94ef40218dbf 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -4960,32 +4960,384 @@ static inline int32_t remote_station_put_u64(struct sk_buff *skb, #endif /** - * hdd_get_station_remote() - get remote peer's info + * hdd_add_survey_info_sap_get_len - get data length used in + * hdd_add_survey_info_sap() + * + * This function calculates the data length used in hdd_add_survey_info_sap() + * + * Return: total data length used in hdd_add_survey_info_sap() + */ +static uint32_t hdd_add_survey_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint32_t) + NLA_HDRLEN)); +} + +/** + * hdd_add_survey_info - add survey info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds survey info attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int32_t hdd_add_survey_info_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY, + stainfo->freq)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_tx_bitrate_sap_get_len - get data length used in + * hdd_add_tx_bitrate_sap() + * + * This function calculates the data length used in hdd_add_tx_bitrate_sap() + * + * Return: total data length used in hdd_add_tx_bitrate_sap() + */ +static uint32_t hdd_add_tx_bitrate_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN)); +} + +/** + * hdd_add_tx_bitrate_sap - add vhs nss info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds vht nss attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_tx_bitrate_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS, + stainfo->nss)) { + hdd_err("put fail"); + goto fail; + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_sta_info_sap_get_len - get data length used in + * hdd_add_sta_info_sap() + * + * This function calculates the data length used in hdd_add_sta_info_sap() + * + * Return: total data length used in hdd_add_sta_info_sap() + */ +static uint32_t hdd_add_sta_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN) + + hdd_add_tx_bitrate_sap_get_len()); +} + +/** + * hdd_add_sta_info_sap - add sta signal info attribute + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds sta signal attribute to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int32_t hdd_add_sta_info_sap(struct sk_buff *skb, int8_t rssi, + struct hdd_station_info *stainfo, int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, rssi)) { + hdd_err("put fail"); + goto fail; + } + if (hdd_add_tx_bitrate_sap(skb, stainfo, NL80211_STA_INFO_TX_BITRATE)) + goto fail; + + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_link_standard_info_sap_get_len - get data length used in + * hdd_add_link_standard_info_sap() + * + * This function calculates the data length used in + * hdd_add_link_standard_info_sap() + * + * Return: total data length used in hdd_add_link_standard_info_sap() + */ +static uint32_t hdd_add_link_standard_info_sap_get_len(void) +{ + return ((NLA_HDRLEN) + + hdd_add_survey_info_sap_get_len() + + hdd_add_sta_info_sap_get_len() + + (sizeof(uint32_t) + NLA_HDRLEN)); +} + +/** + * hdd_add_link_standard_info_sap - add add link info attribut + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds link info attribut to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_link_standard_info_sap(struct sk_buff *skb, int8_t rssi, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + if (hdd_add_survey_info_sap(skb, stainfo, NL80211_ATTR_SURVEY_INFO)) + goto fail; + if (hdd_add_sta_info_sap(skb, rssi, stainfo, NL80211_ATTR_STA_INFO)) + goto fail; + + if (nla_put_u32(skb, NL80211_ATTR_REASON_CODE, stainfo->reason_code)) { + hdd_err("Reason code put fail"); + goto fail; + } + + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_add_ap_standard_info_sap_get_len - get data length used in + * hdd_add_ap_standard_info_sap() + * @stainfo: station information + * + * This function calculates the data length used in + * hdd_add_ap_standard_info_sap() + * + * Return: total data length used in hdd_add_ap_standard_info_sap() + */ +static uint32_t hdd_add_ap_standard_info_sap_get_len( + struct hdd_station_info *stainfo) +{ + uint32_t len; + + len = NLA_HDRLEN; + if (stainfo->vht_present) + len += (sizeof(stainfo->vht_caps) + NLA_HDRLEN); + if (stainfo->ht_present) + len += (sizeof(stainfo->ht_caps) + NLA_HDRLEN); + + return len; +} + +/** + * hdd_add_ap_standard_info_sap - add HT and VHT info attributes + * @skb: pointer to response skb buffer + * @stainfo: station information + * @idx: attribute type index for nla_next_start() + * + * This function adds HT and VHT info attributes to response skb buffer + * + * Return : 0 on success and errno on failure + */ +static int hdd_add_ap_standard_info_sap(struct sk_buff *skb, + struct hdd_station_info *stainfo, + int idx) +{ + struct nlattr *nla_attr; + + nla_attr = nla_nest_start(skb, idx); + if (!nla_attr) + goto fail; + + if (stainfo->vht_present) { + if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY, + sizeof(stainfo->vht_caps), + &stainfo->vht_caps)) { + hdd_err("put fail"); + goto fail; + } + } + if (stainfo->ht_present) { + if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY, + sizeof(stainfo->ht_caps), + &stainfo->ht_caps)) { + hdd_err("put fail"); + goto fail; + } + } + nla_nest_end(skb, nla_attr); + return 0; +fail: + return -EINVAL; +} + +/** + * hdd_decode_ch_width - decode channel band width based + * @ch_width: encoded enum value holding channel band width + * + * This function decodes channel band width from the given encoded enum value. + * + * Returns: decoded channel band width. + */ +static uint8_t hdd_decode_ch_width(tSirMacHTChannelWidth ch_width) +{ + switch (ch_width) { + case 0: + return 20; + case 1: + return 40; + case 2: + return 80; + case 3: + case 4: + return 160; + default: + hdd_debug("invalid enum: %d", ch_width); + return 20; + } +} + +/** + * hdd_get_cached_station_remote() - get cached(deleted) 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 + * This function collect and indicate the cached(deleted) peer's info * * Return: 0 on success, otherwise error value */ -static int hdd_get_station_remote(struct hdd_context *hdd_ctx, - struct hdd_adapter *adapter, - struct qdf_mac_addr mac_addr) + +static int hdd_get_cached_station_remote(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr) { - struct hdd_station_info *stainfo = hdd_get_stainfo(adapter->sta_info, - mac_addr); + struct hdd_station_info *stainfo = hdd_get_stainfo( + adapter->cache_sta_info, + mac_addr); struct sk_buff *skb = NULL; - struct sir_peer_info_ext peer_info; - uint32_t nl_buf_len; - bool txrx_rate = true; + uint32_t nl_buf_len = NLMSG_HDRLEN; + uint8_t channel_width; if (!stainfo) { hdd_err("peer " MAC_ADDRESS_STR " not found", MAC_ADDR_ARRAY(mac_addr.bytes)); + return -EINVAL; + } + + nl_buf_len += hdd_add_link_standard_info_sap_get_len() + + hdd_add_ap_standard_info_sap_get_len(stainfo) + + (sizeof(stainfo->mode) + NLA_HDRLEN) + + (sizeof(stainfo->ch_width) + NLA_HDRLEN) + + (sizeof(stainfo->tx_rate) + NLA_HDRLEN) + + (sizeof(stainfo->rx_rate) + NLA_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hdd_err(FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); + return -ENOMEM; + } + + if (hdd_add_link_standard_info_sap(skb, stainfo->rssi, stainfo, + LINK_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("link standard put fail"); goto fail; } + if (hdd_add_ap_standard_info_sap(skb, stainfo, + AP_INFO_STANDARD_NL80211_ATTR)) { + hdd_err("ap standard put fail"); + goto fail; + } + + /* upper layer expects decoded channel BW */ + channel_width = hdd_decode_ch_width(stainfo->ch_width); + + if (nla_put_u32(skb, REMOTE_SUPPORTED_MODE, + hdd_convert_dot11mode( + stainfo->mode)) || + nla_put_u8(skb, REMOTE_CH_WIDTH, channel_width)) { + hdd_err("remote ch put fail"); + goto fail; + } + if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate)) { + hdd_err("tx rate put fail"); + goto fail; + } + if (nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate)) { + hdd_err("rx rate put fail"); + goto fail; + } + + qdf_mem_zero(stainfo, sizeof(*stainfo)); + + return cfg80211_vendor_cmd_reply(skb); +fail: + if (skb) + kfree_skb(skb); + + return -EINVAL; +} + +/** + * hdd_get_cached_station_remote() - get connected peer's info + * @hdd_ctx: hdd context + * @adapter: hostapd interface + * @mac_addr: mac address of requested peer + * + * This function collect and indicate the connected peer's info + * + * Return: 0 on success, otherwise error value + */ +static int hdd_get_connected_station_info(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr, + struct hdd_station_info *stainfo) +{ + struct sk_buff *skb = NULL; + uint32_t nl_buf_len; + struct sir_peer_info_ext peer_info; + bool txrx_rate = true; + nl_buf_len = NLMSG_HDRLEN; nl_buf_len += (sizeof(stainfo->max_phy_rate) + NLA_HDRLEN) + (sizeof(stainfo->tx_packets) + NLA_HDRLEN) + @@ -5082,6 +5434,43 @@ fail: return -EINVAL; } +/** + * 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(struct hdd_context *hdd_ctx, + struct hdd_adapter *adapter, + struct qdf_mac_addr mac_addr) +{ + struct hdd_station_info *stainfo = hdd_get_stainfo(adapter->sta_info, + mac_addr); + int status = 0; + bool is_associated = false; + + if (!stainfo) { + status = hdd_get_cached_station_remote(hdd_ctx, adapter, + mac_addr); + return status; + } + + is_associated = hdd_is_peer_associated(adapter, &mac_addr); + if (!is_associated) { + status = hdd_get_cached_station_remote(hdd_ctx, adapter, + mac_addr); + return status; + } + + status = hdd_get_connected_station_info(hdd_ctx, adapter, + mac_addr, stainfo); + return status; +} + /** * __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd * @wiphy: corestack handler