| /* |
| * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wlan_hdd_station_info.c |
| * |
| * WLAN station info functions |
| * |
| */ |
| |
| #include "osif_sync.h" |
| #include <wlan_hdd_includes.h> |
| #include <linux/netdevice.h> |
| #include <linux/skbuff.h> |
| #include <linux/etherdevice.h> |
| #include <linux/if_ether.h> |
| #include <wlan_cp_stats_mc_ucfg_api.h> |
| #include <wlan_hdd_stats.h> |
| #include <wlan_hdd_hostapd.h> |
| #include <wlan_hdd_station_info.h> |
| #include "wlan_mlme_ucfg_api.h" |
| #include "wlan_hdd_sta_info.h" |
| #include <cdp_txrx_handle.h> |
| #include <cdp_txrx_stats_struct.h> |
| #include <cdp_txrx_peer_ops.h> |
| #include <cdp_txrx_host_stats.h> |
| #include "wlan_hdd_object_manager.h" |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_get_station_cmd() |
| */ |
| #define STATION_INVALID \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INVALID |
| #define STATION_INFO \ |
| 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 STA_INFO_CONNECT_FAIL_REASON_CODE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE |
| |
| /* 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 HE_OPERATION \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HE_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 |
| #define REMOTE_PAD\ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_PAD |
| #define REMOTE_RX_RETRY_COUNT \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_RETRY_COUNT |
| #define REMOTE_RX_BC_MC_COUNT \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BC_MC_COUNT |
| #define REMOTE_TX_FAILURE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_FAILURE |
| #define REMOTE_AVG_RSSI_PER_CHAIN \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AVG_RSSI_PER_CHAIN |
| #define REMOTE_TX_RETRY_SUCCEED \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_SUCCEED |
| #define REMOTE_RX_LAST_PKT_RSSI \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_LAST_PKT_RSSI |
| #define REMOTE_TX_RETRY \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY |
| #define REMOTE_TX_RETRY_EXHAUST \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_EXHAUST |
| #define REMOTE_TX_TOTAL_FW \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_TOTAL_FW |
| #define REMOTE_TX_RETRY_FW \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_FW |
| #define REMOTE_TX_RETRY_EXHAUST_FW \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_RETRY_EXHAUST_FW |
| #define DISCONNECT_REASON \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_DRIVER_DISCONNECT_REASON |
| #define BEACON_IES \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_BEACON_IES |
| #define ASSOC_REQ_IES \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_REQ_IES |
| |
| /* |
| * MSB of rx_mc_bc_cnt indicates whether FW supports rx_mc_bc_cnt |
| * feature or not, if first bit is 1 it indictes that FW supports this |
| * feature, if it is 0 it indicates FW doesn't support this feature |
| */ |
| #define HDD_STATION_INFO_RX_MC_BC_COUNT (1 << 31) |
| |
| |
| 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}, |
| }; |
| |
| const struct nla_policy |
| hdd_get_sta_policy[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC] = {.type = NLA_BINARY, |
| .len = QDF_MAC_ADDR_SIZE}, |
| }; |
| |
| static int hdd_get_sta_congestion(struct hdd_adapter *adapter, |
| uint32_t *congestion) |
| { |
| QDF_STATUS status; |
| struct cca_stats cca_stats; |
| |
| status = ucfg_mc_cp_stats_cca_stats_get(adapter->vdev, &cca_stats); |
| if (QDF_IS_STATUS_ERROR(status)) |
| return -EINVAL; |
| |
| *congestion = cca_stats.congestion; |
| return 0; |
| } |
| |
| /** |
| * hdd_get_station_assoc_fail() - Handle get station assoc fail |
| * @hdd_ctx: HDD context within host driver |
| * @wdev: wireless device |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION_ASSOC_FAIL. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_get_station_assoc_fail(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| struct sk_buff *skb = NULL; |
| uint32_t nl_buf_len; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| uint32_t congestion; |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += sizeof(uint32_t); |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| if (nla_put_u32(skb, INFO_ASSOC_FAIL_REASON, |
| hdd_sta_ctx->conn_info.assoc_status_code)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (hdd_get_sta_congestion(adapter, &congestion)) |
| congestion = 0; |
| |
| hdd_info("congestion:%d", congestion); |
| if (nla_put_u32(skb, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, |
| congestion)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_map_auth_type() - transform auth type specific to |
| * vendor command |
| * @auth_type: csr auth type |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_convert_auth_type(uint32_t auth_type) |
| { |
| uint32_t ret_val; |
| |
| switch (auth_type) { |
| case eCSR_AUTH_TYPE_OPEN_SYSTEM: |
| ret_val = QCA_WLAN_AUTH_TYPE_OPEN; |
| break; |
| case eCSR_AUTH_TYPE_SHARED_KEY: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHARED; |
| break; |
| case eCSR_AUTH_TYPE_WPA: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA; |
| break; |
| case eCSR_AUTH_TYPE_WPA_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA_PSK; |
| break; |
| case eCSR_AUTH_TYPE_AUTOSWITCH: |
| ret_val = QCA_WLAN_AUTH_TYPE_AUTOSWITCH; |
| break; |
| case eCSR_AUTH_TYPE_WPA_NONE: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA_NONE; |
| break; |
| case eCSR_AUTH_TYPE_RSN: |
| ret_val = QCA_WLAN_AUTH_TYPE_RSN; |
| break; |
| case eCSR_AUTH_TYPE_RSN_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_RSN_PSK; |
| break; |
| case eCSR_AUTH_TYPE_FT_RSN: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT; |
| break; |
| case eCSR_AUTH_TYPE_FT_RSN_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT_PSK; |
| break; |
| case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: |
| ret_val = QCA_WLAN_AUTH_TYPE_WAI; |
| break; |
| case eCSR_AUTH_TYPE_WAPI_WAI_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_WAI_PSK; |
| break; |
| case eCSR_AUTH_TYPE_CCKM_WPA: |
| ret_val = QCA_WLAN_AUTH_TYPE_CCKM_WPA; |
| break; |
| case eCSR_AUTH_TYPE_CCKM_RSN: |
| ret_val = QCA_WLAN_AUTH_TYPE_CCKM_RSN; |
| break; |
| case eCSR_AUTH_TYPE_RSN_PSK_SHA256: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHA256_PSK; |
| break; |
| case eCSR_AUTH_TYPE_RSN_8021X_SHA256: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHA256; |
| break; |
| case eCSR_AUTH_TYPE_FT_SAE: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT_SAE; |
| break; |
| case eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT_SUITEB_EAP_SHA384; |
| break; |
| case eCSR_NUM_OF_SUPPORT_AUTH_TYPE: |
| case eCSR_AUTH_TYPE_FAILED: |
| case eCSR_AUTH_TYPE_NONE: |
| default: |
| ret_val = QCA_WLAN_AUTH_TYPE_INVALID; |
| break; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * hdd_map_dot_11_mode() - transform dot11mode type specific to |
| * vendor command |
| * @dot11mode: dot11mode |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_convert_dot11mode(uint32_t dot11mode) |
| { |
| uint32_t ret_val; |
| |
| switch (dot11mode) { |
| case eCSR_CFG_DOT11_MODE_11A: |
| ret_val = QCA_WLAN_802_11_MODE_11A; |
| break; |
| case eCSR_CFG_DOT11_MODE_11B: |
| ret_val = QCA_WLAN_802_11_MODE_11B; |
| break; |
| case eCSR_CFG_DOT11_MODE_11G: |
| ret_val = QCA_WLAN_802_11_MODE_11G; |
| break; |
| case eCSR_CFG_DOT11_MODE_11N: |
| ret_val = QCA_WLAN_802_11_MODE_11N; |
| break; |
| case eCSR_CFG_DOT11_MODE_11AC: |
| ret_val = QCA_WLAN_802_11_MODE_11AC; |
| break; |
| case eCSR_CFG_DOT11_MODE_AUTO: |
| case eCSR_CFG_DOT11_MODE_ABG: |
| default: |
| ret_val = QCA_WLAN_802_11_MODE_INVALID; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * hdd_add_tx_bitrate() - add tx bitrate attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_tx_bitrate(struct sk_buff *skb, |
| struct hdd_station_ctx *hdd_sta_ctx, |
| int idx) |
| { |
| struct nlattr *nla_attr; |
| uint32_t bitrate, bitrate_compat; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) { |
| hdd_err("nla_nest_start failed"); |
| goto fail; |
| } |
| |
| /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
| if (hdd_conn_is_connected(hdd_sta_ctx)) |
| bitrate = cfg80211_calculate_bitrate( |
| &hdd_sta_ctx->cache_conn_info.max_tx_bitrate); |
| else |
| bitrate = cfg80211_calculate_bitrate( |
| &hdd_sta_ctx->cache_conn_info.txrate); |
| /* report 16-bit bitrate only if we can */ |
| bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; |
| |
| if (bitrate > 0) { |
| if (nla_put_u32(skb, NL80211_RATE_INFO_BITRATE32, bitrate)) { |
| hdd_err("put fail bitrate: %u", bitrate); |
| goto fail; |
| } |
| } else { |
| hdd_err("Invalid bitrate: %u", bitrate); |
| } |
| |
| if (bitrate_compat > 0) { |
| if (nla_put_u16(skb, NL80211_RATE_INFO_BITRATE, |
| bitrate_compat)) { |
| hdd_err("put fail bitrate_compat: %u", bitrate_compat); |
| goto fail; |
| } |
| } else { |
| hdd_err("Invalid bitrate_compat: %u", bitrate_compat); |
| } |
| |
| if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS, |
| hdd_sta_ctx->cache_conn_info.txrate.nss)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_get_max_tx_bitrate() - Get the max tx bitrate of the AP |
| * @hdd_ctx: hdd context |
| * @adapter: hostapd interface |
| * |
| * THis function gets the MAX supported rate by AP and cache |
| * it into connection info structure |
| * |
| * Return: None |
| */ |
| static void hdd_get_max_tx_bitrate(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| struct station_info sinfo; |
| enum tx_rate_info tx_rate_flags; |
| uint8_t tx_mcs_index, tx_nss = 1; |
| uint16_t my_tx_rate; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| struct wlan_objmgr_vdev *vdev; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| qdf_mem_zero(&sinfo, sizeof(struct station_info)); |
| |
| sinfo.signal = adapter->rssi; |
| tx_rate_flags = adapter->hdd_stats.class_a_stat.tx_rx_rate_flags; |
| tx_mcs_index = adapter->hdd_stats.class_a_stat.tx_mcs_index; |
| my_tx_rate = adapter->hdd_stats.class_a_stat.tx_rate; |
| |
| if (!(tx_rate_flags & TX_RATE_LEGACY)) { |
| vdev = hdd_objmgr_get_vdev(adapter); |
| if (vdev) { |
| /* |
| * Take static NSS for reporting max rates. |
| * NSS from FW is not reliable as it changes |
| * as per the environment quality. |
| */ |
| tx_nss = wlan_vdev_mlme_get_nss(vdev); |
| hdd_objmgr_put_vdev(vdev); |
| } else { |
| tx_nss = adapter->hdd_stats.class_a_stat.tx_nss; |
| } |
| hdd_check_and_update_nss(hdd_ctx, &tx_nss, NULL); |
| |
| if (tx_mcs_index == INVALID_MCS_IDX) |
| tx_mcs_index = 0; |
| } |
| if (hdd_report_max_rate(hdd_ctx->mac_handle, &sinfo.txrate, |
| sinfo.signal, tx_rate_flags, tx_mcs_index, |
| my_tx_rate, tx_nss)) { |
| hdd_sta_ctx->cache_conn_info.max_tx_bitrate = sinfo.txrate; |
| hdd_debug("Reporting max tx rate flags %d mcs %d nss %d bw %d", |
| sinfo.txrate.flags, sinfo.txrate.mcs, |
| sinfo.txrate.nss, sinfo.txrate.bw); |
| } |
| } |
| |
| /** |
| * hdd_add_sta_info() - add station info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_sta_info(struct sk_buff *skb, |
| struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, |
| int idx) |
| { |
| struct nlattr *nla_attr; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (!hdd_sta_ctx) { |
| hdd_err("Invalid sta ctx"); |
| goto fail; |
| } |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) { |
| hdd_err("nla_nest_start failed"); |
| goto fail; |
| } |
| |
| if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, |
| (hdd_sta_ctx->cache_conn_info.signal + 100))) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_conn_is_connected(hdd_sta_ctx)) |
| hdd_get_max_tx_bitrate(hdd_ctx, adapter); |
| |
| if (hdd_add_tx_bitrate(skb, hdd_sta_ctx, NL80211_STA_INFO_TX_BITRATE)) { |
| hdd_err("hdd_add_tx_bitrate failed"); |
| goto fail; |
| } |
| |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_survey_info() - add survey info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_survey_info(struct sk_buff *skb, |
| struct hdd_station_ctx *hdd_sta_ctx, |
| 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, |
| hdd_sta_ctx->cache_conn_info.chan_freq) || |
| nla_put_u8(skb, NL80211_SURVEY_INFO_NOISE, |
| (hdd_sta_ctx->cache_conn_info.noise + 100))) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_link_standard_info() - add link info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_add_link_standard_info(struct sk_buff *skb, |
| struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter, int idx) |
| { |
| struct nlattr *nla_attr; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (!hdd_sta_ctx) { |
| hdd_err("Invalid sta ctx"); |
| goto fail; |
| } |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) { |
| hdd_err("nla_nest_start failed"); |
| goto fail; |
| } |
| |
| if (nla_put(skb, |
| NL80211_ATTR_SSID, |
| hdd_sta_ctx->cache_conn_info.last_ssid.SSID.length, |
| hdd_sta_ctx->cache_conn_info.last_ssid.SSID.ssId)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (nla_put(skb, NL80211_ATTR_MAC, QDF_MAC_ADDR_SIZE, |
| hdd_sta_ctx->cache_conn_info.bssid.bytes)) { |
| hdd_err("put bssid failed"); |
| goto fail; |
| } |
| if (hdd_add_survey_info(skb, hdd_sta_ctx, NL80211_ATTR_SURVEY_INFO)) { |
| hdd_err("hdd_add_survey_info failed"); |
| goto fail; |
| } |
| |
| if (hdd_add_sta_info(skb, hdd_ctx, adapter, NL80211_ATTR_STA_INFO)) { |
| hdd_err("hdd_add_sta_info failed"); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_ap_standard_info() - add ap info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_add_ap_standard_info(struct sk_buff *skb, |
| struct hdd_station_ctx *hdd_sta_ctx, int idx) |
| { |
| struct nlattr *nla_attr; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present) |
| if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY, |
| sizeof(hdd_sta_ctx->cache_conn_info.vht_caps), |
| &hdd_sta_ctx->cache_conn_info.vht_caps)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present) |
| if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY, |
| sizeof(hdd_sta_ctx->cache_conn_info.ht_caps), |
| &hdd_sta_ctx->cache_conn_info.ht_caps)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) && \ |
| defined(WLAN_FEATURE_11AX) |
| static int32_t hdd_add_he_oper_info( |
| struct sk_buff *skb, |
| struct hdd_station_ctx *hdd_sta_ctx) |
| { |
| int32_t ret = 0; |
| |
| if (!hdd_sta_ctx->cache_conn_info.he_oper_len || |
| !hdd_sta_ctx->cache_conn_info.he_operation) |
| return ret; |
| |
| if (nla_put(skb, HE_OPERATION, |
| hdd_sta_ctx->cache_conn_info.he_oper_len, |
| hdd_sta_ctx->cache_conn_info.he_operation)) |
| ret = -EINVAL; |
| |
| qdf_mem_free(hdd_sta_ctx->cache_conn_info.he_operation); |
| hdd_sta_ctx->cache_conn_info.he_operation = NULL; |
| hdd_sta_ctx->cache_conn_info.he_oper_len = 0; |
| return ret; |
| } |
| |
| static int32_t hdd_get_he_op_len(struct hdd_station_ctx *hdd_sta_ctx) |
| { |
| return hdd_sta_ctx->cache_conn_info.he_oper_len; |
| } |
| #else |
| static inline uint32_t hdd_add_he_oper_info( |
| struct sk_buff *skb, |
| struct hdd_station_ctx *hdd_sta_ctx) |
| { |
| return 0; |
| } |
| |
| static uint32_t hdd_get_he_op_len(struct hdd_station_ctx *hdd_sta_ctx) |
| { |
| return 0; |
| } |
| #endif |
| |
| /** |
| * hdd_get_station_info() - send BSS information to supplicant |
| * @hdd_ctx: pointer to hdd context |
| * @adapter: pointer to adapter |
| * |
| * Return: 0 if success else error status |
| */ |
| static int hdd_get_station_info(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| struct sk_buff *skb = NULL; |
| uint8_t *tmp_hs20 = NULL, *ies = NULL; |
| uint32_t nl_buf_len, ie_len = 0, hdd_he_op_len = 0; |
| struct hdd_station_ctx *hdd_sta_ctx; |
| QDF_STATUS status; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += sizeof(hdd_sta_ctx-> |
| cache_conn_info.last_ssid.SSID.length) + |
| QDF_MAC_ADDR_SIZE + |
| sizeof(hdd_sta_ctx->cache_conn_info.chan_freq) + |
| sizeof(hdd_sta_ctx->cache_conn_info.noise) + |
| sizeof(hdd_sta_ctx->cache_conn_info.signal) + |
| (sizeof(uint32_t) * 2) + |
| sizeof(hdd_sta_ctx->cache_conn_info.txrate.nss) + |
| sizeof(hdd_sta_ctx->cache_conn_info.roam_count) + |
| sizeof(hdd_sta_ctx->cache_conn_info.last_auth_type) + |
| sizeof(hdd_sta_ctx->cache_conn_info.dot11mode) + |
| sizeof(uint32_t); |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.vht_caps); |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.ht_caps); |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present) { |
| tmp_hs20 = (uint8_t *)&(hdd_sta_ctx-> |
| cache_conn_info.hs20vendor_ie); |
| nl_buf_len += (sizeof(hdd_sta_ctx-> |
| cache_conn_info.hs20vendor_ie) - 1); |
| } |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present) |
| nl_buf_len += sizeof(hdd_sta_ctx-> |
| cache_conn_info.ht_operation); |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present) |
| nl_buf_len += sizeof(hdd_sta_ctx-> |
| cache_conn_info.vht_operation); |
| status = sme_get_prev_connected_bss_ies(hdd_ctx->mac_handle, |
| adapter->vdev_id, |
| &ies, &ie_len); |
| if (QDF_IS_STATUS_SUCCESS(status)) |
| nl_buf_len += ie_len; |
| |
| hdd_he_op_len = hdd_get_he_op_len(hdd_sta_ctx); |
| nl_buf_len += hdd_he_op_len; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (hdd_add_link_standard_info(skb, hdd_ctx, adapter, |
| LINK_INFO_STANDARD_NL80211_ATTR)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_add_ap_standard_info(skb, hdd_sta_ctx, |
| AP_INFO_STANDARD_NL80211_ATTR)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (nla_put_u32(skb, INFO_ROAM_COUNT, |
| hdd_sta_ctx->cache_conn_info.roam_count) || |
| nla_put_u32(skb, INFO_AKM, |
| hdd_convert_auth_type( |
| hdd_sta_ctx->cache_conn_info.last_auth_type)) || |
| nla_put_u32(skb, WLAN802_11_MODE, |
| hdd_convert_dot11mode( |
| hdd_sta_ctx->cache_conn_info.dot11mode))) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present) |
| if (nla_put(skb, HT_OPERATION, |
| (sizeof(hdd_sta_ctx->cache_conn_info.ht_operation)), |
| &hdd_sta_ctx->cache_conn_info.ht_operation)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present) |
| if (nla_put(skb, VHT_OPERATION, |
| (sizeof(hdd_sta_ctx-> |
| cache_conn_info.vht_operation)), |
| &hdd_sta_ctx->cache_conn_info.vht_operation)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_add_he_oper_info(skb, hdd_sta_ctx)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present) |
| if (nla_put(skb, AP_INFO_HS20_INDICATION, |
| (sizeof(hdd_sta_ctx->cache_conn_info.hs20vendor_ie) |
| - 1), |
| tmp_hs20 + 1)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (nla_put_u32(skb, DISCONNECT_REASON, |
| adapter->last_disconnect_reason)) { |
| hdd_err("Failed to put disconect reason"); |
| goto fail; |
| } |
| |
| if (ie_len) { |
| if (nla_put(skb, BEACON_IES, ie_len, ies)) { |
| hdd_err("Failed to put beacon IEs"); |
| goto fail; |
| } |
| qdf_mem_free(ies); |
| ie_len = 0; |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| qdf_mem_free(ies); |
| ie_len = 0; |
| return -EINVAL; |
| } |
| |
| #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_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)); |
| } |
| |
| static uint32_t hdd_add_sta_capability_get_len(void) |
| { |
| return nla_total_size(sizeof(uint16_t)); |
| } |
| |
| /** |
| * 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_capability_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_NOISE_FLOOR_DBM)) { |
| 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; |
| } |
| if (nla_put_u16(skb, NL80211_ATTR_STA_CAPABILITY, |
| stainfo->capability)) { |
| hdd_err("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_peer_stats - get the peer stats |
| * @vdev_id: vdev id |
| * @mac_addr: mac address |
| * @stainfo: station info pointer |
| * |
| * Return: None |
| */ |
| static void hdd_get_peer_stats(uint8_t vdev_id, |
| struct qdf_mac_addr mac_addr, |
| struct hdd_station_info *stainfo) |
| { |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| struct cdp_peer_stats *peer_stats; |
| QDF_STATUS status; |
| |
| peer_stats = qdf_mem_malloc(sizeof(*peer_stats)); |
| if (!peer_stats) |
| return; |
| |
| status = cdp_host_get_peer_stats(soc, vdev_id, mac_addr.bytes, |
| peer_stats); |
| if (status == QDF_STATUS_SUCCESS) { |
| stainfo->rx_retry_cnt = peer_stats->rx.rx_retries; |
| stainfo->rx_mc_bc_cnt = peer_stats->rx.multicast.num + |
| peer_stats->rx.bcast.num; |
| } |
| |
| qdf_mem_free(peer_stats); |
| } |
| |
| /** |
| * 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 cached(deleted) peer's info |
| * |
| * Return: 0 on success, otherwise error value |
| */ |
| |
| 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; |
| struct sk_buff *skb = NULL; |
| uint32_t nl_buf_len = NLMSG_HDRLEN; |
| uint8_t channel_width; |
| |
| stainfo = hdd_get_sta_info_by_mac(&adapter->cache_sta_info_list, |
| mac_addr.bytes, |
| STA_INFO_GET_CACHED_STATION_REMOTE); |
| |
| if (!stainfo) { |
| hdd_err("peer " QDF_MAC_ADDR_FMT " not found", |
| QDF_MAC_ADDR_REF(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->dot11_mode) + NLA_HDRLEN) + |
| (sizeof(stainfo->ch_width) + NLA_HDRLEN) + |
| (sizeof(stainfo->tx_rate) + NLA_HDRLEN) + |
| (sizeof(stainfo->rx_rate) + NLA_HDRLEN) + |
| (sizeof(stainfo->support_mode) + NLA_HDRLEN) + |
| |
| (sizeof(stainfo->rx_mc_bc_cnt) + |
| NLA_HDRLEN) + |
| (sizeof(stainfo->rx_retry_cnt) + |
| NLA_HDRLEN); |
| if (stainfo->assoc_req_ies.len) |
| nl_buf_len += stainfo->assoc_req_ies.len + NLA_HDRLEN; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hdd_put_sta_info_ref(&adapter->cache_sta_info_list, |
| &stainfo, true, |
| STA_INFO_GET_CACHED_STATION_REMOTE); |
| hdd_err("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, |
| stainfo->support_mode) || |
| nla_put_u8(skb, REMOTE_CH_WIDTH, channel_width)) { |
| hdd_err("remote ch put fail"); |
| goto fail; |
| } |
| /* Convert the data from kbps to mbps as expected by the user space */ |
| if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate / 1000)) { |
| hdd_err("tx rate put fail"); |
| goto fail; |
| } |
| /* Convert the data from kbps to mbps as expected by the user space */ |
| if (nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate / 1000)) { |
| hdd_err("rx rate put fail"); |
| goto fail; |
| } |
| if (nla_put_u32(skb, WLAN802_11_MODE, stainfo->dot11_mode)) { |
| hdd_err("dot11 mode put fail"); |
| goto fail; |
| } |
| |
| if (!(stainfo->rx_mc_bc_cnt & HDD_STATION_INFO_RX_MC_BC_COUNT)) { |
| hdd_debug("rx mc bc count is not supported by FW"); |
| } |
| |
| else if (nla_put_u32(skb, REMOTE_RX_BC_MC_COUNT, |
| (stainfo->rx_mc_bc_cnt & |
| (~HDD_STATION_INFO_RX_MC_BC_COUNT)))) { |
| hdd_err("rx mc bc put fail"); |
| goto fail; |
| } |
| |
| /* Currently rx_retry count is not supported */ |
| if (stainfo->rx_retry_cnt) { |
| if (nla_put_u32(skb, REMOTE_RX_RETRY_COUNT, |
| stainfo->rx_retry_cnt)) { |
| hdd_err("rx retry count put fail"); |
| goto fail; |
| } |
| } |
| |
| if (stainfo->assoc_req_ies.len) { |
| if (nla_put(skb, ASSOC_REQ_IES, stainfo->assoc_req_ies.len, |
| stainfo->assoc_req_ies.data)) { |
| hdd_err("Failed to put assoc req IEs"); |
| goto fail; |
| } |
| } |
| hdd_sta_info_detach(&adapter->cache_sta_info_list, &stainfo); |
| hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &stainfo, true, |
| STA_INFO_GET_CACHED_STATION_REMOTE); |
| qdf_atomic_dec(&adapter->cache_sta_count); |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &stainfo, true, |
| STA_INFO_GET_CACHED_STATION_REMOTE); |
| 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; |
| struct nlattr *attr; |
| uint32_t nl_buf_len; |
| struct sir_peer_info_ext peer_info; |
| bool txrx_rate = true; |
| bool value; |
| QDF_STATUS status; |
| int i; |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += hdd_add_link_standard_info_sap_get_len() + |
| (NLA_ALIGN(sizeof(stainfo->max_phy_rate)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_packets)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_bytes)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_packets)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_bytes)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_mc_bc_cnt)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->is_qos_enabled)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->ch_width)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->dot11_mode)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->mode)) + NLA_HDRLEN); |
| |
| hdd_get_peer_stats(adapter->vdev_id, mac_addr, stainfo); |
| |
| status = ucfg_mlme_get_sap_get_peer_info(hdd_ctx->psoc, &value); |
| if (status != QDF_STATUS_SUCCESS) |
| hdd_err("Unable to fetch sap ger peer info"); |
| if (!value || |
| wlan_hdd_get_peer_info(adapter, mac_addr, &peer_info)) { |
| hdd_err("fail to get tx/rx rate"); |
| txrx_rate = false; |
| } else { |
| stainfo->tx_failed = peer_info.tx_failed; |
| |
| stainfo->rx_last_pkt_rssi = peer_info.rssi; |
| if (stainfo->rssi == 0) |
| stainfo->rssi = peer_info.rssi; |
| |
| for (i = 0; i < WMI_MAX_CHAINS; i++) |
| stainfo->peer_rssi_per_chain[i] = |
| peer_info.peer_rssi_per_chain[i]; |
| |
| stainfo->tx_retry_succeed = |
| peer_info.tx_retries - peer_info.tx_failed; |
| |
| stainfo->tx_retry = 0; |
| stainfo->tx_retry_exhaust = 0; |
| |
| stainfo->tx_total_fw = peer_info.tx_packets; |
| stainfo->tx_retry_fw = peer_info.tx_retries; |
| stainfo->tx_retry_exhaust_fw = peer_info.tx_failed; |
| |
| stainfo->tx_rate = peer_info.tx_rate; |
| stainfo->rx_rate = peer_info.rx_rate; |
| |
| nl_buf_len += |
| (NLA_ALIGN(sizeof(stainfo->rx_retry_cnt)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_failed)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_last_pkt_rssi)) + |
| NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_retry_succeed)) + |
| NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_retry)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_retry_exhaust)) + |
| NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_total_fw)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_retry_fw)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_retry_exhaust_fw)) + |
| NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_rate)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_rate)) + NLA_HDRLEN); |
| |
| nl_buf_len += NLA_HDRLEN; |
| for (i = 0; i < WMI_MAX_CHAINS; i++) |
| nl_buf_len += |
| (NLA_ALIGN(sizeof(stainfo->peer_rssi_per_chain[i])) + |
| NLA_HDRLEN); |
| } |
| |
| /* below info is only valid for HT/VHT mode */ |
| if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) |
| nl_buf_len += |
| (NLA_ALIGN(sizeof(stainfo->ampdu)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->tx_stbc)) + NLA_HDRLEN) + |
| (NLA_ALIGN(sizeof(stainfo->rx_stbc)) + NLA_HDRLEN) + |
| (NLA_ALIGN(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->is_qos_enabled, |
| stainfo->ch_width, |
| stainfo->sgi_enable); |
| } |
| |
| 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 (nla_put_u32(skb, REMOTE_RX_BC_MC_COUNT, |
| (stainfo->rx_mc_bc_cnt & |
| (~HDD_STATION_INFO_RX_MC_BC_COUNT))) || |
| nla_put_u8(skb, REMOTE_CH_WIDTH, stainfo->ch_width) || |
| nla_put_u32(skb, WLAN802_11_MODE, stainfo->dot11_mode) || |
| 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->is_qos_enabled) || |
| nla_put_u8(skb, REMOTE_SUPPORTED_MODE, stainfo->mode)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (txrx_rate) { |
| if (nla_put_u32(skb, REMOTE_TX_FAILURE, stainfo->tx_failed) || |
| nla_put_u32(skb, REMOTE_RX_RETRY_COUNT, |
| stainfo->rx_retry_cnt) || |
| nla_put_u32(skb, REMOTE_TX_RETRY_SUCCEED, |
| stainfo->tx_retry_succeed) || |
| nla_put_u32(skb, REMOTE_RX_LAST_PKT_RSSI, |
| stainfo->rx_last_pkt_rssi) || |
| nla_put_u32(skb, REMOTE_TX_RETRY, stainfo->tx_retry) || |
| nla_put_u32(skb, REMOTE_TX_RETRY_EXHAUST, |
| stainfo->tx_retry_exhaust) || |
| nla_put_u32(skb, REMOTE_TX_TOTAL_FW, |
| stainfo->tx_total_fw) || |
| nla_put_u32(skb, REMOTE_TX_RETRY_FW, |
| stainfo->tx_retry_fw) || |
| nla_put_u32(skb, REMOTE_TX_RETRY_EXHAUST_FW, |
| stainfo->tx_retry_exhaust_fw) || |
| 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; |
| } |
| |
| attr = nla_nest_start(skb, REMOTE_AVG_RSSI_PER_CHAIN); |
| if (!attr) |
| goto fail; |
| for (i = 0; i < WMI_MAX_CHAINS; i++) { |
| if (nla_put_u32(skb, i, |
| stainfo->peer_rssi_per_chain[i])) { |
| hdd_err("Failed to rssi per chain"); |
| goto fail; |
| } |
| } |
| nla_nest_end(skb, attr); |
| |
| 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_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_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) |
| { |
| int status = 0; |
| bool is_associated = false; |
| struct hdd_station_info *stainfo = |
| hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, |
| mac_addr.bytes, |
| STA_INFO_HDD_GET_STATION_REMOTE); |
| |
| 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); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, |
| STA_INFO_HDD_GET_STATION_REMOTE); |
| return status; |
| } |
| |
| status = hdd_get_connected_station_info(hdd_ctx, adapter, |
| mac_addr, stainfo); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &stainfo, true, |
| STA_INFO_HDD_GET_STATION_REMOTE); |
| return status; |
| } |
| |
| /** |
| * __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd |
| * @wiphy: corestack handler |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int |
| __hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + 1]; |
| int32_t status; |
| |
| hdd_enter_dev(dev); |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { |
| hdd_err("Command not allowed in FTM mode"); |
| status = -EPERM; |
| goto out; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status != 0) |
| goto out; |
| |
| status = wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX, |
| data, data_len, |
| hdd_get_station_policy); |
| if (status) { |
| hdd_err("Invalid ATTR"); |
| goto out; |
| } |
| |
| /* Parse and fetch Command Type*/ |
| if (tb[STATION_INFO]) { |
| 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 " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac_addr.bytes)); |
| |
| status = hdd_get_station_remote(hdd_ctx, adapter, mac_addr); |
| } else { |
| hdd_err("get station info cmd type failed"); |
| status = -EINVAL; |
| goto out; |
| } |
| hdd_exit(); |
| out: |
| return status; |
| } |
| |
| int32_t hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_cfg80211_get_station_cmd(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |
| static uint32_t |
| hdd_get_connect_fail_reason_code_len(struct hdd_adapter *adapter) |
| { |
| if (adapter->connect_req_status == STATUS_SUCCESS) |
| return 0; |
| |
| return nla_total_size(sizeof(uint32_t)); |
| } |
| |
| /** |
| * hdd_get_umac_to_osif_connect_fail_reason() - Convert to qca internal connect |
| * fail reason |
| * @internal_reason: Mac reason code of type @wlan_status_code |
| * |
| * Check if it is internal status code and convert it to the |
| * enum qca_sta_connect_fail_reason_codes. |
| * |
| * Return: Reason code of type enum qca_sta_connect_fail_reason_codes |
| */ |
| static enum qca_sta_connect_fail_reason_codes |
| hdd_get_umac_to_osif_connect_fail_reason(enum wlan_status_code internal_reason) |
| { |
| enum qca_sta_connect_fail_reason_codes reason = 0; |
| |
| if (internal_reason < STATUS_PROP_START) |
| return reason; |
| |
| switch (internal_reason) { |
| case STATUS_NO_NETWORK_FOUND: |
| reason = QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND; |
| break; |
| case STATUS_AUTH_TX_FAIL: |
| reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL; |
| break; |
| case STATUS_AUTH_NO_ACK_RECEIVED: |
| reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED; |
| break; |
| case STATUS_AUTH_NO_RESP_RECEIVED: |
| reason = QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED; |
| break; |
| case STATUS_ASSOC_TX_FAIL: |
| reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL; |
| break; |
| case STATUS_ASSOC_NO_ACK_RECEIVED: |
| reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED; |
| break; |
| case STATUS_ASSOC_NO_RESP_RECEIVED: |
| reason = QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED; |
| break; |
| default: |
| hdd_debug("QCA code not present for internal status code %d", |
| internal_reason); |
| } |
| |
| return reason; |
| } |
| |
| /** |
| * hdd_add_connect_fail_reason_code() - Fills connect fail reason code |
| * @skb: pointer to skb |
| * @adapter: pointer to hdd adapter |
| * |
| * Return: on success 0 else error code |
| */ |
| static int hdd_add_connect_fail_reason_code(struct sk_buff *skb, |
| struct hdd_adapter *adapter) |
| { |
| uint32_t reason; |
| |
| reason = hdd_get_umac_to_osif_connect_fail_reason( |
| adapter->connect_req_status); |
| if (!reason) |
| return 0; |
| |
| if (nla_put_u32(skb, STA_INFO_CONNECT_FAIL_REASON_CODE, reason)) { |
| hdd_err("put fail"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_get_station_info_ex() - send STA info to userspace, for STA mode only |
| * @hdd_ctx: pointer to hdd context |
| * @adapter: pointer to adapter |
| * |
| * Return: 0 if success else error status |
| */ |
| static int hdd_get_station_info_ex(struct hdd_context *hdd_ctx, |
| struct hdd_adapter *adapter) |
| { |
| struct sk_buff *skb; |
| uint32_t nl_buf_len = 0, connect_fail_rsn_len; |
| |
| connect_fail_rsn_len = hdd_get_connect_fail_reason_code_len(adapter); |
| nl_buf_len = connect_fail_rsn_len; |
| |
| nl_buf_len += NLMSG_HDRLEN; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hdd_err_rl("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| if (connect_fail_rsn_len) { |
| if (hdd_add_connect_fail_reason_code(skb, adapter)) { |
| hdd_err_rl("hdd_add_connect_fail_reason_code fail"); |
| return -ENOMEM; |
| } |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| } |
| |
| /** |
| * __hdd_cfg80211_get_sta_info_cmd() - Handle get sta info vendor cmd |
| * @wiphy: pointer to wireless phy |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int |
| __hdd_cfg80211_get_sta_info_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1]; |
| struct qdf_mac_addr mac_addr; |
| int32_t status; |
| |
| hdd_enter_dev(dev); |
| |
| if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE || |
| hdd_get_conparam() == QDF_GLOBAL_MONITOR_MODE) { |
| hdd_err_rl("Command not allowed in FTM / Monitor mode"); |
| status = -EPERM; |
| goto out; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (status != 0) |
| goto out; |
| |
| status = wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX, |
| data, data_len, |
| hdd_get_sta_policy); |
| if (status) { |
| hdd_err_rl("Invalid ATTR"); |
| goto out; |
| } |
| |
| switch (adapter->device_mode) { |
| case QDF_STA_MODE: |
| case QDF_P2P_CLIENT_MODE: |
| status = hdd_get_station_info_ex(hdd_ctx, adapter); |
| break; |
| case QDF_SAP_MODE: |
| case QDF_P2P_GO_MODE: |
| if (!tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC]) { |
| hdd_err_rl("MAC address is not present"); |
| status = -EINVAL; |
| goto out; |
| } |
| |
| nla_memcpy(mac_addr.bytes, |
| tb[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC], |
| QDF_MAC_ADDR_SIZE); |
| hdd_debug("STA " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac_addr.bytes)); |
| break; |
| default: |
| hdd_err_rl("Invalid device_mode: %d", adapter->device_mode); |
| status = -EINVAL; |
| goto out; |
| } |
| |
| hdd_exit(); |
| out: |
| return status; |
| } |
| |
| int32_t hdd_cfg80211_get_sta_info_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| int errno; |
| |
| errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); |
| if (errno) |
| return errno; |
| |
| errno = __hdd_cfg80211_get_sta_info_cmd(wiphy, wdev, data, data_len); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return errno; |
| } |
| |