summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVidyullatha Kanchanapally <vidyullatha@codeaurora.org>2017-03-10 04:37:26 +0530
committerSandeep Puligilla <spuligil@codeaurora.org>2017-03-20 15:49:05 -0700
commit005cb4d8db011e99a2b67c7b90df9cab2d04116f (patch)
treeab999ad97d124717afc9dce791a69db12c838280
parent40e1d24a1bfaed83fafadcf78c4d8aef92a9bfd1 (diff)
qcacld-3.0: Determine transition status for BSS transition candidates
Determine bss transition status for preferrable candidates provided by userspace based on the transition reason, rssi of connected and candidate bssids and other parameters like whether transitiong to the candidate will result in sub-optimal scenario. The transition status is either accept or a reason for reject. Change-Id: Ib83c81909f4d8e31b4125309b8ac392a26a0d6bf CRs-Fixed: 2007107
-rw-r--r--core/cds/inc/cds_ieee80211_defines.h17
-rw-r--r--core/hdd/src/wlan_hdd_cfg80211.c481
-rw-r--r--core/hdd/src/wlan_hdd_cfg80211.h72
-rw-r--r--core/sme/inc/sme_api.h10
-rw-r--r--core/sme/src/common/sme_api.c16
-rw-r--r--core/sme/src/csr/csr_api_scan.c52
-rw-r--r--core/sme/src/csr/csr_inside_api.h11
7 files changed, 657 insertions, 2 deletions
diff --git a/core/cds/inc/cds_ieee80211_defines.h b/core/cds/inc/cds_ieee80211_defines.h
index 037ad4295050..6845838b32b0 100644
--- a/core/cds/inc/cds_ieee80211_defines.h
+++ b/core/cds/inc/cds_ieee80211_defines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2014-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2014-2015, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1371,4 +1371,19 @@ enum {
IEEE80211_MACCMD_POLICY_RADIUS = 5, /* set policy: RADIUS managed ACLs */
};
+/*
+ * MBO transition reason codes
+ */
+enum {
+ MBO_TRANSITION_REASON_UNSPECIFIED,
+ MBO_TRANSITION_REASON_EXCESSIVE_FRAME_LOSS_RATE,
+ MBO_TRANSITION_REASON_EXCESSIVE_DELAY_FOR_CURRENT_TRAFFIC,
+ MBO_TRANSITION_REASON_INSUFFICIENT_BANDWIDTH_FOR_CURRENT_TRAFFIC,
+ MBO_TRANSITION_REASON_LOAD_BALANCING,
+ MBO_TRANSITION_REASON_LOW_RSSI,
+ MBO_TRANSITION_REASON_RECEIVED_EXCESSIVE_RETRANSMISSIONS,
+ MBO_TRANSITION_REASON_HIGH_INTERFERENCE,
+ MBO_TRANSITION_REASON_GRAY_ZONE,
+ MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP,
+};
#endif
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 10eefe7c3ae2..7c8bc6cd581c 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -8908,6 +8908,478 @@ static inline bool wlan_hdd_is_bt_in_progress(hdd_context_t *hdd_ctx)
return false;
}
+#ifdef FEATURE_WLAN_CH_AVOID
+/**
+ * wlan_hdd_is_channel_to_avoid() - Check channel to avoid
+ * @hdd_ctx : HDD contex
+ * @channel_id : channel to check
+ *
+ * This fuction checks if channel is unsafe to use. Unsafe channel here are
+ * LTE-Coex channels.
+ *
+ * Return : true if channel is unsafe to use else false.
+ */
+static bool wlan_hdd_is_channel_to_avoid(hdd_context_t *hdd_ctx,
+ uint16_t channel_id)
+{
+ uint16_t cnt;
+
+ for (cnt = 0; cnt < hdd_ctx->unsafe_channel_count; cnt++)
+ if (channel_id == hdd_ctx->unsafe_channel_list[cnt])
+ return true;
+
+ /* No matching channel */
+ return false;
+}
+#else
+static bool wlan_hdd_is_channel_to_avoid(hdd_context_t *hdd_ctx,
+ uint16_t channel_id)
+{
+ return false;
+}
+#endif
+
+/**
+ * wlan_hdd_is_mcc_channel() - check if using the channel results into MCC
+ * @adapter : pointer to adapter
+ * @channel : channel number to check for MCC scenario
+ *
+ * Return : true if channel causes MCC, else false
+ */
+static bool wlan_hdd_is_mcc_channel(hdd_adapter_t *adapter, uint8_t channel)
+{
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
+ hdd_adapter_t *p_adapter;
+ hdd_station_ctx_t *sta_ctx;
+ hdd_ap_ctx_t *ap_ctx;
+ hdd_hostapd_state_t *hostapd_state;
+ QDF_STATUS status;
+ uint8_t oper_channel = 0;
+
+ if (channel == 0)
+ return false;
+
+ status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
+ while (QDF_STATUS_SUCCESS == status && NULL != adapter_node) {
+ p_adapter = adapter_node->pAdapter;
+
+ if (p_adapter && p_adapter != adapter) {
+ if (QDF_STA_MODE == p_adapter->device_mode ||
+ QDF_P2P_CLIENT_MODE == p_adapter->device_mode) {
+ sta_ctx =
+ WLAN_HDD_GET_STATION_CTX_PTR(p_adapter);
+ if (eConnectionState_Associated ==
+ sta_ctx->conn_info.connState)
+ oper_channel =
+ sta_ctx->conn_info.operationChannel;
+ } else if (QDF_P2P_GO_MODE == p_adapter->device_mode ||
+ QDF_SAP_MODE == p_adapter->device_mode) {
+ ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(p_adapter);
+ hostapd_state =
+ WLAN_HDD_GET_HOSTAP_STATE_PTR(p_adapter);
+ if (hostapd_state->bssState == BSS_START &&
+ hostapd_state->qdf_status ==
+ QDF_STATUS_SUCCESS)
+ oper_channel = ap_ctx->operatingChannel;
+ }
+
+ if (oper_channel && channel != oper_channel &&
+ (!wma_is_hw_dbs_capable() ||
+ CDS_IS_SAME_BAND_CHANNELS(channel, oper_channel)))
+ return true;
+ }
+
+ status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
+ adapter_node = next;
+ }
+
+ return false;
+}
+
+/**
+ * wlan_hdd_get_status_for_candidate() - Get bss transition status for candidate
+ * @adapter : pointer to adapter
+ * @conn_bss_desc : connected bss descriptor
+ * @bss_desc : candidate bss descriptor
+ * @info : candiadate bss information
+ * @trans_reason : transition reason code
+ *
+ * Return : true if candidate is rejected and reject reason is filled
+ * @info->status. Otherwise returns false.
+ */
+static bool wlan_hdd_get_status_for_candidate(hdd_adapter_t *adapter,
+ tSirBssDescription *conn_bss_desc,
+ tSirBssDescription *bss_desc,
+ struct bss_candidate_info *info,
+ uint8_t trans_reason)
+{
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+
+ /* Low RSSI based rejection
+ * If candidate rssi is less than mbo_candidate_rssi_thres and connected
+ * bss rssi is greater than mbo_current_rssi_thres, then reject the
+ * candidate with MBO reason code 4.
+ */
+ if (bss_desc->rssi < hdd_ctx->config->mbo_candidate_rssi_thres &&
+ conn_bss_desc->rssi > hdd_ctx->config->mbo_current_rssi_thres) {
+ hdd_err("Candidate BSS "MAC_ADDRESS_STR" has LOW RSSI(%d), hence reject",
+ MAC_ADDR_ARRAY(bss_desc->bssId), bss_desc->rssi);
+ info->status = QCA_STATUS_REJECT_LOW_RSSI;
+ return true;
+ }
+
+ if (trans_reason == MBO_TRANSITION_REASON_LOAD_BALANCING ||
+ trans_reason == MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP) {
+ /* MCC rejection
+ * If moving to candidate's channel will result in MCC scenario
+ * and the rssi of connected bss is greater than
+ * mbo_current_rssi_mss_thres, then reject the candidate with
+ * MBO reason code 3.
+ */
+ if ((conn_bss_desc->rssi >
+ hdd_ctx->config->mbo_current_rssi_mcc_thres) &&
+ wlan_hdd_is_mcc_channel(adapter, bss_desc->channelId)) {
+ hdd_err("Candidate BSS "MAC_ADDRESS_STR" causes MCC, hence reject",
+ MAC_ADDR_ARRAY(bss_desc->bssId));
+ info->status =
+ QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY;
+ return true;
+ }
+
+ /* BT coex rejection
+ * If AP is trying to move the client from 5G to 2.4G and moving
+ * to 2.4G will result in BT coex and candidate channel rssi is
+ * less than mbo_candidate_rssi_btc_thres, then reject the
+ * candidate with MBO reason code 2.
+ */
+ if (CDS_IS_CHANNEL_5GHZ(conn_bss_desc->channelId) &&
+ CDS_IS_CHANNEL_24GHZ(bss_desc->channelId) &&
+ wlan_hdd_is_bt_in_progress(hdd_ctx) &&
+ (bss_desc->rssi <
+ hdd_ctx->config->mbo_candidate_rssi_btc_thres)) {
+ hdd_err("Candidate BSS "MAC_ADDRESS_STR" causes BT coex, hence reject",
+ MAC_ADDR_ARRAY(bss_desc->bssId));
+ info->status =
+ QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED;
+ return true;
+ }
+
+ /* LTE coex rejection
+ * If moving to candidate's channel can cause LTE coex, then
+ * reject the candidate with MBO reason code 5.
+ */
+ if (!wlan_hdd_is_channel_to_avoid(hdd_ctx,
+ conn_bss_desc->channelId) &&
+ wlan_hdd_is_channel_to_avoid(hdd_ctx,
+ bss_desc->channelId)) {
+ hdd_err("High interference expected if transitioned to BSS "
+ MAC_ADDRESS_STR" hence reject",
+ MAC_ADDR_ARRAY(bss_desc->bssId));
+ info->status =
+ QCA_STATUS_REJECT_HIGH_INTERFERENCE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * wlan_hdd_get_bss_transition_status() - get bss transition status all
+ * cadidates
+ * @adapter : Pointer to adapter
+ * @transition_reason : Transition reason
+ * @info : bss candidate information
+ * @n_candidates : number of candidates
+ *
+ * Return : 0 on success otherwise errno
+ */
+static int wlan_hdd_get_bss_transition_status(hdd_adapter_t *adapter,
+ uint8_t transition_reason,
+ struct bss_candidate_info *info,
+ uint16_t n_candidates)
+{
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ hdd_station_ctx_t *hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ tSirBssDescription *bss_desc, *conn_bss_desc;
+ tCsrScanResultInfo *res, *conn_res;
+ uint16_t i;
+
+ if (!n_candidates || !info) {
+ hdd_err("No candidate info available");
+ return -EINVAL;
+ }
+
+ /* Get the connected BSS descriptor */
+ conn_res = sme_scan_get_result_for_bssid(hdd_ctx->hHal,
+ &hdd_sta_ctx->conn_info.bssId);
+ if (!conn_res) {
+ hdd_err("Failed to find connected BSS in scan list");
+ return -EINVAL;
+ }
+ conn_bss_desc = &conn_res->BssDescriptor;
+
+ for (i = 0; i < n_candidates; i++) {
+ /* Get candidate BSS descriptors */
+ res = sme_scan_get_result_for_bssid(hdd_ctx->hHal,
+ &info[i].bssid);
+ if (!res) {
+ hdd_err("BSS "MAC_ADDRESS_STR" not present in scan list",
+ MAC_ADDR_ARRAY(info[i].bssid.bytes));
+ info[i].status = QCA_STATUS_REJECT_UNKNOWN;
+ continue;
+ }
+
+ bss_desc = &res->BssDescriptor;
+
+ if (!wlan_hdd_get_status_for_candidate(adapter, conn_bss_desc,
+ bss_desc, &info[i],
+ transition_reason)) {
+ /* If status is not over written, it means it is a
+ * candidate for accept.
+ */
+ info[i].status = QCA_STATUS_ACCEPT;
+ }
+
+ qdf_mem_free(res->pvIes);
+ qdf_mem_free(res);
+ }
+
+ /* free allocated memory */
+ qdf_mem_free(conn_res->pvIes);
+ qdf_mem_free(conn_res);
+
+ /* success */
+ return 0;
+}
+
+/**
+ * wlan_hdd_fill_btm_resp() - Fill bss candidate response buffer
+ * @reply_skb : pointer to reply_skb
+ * @info : bss candidate information
+ * @index : attribute type index for nla_next_start()
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int wlan_hdd_fill_btm_resp(struct sk_buff *reply_skb,
+ struct bss_candidate_info *info,
+ int index)
+{
+ struct nlattr *attr;
+
+ attr = nla_nest_start(reply_skb, index);
+ if (!attr) {
+ hdd_err("nla_nest_start failed");
+ kfree_skb(reply_skb);
+ return -EINVAL;
+ }
+
+ if (nla_put(reply_skb,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+ ETH_ALEN, info->bssid.bytes) ||
+ nla_put_u32(reply_skb,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS,
+ info->status)) {
+ hdd_err("nla_put failed");
+ kfree_skb(reply_skb);
+ return -EINVAL;
+ }
+
+ nla_nest_end(reply_skb, attr);
+
+ return 0;
+}
+
+/**
+ * __wlan_hdd_cfg80211_fetch_bss_transition_status () - fetch bss transition
+ * status
+ * @wiphy : WIPHY structure pointer
+ * @wdev : Wireless device structure pointer
+ * @data : Pointer to the data received
+ * @data_len : Length of the data received
+ *
+ * This fuction is used to fetch transition status for candidate bss. The
+ * transition status is either accept or reason for reject.
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int __wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+ uint8_t transition_reason;
+ struct nlattr *attr;
+ struct sk_buff *reply_skb;
+ int rem, j;
+ int ret;
+ struct bss_candidate_info candidate_info[MAX_CANDIDATE_INFO];
+ uint16_t nof_candidates, i = 0;
+ hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+ struct net_device *dev = wdev->netdev;
+ hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+ hdd_station_ctx_t *hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ const struct nla_policy
+ btm_params_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] = {
+ .type = NLA_U8},
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO] = {
+ .type = NLA_NESTED},
+ };
+ const struct nla_policy
+ btm_cand_list_policy[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]
+ = {[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+ .len = QDF_MAC_ADDR_SIZE},
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+ .type = NLA_U32},
+ };
+
+ ENTER();
+
+ if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+ hdd_err("Command not allowed in FTM mode");
+ return -EINVAL;
+ }
+
+ ret = wlan_hdd_validate_context(hdd_ctx);
+ if (ret)
+ return ret;
+
+ if (adapter->device_mode != QDF_STA_MODE ||
+ hdd_sta_ctx->conn_info.connState != eConnectionState_Associated) {
+ hdd_err("Command is either not invoked for STA mode (device mode: %d)"
+ "or STA is not associated (Connection state: %d)",
+ adapter->device_mode, hdd_sta_ctx->conn_info.connState);
+ return -EINVAL;
+ }
+
+ ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data,
+ data_len, btm_params_policy);
+ if (ret) {
+ hdd_err("Attribute parse failed");
+ return -EINVAL;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) {
+ hdd_err("Missing attributes");
+ return -EINVAL;
+ }
+
+ transition_reason = nla_get_u8(
+ tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON]);
+
+ nla_for_each_nested(attr,
+ tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+ rem) {
+ ret = nla_parse_nested(
+ tb_msg,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+ attr, btm_cand_list_policy);
+ if (ret) {
+ hdd_err("Attribute parse failed");
+ return -EINVAL;
+ }
+
+ if (!tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]) {
+ hdd_err("Missing BSSID attribute");
+ return -EINVAL;
+ }
+
+ qdf_mem_copy((void *)candidate_info[i].bssid.bytes,
+ nla_data(tb_msg[
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+ QDF_MAC_ADDR_SIZE);
+ i++;
+ if (i == MAX_CANDIDATE_INFO)
+ break;
+ }
+
+ /* Determine status for each candidate and fill in the status field.
+ * Also arrange the candidates in the order of preference.
+ */
+ nof_candidates = i;
+
+ ret = wlan_hdd_get_bss_transition_status(adapter, transition_reason,
+ candidate_info,
+ nof_candidates);
+ if (ret)
+ return -EINVAL;
+
+ /* Prepare the reply and send it to userspace */
+ reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
+ ((QDF_MAC_ADDR_SIZE + sizeof(uint32_t)) *
+ nof_candidates) + NLMSG_HDRLEN);
+ if (!reply_skb) {
+ hdd_err("reply buffer alloc failed");
+ return -ENOMEM;
+ }
+
+ attr = nla_nest_start(reply_skb,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+ if (!attr) {
+ hdd_err("nla_nest_start failed");
+ kfree_skb(reply_skb);
+ return -EINVAL;
+ }
+
+ /* Order candidates as - accepted candidate list followed by rejected
+ * candidate list
+ */
+ for (i = 0, j = 0; i < nof_candidates; i++) {
+ /* copy accepted candidate list */
+ if (candidate_info[i].status == QCA_STATUS_ACCEPT) {
+ if (wlan_hdd_fill_btm_resp(reply_skb,
+ &candidate_info[i], j))
+ return -EINVAL;
+ j++;
+ }
+ }
+ for (i = 0; i < nof_candidates; i++) {
+ /* copy rejected candidate list */
+ if (candidate_info[i].status != QCA_STATUS_ACCEPT) {
+ if (wlan_hdd_fill_btm_resp(reply_skb,
+ &candidate_info[i], j))
+ return -EINVAL;
+ j++;
+ }
+ }
+ nla_nest_end(reply_skb, attr);
+
+ EXIT();
+
+ return cfg80211_vendor_cmd_reply(reply_skb);
+}
+
+/**
+ * __wlan_hdd_cfg80211_fetch_bss_transition_status () - fetch bss transition
+ * status
+ * @wiphy : WIPHY structure pointer
+ * @wdev : Wireless device structure pointer
+ * @data : Pointer to the data received
+ * @data_len : Length of the data received
+ *
+ * This fuction is used to fetch transition status for candidate bss. The
+ * transition status is either accept or reason for reject.
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ int ret;
+
+ cds_ssr_protect(__func__);
+ ret = __wlan_hdd_cfg80211_fetch_bss_transition_status(wiphy, wdev,
+ data, data_len);
+ cds_ssr_unprotect(__func__);
+
+ return ret;
+}
+
const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
@@ -9545,6 +10017,15 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_nud_stats
},
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd =
+ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wlan_hdd_cfg80211_fetch_bss_transition_status
+ },
};
/**
diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h
index fac6d1df84a2..11da3be12dec 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.h
+++ b/core/hdd/src/wlan_hdd_cfg80211.h
@@ -148,6 +148,18 @@ static inline void wlan_hdd_clear_link_layer_stats(hdd_adapter_t *adapter) {}
#define USE_CFG80211_DEL_STA_V2
#endif
+#define MAX_CANDIDATE_INFO 10
+/**
+ * struct bss_candidate_info - Candidate bss information
+ *
+ * @bssid : BSSID of candidate bss
+ * @status : status code for candidate bss
+ */
+struct bss_candidate_info {
+ struct qdf_mac_addr bssid;
+ uint32_t status;
+};
+
/**
* enum eDFS_CAC_STATUS: CAC status
*
@@ -308,6 +320,18 @@ typedef enum {
* Start / Stop the NUD stats collections
* @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET
* Get the NUD stats, represented by the enum qca_attr_nud_stats_get
+ * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch
+ * the BSS transition status, whether accept or reject, for a list of
+ * candidate BSSIDs provided by the userspace. This uses the vendor
+ * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify
+ * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an
+ * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response
+ * the driver shall specify array of
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO.
*/
enum qca_nl80211_vendor_subcmds {
@@ -466,6 +490,9 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149,
QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
+
+ /* Fetch BSS transition status */
+ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
};
/**
@@ -943,6 +970,18 @@ enum qca_wlan_vendor_attr_get_tdls_capabilities {
* @QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS: Supported Features
*@QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE: setband attribute which is used by
* QCA_NL80211_VENDOR_SUBCMD_SETBAND
+ * @QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON : Unsigned 8-bit value
+ * representing MBO transition reason code as provided by the AP used by
+ * subcommand QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS.
+ * This is specified by the userspace in the request to the driver.
+ *@QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO : Array of nested attributes, BSSID
+ * and status code, used by subcommand
+ * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each entry
+ * is taken from enum qca_wlan_vendor_attr_btm_candidate_info. The
+ * userspace space specifies the list/array of candidate BSSIDs in the
+ * order of preference in the request. The driver specifies the status
+ * code, for each BSSID in the list, in the response. The acceptable
+ * candidates are listed in the order preferred by the driver.
* @QCA_WLAN_VENDOR_ATTR_AFTER_LAST: After last
* @QCA_WLAN_VENDOR_ATTR_MAX: Max value
*/
@@ -960,6 +999,9 @@ enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
+ QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1
@@ -3419,6 +3461,36 @@ enum qca_wlan_vendor_attr_ll_stats_ext {
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
};
+enum qca_wlan_btm_candidate_status {
+ QCA_STATUS_ACCEPT,
+ QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED,
+ QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED,
+ QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY,
+ QCA_STATUS_REJECT_LOW_RSSI,
+ QCA_STATUS_REJECT_HIGH_INTERFERENCE,
+ QCA_STATUS_REJECT_UNKNOWN,
+};
+
+enum qca_wlan_vendor_attr_btm_candidate_info {
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0,
+
+ /* 6-byte MAC address representing the bssid of transition candidate */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1,
+ /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status
+ * returned by the driver. It says whether the bssid provided in
+ * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by
+ * the driver, if not it specifies the reason for rejection.
+ * Note that the user-space can overwrite the transition reject reason
+ * codes provided by driver based on more information.
+ */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1,
+};
+
struct cfg80211_bss *wlan_hdd_cfg80211_update_bss_db(hdd_adapter_t *pAdapter,
tCsrRoamInfo *pRoamInfo);
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index 313b3222d5eb..8eeb5aa02fae 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -1523,4 +1523,14 @@ void sme_set_5g_band_pref(tHalHandle hal_handle,
QDF_STATUS sme_set_bt_activity_info_cb(tHalHandle hal,
void (*cb)(void *, uint32_t profile_info));
+/**
+ * sme_scan_get_result_for_bssid - gets the scan result from scan cache for the
+ * bssid specified
+ * @hal: handle returned by mac_open
+ * @bssid: bssid to get the scan result for
+ *
+ * Return: tCsrScanResultInfo * or NULL if no result
+ */
+tCsrScanResultInfo *sme_scan_get_result_for_bssid(tHalHandle hal_handle,
+ struct qdf_mac_addr *bssid);
#endif /* #if !defined( __SME_API_H ) */
diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c
index 10c8d8faa91c..73ff75d5ab78 100644
--- a/core/sme/src/common/sme_api.c
+++ b/core/sme/src/common/sme_api.c
@@ -3505,6 +3505,22 @@ QDF_STATUS sme_scan_get_result(tHalHandle hHal, uint8_t sessionId,
return status;
}
+tCsrScanResultInfo *sme_scan_get_result_for_bssid(tHalHandle hal_handle,
+ struct qdf_mac_addr *bssid)
+{
+ tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal_handle);
+ QDF_STATUS status;
+ tCsrScanResultInfo *res = NULL;
+
+ status = sme_acquire_global_lock(&mac_ctx->sme);
+ if (QDF_IS_STATUS_SUCCESS(status)) {
+ res = csr_scan_get_result_for_bssid(hal_handle, bssid);
+ sme_release_global_lock(&mac_ctx->sme);
+ }
+
+ return res;
+}
+
/**
* sme_get_ap_channel_from_scan_cache() - a wrapper function to get AP's
* channel id from CSR by filtering the
diff --git a/core/sme/src/csr/csr_api_scan.c b/core/sme/src/csr/csr_api_scan.c
index 6e7d914b762a..d4d05d226c47 100644
--- a/core/sme/src/csr/csr_api_scan.c
+++ b/core/sme/src/csr/csr_api_scan.c
@@ -2151,7 +2151,7 @@ QDF_STATUS csr_scan_get_result(tpAniSirGlobal pMac,
pRetList->pCurEntry = NULL;
status = csr_parse_scan_results(pMac, pFilter, pRetList, &count);
sms_log(pMac, LOGD, FL("return %d BSS %d"),
- csr_ll_count(&pRetList->List), status);
+ status, csr_ll_count(&pRetList->List));
if (!QDF_IS_STATUS_SUCCESS(status) || (phResult == NULL)) {
/* Fail or No one wants the result. */
csr_scan_result_purge(pMac, (tScanResultHandle) pRetList);
@@ -2168,6 +2168,56 @@ QDF_STATUS csr_scan_get_result(tpAniSirGlobal pMac,
return status;
}
+tCsrScanResultInfo *csr_scan_get_result_for_bssid(tpAniSirGlobal mac_ctx,
+ struct qdf_mac_addr *bssid)
+{
+ tDblLinkList *list = &mac_ctx->scan.scanResultList;
+ tListElem *entry;
+ tCsrScanResultInfo *scan_info = NULL;
+ tCsrScanResult *scan_res;
+ size_t bss_len, len;
+ tDot11fBeaconIEs *ies = NULL;
+ QDF_STATUS status;
+
+ csr_ll_lock(list);
+ entry = csr_ll_peek_head(list, LL_ACCESS_NOLOCK);
+ while (entry) {
+ scan_res = GET_BASE_ADDR(entry, tCsrScanResult, Link);
+ if (qdf_mem_cmp(scan_res->Result.BssDescriptor.bssId,
+ bssid->bytes, QDF_MAC_ADDR_SIZE) == 0) {
+ /* Found matching scan resut for this bssid */
+ bss_len = scan_res->Result.BssDescriptor.length +
+ sizeof(scan_res->Result.BssDescriptor.length);
+ len = sizeof(*scan_info) + bss_len;
+ scan_info = qdf_mem_malloc(len);
+ if (!scan_info) {
+ sms_log(mac_ctx, LOGE,
+ FL("Failed to allocate memory for scan info, len = %lu"),
+ len);
+ break;
+ }
+ scan_info->ssId = scan_res->Result.ssId;
+ scan_info->timer = scan_res->Result.timer;
+ qdf_mem_copy(&scan_info->BssDescriptor,
+ &scan_res->Result.BssDescriptor, bss_len);
+ status = csr_get_parsed_bss_description_ies(
+ mac_ctx,
+ &scan_info->BssDescriptor,
+ &ies);
+ if (QDF_IS_STATUS_ERROR(status)) {
+ qdf_mem_free(scan_info);
+ scan_info = NULL;
+ }
+ scan_info->pvIes = ies;
+ break;
+ }
+ entry = csr_ll_next(list, entry, LL_ACCESS_NOLOCK);
+ }
+ csr_ll_unlock(list);
+
+ return scan_info;
+}
+
/*
* NOTE: This routine is being added to make
* sure that scan results are not being flushed
diff --git a/core/sme/src/csr/csr_inside_api.h b/core/sme/src/csr/csr_inside_api.h
index d1610e87edbf..fb5c08356dce 100644
--- a/core/sme/src/csr/csr_inside_api.h
+++ b/core/sme/src/csr/csr_inside_api.h
@@ -539,6 +539,17 @@ QDF_STATUS csrScanAbort(tpAniSirGlobal, uint32_t scanRequestID);
QDF_STATUS csr_scan_get_result(tpAniSirGlobal, tCsrScanResultFilter *pFilter,
tScanResultHandle *phResult);
+/**
+ * csr_scan_get_result_for_bssid - gets the scan result from scan cache for the
+ * bssid specified
+ * @mac_ctx: mac context
+ * @bssid: bssid to get the scan result for
+ *
+ * Return: tCsrScanResultInfo * or NULL if no result
+ */
+tCsrScanResultInfo *csr_scan_get_result_for_bssid(tpAniSirGlobal mac_ctx,
+ struct qdf_mac_addr *bssid);
+
/* ---------------------------------------------------------------------------
\fn csr_scan_flush_result
\brief Clear scan results.