summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.