Ver código fonte

qcacld-3.0: Add support for external auto channel selection

Add changes to enabled driver to use external auto selection
logic instead of current driver based implementation.
In case external application is not present, driver will fall
back to current solution.

Change-Id: Ib7474e82bc7f5878af46c91ea09711775a7c046a
CRs-Fixed: 1110061
Kapil Gupta 8 anos atrás
pai
commit
8878ad9517

+ 46 - 0
core/hdd/inc/wlan_hdd_cfg.h

@@ -4683,6 +4683,50 @@ enum dot11p_mode {
 #define CFG_CREATE_BUG_REPORT_FOR_SCAN_ENABLE     (1)
 #define CFG_CREATE_BUG_REPORT_FOR_SCAN_DEFAULT    (0)
 
+/*
+ * <ini>
+ * gvendor_acs_support - vendor based channel selection manager
+ * @Min: 0
+ * @Max: 1
+ * @Default: 0
+ *
+ * Enabling this parameter will force driver to use user application based
+ * channel selection algo instead of driver based auto channel selection
+ * logic.
+ *
+ * Supported Feature: ACS
+ *
+ * Usage: External/Internal
+ *
+ * </ini>
+ */
+#define CFG_USER_AUTO_CHANNEL_SELECTION       "gvendor_acs_support"
+#define CFG_USER_AUTO_CHANNEL_SELECTION_DISABLE   (0)
+#define CFG_USER_AUTO_CHANNEL_SELECTION_ENABLE    (1)
+#define CFG_USER_AUTO_CHANNEL_SELECTION_DEFAULT   (0)
+
+/*
+ * <ini>
+ * gacs_support_for_dfs_lte_coex - acs support for lte coex and dfs event
+ * @Min: 0
+ * @Max: 1
+ * @Default: 0
+ *
+ * Enabling this parameter will force driver to use user application based
+ * channel selection algo for channel selection in case of dfs and lte
+ * coex event.
+ *
+ * Supported Feature: ACS
+ *
+ * Usage: Internal
+ *
+ * </ini>
+ */
+#define CFG_USER_ACS_DFS_LTE           "gacs_support_for_dfs_lte_coex"
+#define CFG_USER_ACS_DFS_LTE_DISABLE   (0)
+#define CFG_USER_ACS_DFS_LTE_ENABLE    (1)
+#define CFG_USER_ACS_DFS_LTE_DEFAULT   (0)
+
 /*
  * Enabling gignore_peer_ht_opmode will enable 11g
  * protection only when there is a 11g AP in vicinity.
@@ -6518,6 +6562,8 @@ struct hdd_config {
 	uint8_t adapt_dwell_lpf_weight;
 	uint8_t adapt_dwell_passive_mon_intval;
 	uint8_t adapt_dwell_wifi_act_threshold;
+	bool vendor_acs_support;
+	bool acs_support_for_dfs_ltecoex;
 	bool bug_report_for_no_scan_results;
 	bool bug_on_reinit_failure;
 #ifdef WLAN_FEATURE_NAN_DATAPATH

+ 94 - 0
core/hdd/inc/wlan_hdd_main.h

@@ -107,6 +107,8 @@
 #define DEVICE_IFACE_OPENED    (5)
 #define TDLS_INIT_DONE         (6)
 #define ACS_PENDING            (7)
+/* Waiting for event for vendor acs */
+#define VENDOR_ACS_RESPONSE_PENDING   (8)
 
 /* HDD global event flags */
 #define ACS_IN_PROGRESS        (0)
@@ -159,6 +161,9 @@
 
 #define MAX_CFG_STRING_LEN  255
 
+/* Maximum time(ms) to wait for external acs response */
+#define WLAN_VENDOR_ACS_WAIT_TIME 1000
+
 /* SSR Retry Count */
 #define HDD_MOD_EXIT_SSR_MAX_RETRIES 75
 
@@ -803,6 +808,8 @@ struct hdd_ap_ctx_s {
 	void *sapContext;
 
 	bool dfs_cac_block_tx;
+	qdf_mc_timer_t vendor_acs_timer;
+	bool vendor_acs_timer_initialized;
 
 	enum bss_stop_reason bss_stop_reason;
 };
@@ -1611,6 +1618,46 @@ struct hdd_context_s {
 	uint8_t beacon_probe_rsp_cnt_per_scan;
 };
 
+/**
+ * struct hdd_vendor_acs_chan_params - vendor acs channel parameters
+ * @channel_count: channel count
+ * @channel_list: pointer to channel list
+ * @vendor_pcl_list: pointer to pcl list
+ * @vendor_weight_list: pointer to pcl weight list
+ */
+struct hdd_vendor_acs_chan_params {
+	uint32_t channel_count;
+	uint8_t *channel_list;
+	uint8_t *vendor_pcl_list;
+	uint8_t *vendor_weight_list;
+};
+
+/**
+ * struct hdd_external_acs_timer_context - acs timer context
+ * @reason: reason for acs trigger
+ * @adapter: hdd adapter for acs
+ */
+struct hdd_external_acs_timer_context {
+	int8_t reason;
+	hdd_adapter_t *adapter;
+};
+
+/**
+ * struct hdd_vendor_chan_info - vendor channel info
+ * @pri_ch: primary channel
+ * @ht_sec_ch: secondary channel
+ * @vht_seg0_center_ch: segment0 for vht
+ * @vht_seg1_center_ch: vht segment 1
+ * @chan_width: channel width
+ */
+struct hdd_vendor_chan_info {
+	uint8_t pri_ch;
+	uint8_t ht_sec_ch;
+	uint8_t vht_seg0_center_ch;
+	uint8_t vht_seg1_center_ch;
+	uint8_t chan_width;
+};
+
 /**
  * struct  hdd_channel_info - standard channel info
  * @freq: Freq in Mhz
@@ -1796,7 +1843,54 @@ int wlan_hdd_scan_abort(hdd_adapter_t *pAdapter);
 void hdd_get_fw_version(hdd_context_t *hdd_ctx,
 			uint32_t *major_spid, uint32_t *minor_spid,
 			uint32_t *siid, uint32_t *crmid);
+/**
+ * hdd_acs_response_timeout_handler() - timeout handler for acs_timer
+ * @context : timeout handler context
+ *
+ * Return: None
+ */
+void hdd_acs_response_timeout_handler(void *context);
 
+/**
+ * wlan_hdd_cfg80211_start_acs(): Start ACS Procedure for SAP
+ * @adapter: pointer to SAP adapter struct
+ *
+ * This function starts the ACS procedure if there are no
+ * constraints like MBSSID DFS restrictions.
+ *
+ * Return: Status of ACS Start procedure
+ */
+int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter);
+
+/**
+ * hdd_cfg80211_update_acs_config() - update acs config to application
+ * @adapter: hdd adapter
+ * @reason: channel change reason
+ *
+ * Return: none
+ */
+void hdd_cfg80211_update_acs_config(hdd_adapter_t *adapter,
+				    uint8_t reason);
+/**
+ * hdd_update_acs_timer_reason() - update acs timer start reason
+ * @adapter: hdd adapter
+ * @reason: channel change reason
+ *
+ * Return: 0 for success
+ */
+int hdd_update_acs_timer_reason(hdd_adapter_t *adapter, uint8_t reason);
+
+/**
+ * hdd_restart_sap() - Restarts SAP on the given channel
+ * @adapter: AP adapter
+ * @channel: Channel
+ *
+ * Restarts the SAP interface by invoking the function which executes the
+ * callback to perform channel switch using (E)CSA.
+ *
+ * Return: None
+ */
+void hdd_restart_sap(hdd_adapter_t *adapter, uint8_t channel);
 #ifdef WLAN_FEATURE_MEMDUMP
 /**
  * hdd_is_memdump_supported() - to check if memdump feature support

+ 14 - 0
core/hdd/src/wlan_hdd_cfg.c

@@ -3941,6 +3941,20 @@ REG_TABLE_ENTRY g_registry_table[] = {
 		CFG_CREATE_BUG_REPORT_FOR_SCAN_DISABLE,
 		CFG_CREATE_BUG_REPORT_FOR_SCAN_ENABLE),
 
+	REG_VARIABLE(CFG_USER_AUTO_CHANNEL_SELECTION, WLAN_PARAM_Integer,
+		struct hdd_config, vendor_acs_support,
+		VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+		CFG_USER_AUTO_CHANNEL_SELECTION_DEFAULT,
+		CFG_USER_AUTO_CHANNEL_SELECTION_DISABLE,
+		CFG_USER_AUTO_CHANNEL_SELECTION_ENABLE),
+
+	REG_VARIABLE(CFG_USER_ACS_DFS_LTE, WLAN_PARAM_Integer,
+		struct hdd_config, acs_support_for_dfs_ltecoex,
+		VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+		CFG_USER_ACS_DFS_LTE_DEFAULT,
+		CFG_USER_ACS_DFS_LTE_DISABLE,
+		CFG_USER_ACS_DFS_LTE_ENABLE),
+
 	REG_VARIABLE(CFG_ENABLE_DP_TRACE, WLAN_PARAM_Integer,
 		struct hdd_config, enable_dp_trace,
 		VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,

+ 572 - 15
core/hdd/src/wlan_hdd_cfg80211.c

@@ -1101,6 +1101,10 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
 		.vendor_id = QCA_NL80211_VENDOR_ID,
 		.subcmd = QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH
 	},
+	[QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG] = {
+		.vendor_id = QCA_NL80211_VENDOR_ID,
+		.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
+	},
 #ifdef WLAN_UMAC_CONVERGENCE
 	COMMON_VENDOR_EVENTS
 #endif
@@ -1336,8 +1340,7 @@ static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work);
  *
  * Return: Status of ACS Start procedure
  */
-
-static int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter)
+int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter)
 {
 
 	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
@@ -1372,7 +1375,7 @@ static int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter)
 
 	qdf_mem_copy(sap_config->self_macaddr.bytes,
 		adapter->macAddressCurrent.bytes, sizeof(struct qdf_mac_addr));
-	hdd_notice("ACS Started for wlan%d", adapter->dev->ifindex);
+	hdd_notice("ACS Started for %s", adapter->dev->name);
 	status = wlansap_acs_chselect(
 		WLAN_HDD_GET_SAP_CTX_PTR(adapter),
 		acs_event_callback, sap_config, adapter->dev);
@@ -1412,6 +1415,54 @@ static int wlan_hdd_sap_get_nol(hdd_adapter_t *ap_adapter, uint8_t *nol,
 	return 0;
 }
 
+/**
+ * hdd_update_vendor_pcl_list() - This API will return unsorted pcl list
+ * @hdd_ctx: hdd context
+ * @acs_chan_params: external acs channel params
+ * @sap_config: SAP config
+ *
+ * This API provides unsorted pcl list.
+ * this list is a subset of the valid channel list given by hostapd.
+ * if channel is not present in pcl, weightage will be given as zero
+ *
+ * Return: Zero on success, non-zero on failure
+ */
+static void hdd_update_vendor_pcl_list(hdd_context_t *hdd_ctx,
+		struct hdd_vendor_acs_chan_params *acs_chan_params,
+		tsap_Config_t *sap_config)
+{
+	int i, j;
+
+	for (i = 0; i < acs_chan_params->channel_count; i++) {
+		for (j = 0; j < sap_config->acs_cfg.pcl_ch_count; j++) {
+			if (acs_chan_params->channel_list[i] ==
+				sap_config->acs_cfg.pcl_channels[j]) {
+				acs_chan_params->vendor_pcl_list[i] =
+					sap_config->acs_cfg.pcl_channels[j];
+				acs_chan_params->vendor_weight_list[i] =
+					sap_config->acs_cfg.
+						pcl_channels_weight_list[j];
+				break;
+			} else {
+				acs_chan_params->vendor_pcl_list[i] =
+					acs_chan_params->channel_list[i];
+				acs_chan_params->vendor_weight_list[i] = 0;
+			}
+		}
+	}
+	if (hdd_ctx->unsafe_channel_count == 0)
+		return;
+	/* Update unsafe channel weight as zero */
+	for (i = 0; i < acs_chan_params->channel_count; i++) {
+		for (j = 0; j < hdd_ctx->unsafe_channel_count; j++) {
+			if (acs_chan_params->channel_list[i] ==
+				hdd_ctx->unsafe_channel_list[j]) {
+				acs_chan_params->vendor_weight_list[i] = 0;
+			}
+		}
+	}
+}
+
 /**
  * hdd_update_reg_chan_info : This API contructs channel info
  * for all the given channel
@@ -1421,13 +1472,13 @@ static int wlan_hdd_sap_get_nol(hdd_adapter_t *ap_adapter, uint8_t *nol,
  *
  * Return: Status of of channel information updation
  */
-int hdd_update_reg_chan_info(hdd_adapter_t *adapter,
+static int hdd_update_reg_chan_info(hdd_adapter_t *adapter,
 			uint32_t channel_count,
 			uint8_t *channel_list)
 {
 	int i;
 	struct hdd_channel_info *icv;
-	struct ch_params_s ch_params;
+	struct ch_params_s ch_params = {0};
 	uint8_t bw_offset = 0, chan = 0;
 	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 	tsap_Config_t *sap_config = &adapter->sessionCtx.ap.sapConfig;
@@ -1520,7 +1571,7 @@ int hdd_update_reg_chan_info(hdd_adapter_t *adapter,
  *
  * Return: Success(0) or reason code for failure
  */
-int32_t
+static int32_t
 hdd_cfg80211_update_channel_info(struct sk_buff *skb,
 			   tsap_Config_t *sap_config, int idx)
 {
@@ -1584,6 +1635,185 @@ fail:
 #undef CHAN_INFO_ATTR_VHT_SEG_0
 #undef CHAN_INFO_ATTR_VHT_SEG_1
 
+/**
+ * hdd_cfg80211_update_pcl() - add pcl info attributes
+ * @skb: pointer to sk buff
+ * @hdd_ctx: pointer to hdd station context
+ * @idx: attribute index
+ * @vendor_pcl_list: PCL list
+ * @vendor_weight_list: PCL weights
+ *
+ * Return: Success(0) or reason code for failure
+ */
+static int32_t
+hdd_cfg80211_update_pcl(struct sk_buff *skb,
+			uint8_t ch_list_count, int idx,
+			uint8_t *vendor_pcl_list, uint8_t *vendor_weight_list)
+{
+	struct nlattr *nla_attr, *channel;
+	int i;
+
+	nla_attr = nla_nest_start(skb, idx);
+
+	if (!nla_attr)
+		goto fail;
+
+	for (i = 0; i < ch_list_count; i++) {
+		channel = nla_nest_start(skb, i);
+		if (!channel)
+			goto fail;
+		if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_CHANNEL,
+			       vendor_pcl_list[i]) ||
+		    nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_WEIGHT,
+			       vendor_weight_list[i])) {
+			hdd_err("put fail");
+			goto fail;
+		}
+		nla_nest_end(skb, channel);
+	}
+	nla_nest_end(skb, nla_attr);
+
+	return 0;
+fail:
+	hdd_err("updating pcl list failed");
+	return -EINVAL;
+}
+
+static void hdd_get_scan_band(tsap_Config_t *sap_config, eCsrBand *band)
+{
+	/* Get scan band */
+	if ((sap_config->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211B) ||
+	   (sap_config->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211G)) {
+		*band = eCSR_BAND_24;
+	} else if (sap_config->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211A) {
+		*band = eCSR_BAND_5G;
+	} else if (sap_config->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211ANY) {
+		*band = eCSR_BAND_ALL;
+	}
+	/* Auto is not supported currently */
+	if (!((*band == eCSR_BAND_24) || (eCSR_BAND_5G == *band))) {
+		hdd_err("invalid band");
+		*band = eCSR_BAND_24;
+	}
+}
+
+void hdd_cfg80211_update_acs_config(hdd_adapter_t *adapter,
+				    uint8_t reason)
+{
+	struct sk_buff *skb;
+	tsap_Config_t *sap_config;
+	uint32_t channel_count = 0, status;
+	uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0};
+	uint8_t vendor_pcl_list[QDF_MAX_NUM_CHAN] = {0};
+	uint8_t vendor_weight_list[QDF_MAX_NUM_CHAN] = {0};
+	struct hdd_vendor_acs_chan_params acs_chan_params;
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	eCsrBand band = eCSR_BAND_24;
+	eCsrPhyMode phy_mode;
+
+	if (!hdd_ctx) {
+		hdd_err("HDD context is NULL");
+		return;
+	}
+
+	ENTER();
+	sap_config = &adapter->sessionCtx.ap.sapConfig;
+
+	/* Get valid channels for SAP */
+	wlan_hdd_sap_get_valid_channellist(adapter,
+					   &channel_count, channel_list);
+
+	hdd_update_reg_chan_info(adapter, channel_count, channel_list);
+	hdd_get_scan_band(&adapter->sessionCtx.ap.sapConfig, &band);
+	/* Get phymode */
+	phy_mode = sme_get_phy_mode(WLAN_HDD_GET_HAL_CTX(adapter));
+
+	skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
+			&(adapter->wdev),
+			EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
+			QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG,
+			GFP_KERNEL);
+
+	if (!skb) {
+		hdd_err("cfg80211_vendor_event_alloc failed");
+		return;
+	}
+	/*
+	 * Application expects pcl to be a subset of channel list
+	 * Remove all channels which are not in channel list from pcl
+	 * and add weight as zero
+	 */
+	acs_chan_params.channel_count = channel_count;
+	acs_chan_params.channel_list = channel_list;
+	acs_chan_params.vendor_pcl_list = vendor_pcl_list;
+	acs_chan_params.vendor_weight_list = vendor_weight_list;
+
+	hdd_update_vendor_pcl_list(hdd_ctx, &acs_chan_params,
+				   sap_config);
+	/* Update values in NL buffer */
+	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON,
+		       reason) ||
+	    nla_put_u8(skb,
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED,
+		       false) ||
+	    nla_put_u8(skb,
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED,
+		       true) ||
+	    nla_put_u8(skb,
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT,
+		       true) ||
+	    nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH,
+		       sap_config->acs_cfg.ch_width) ||
+	    nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAC_ADDR,
+		    QDF_MAC_ADDR_SIZE, adapter->macAddressCurrent.bytes) ||
+	    nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND,
+		       band) ||
+	    nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE,
+		       phy_mode) ||
+	    nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHANLIST,
+		    channel_count, channel_list)) {
+		hdd_err("nla put fail");
+		goto fail;
+	}
+	status = hdd_cfg80211_update_pcl(skb, channel_count,
+			QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL,
+			vendor_pcl_list, vendor_weight_list);
+
+	if (status != 0)
+		goto fail;
+
+	status = hdd_cfg80211_update_channel_info(skb, sap_config,
+			QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO);
+
+	if (status != 0)
+		goto fail;
+
+	cfg80211_vendor_event(skb, GFP_KERNEL);
+	return;
+fail:
+	if (skb)
+		kfree_skb(skb);
+}
+
+static int hdd_create_acs_timer(hdd_adapter_t *adapter)
+{
+	struct hdd_external_acs_timer_context *timer_context;
+
+	if (adapter->sessionCtx.ap.vendor_acs_timer_initialized)
+		return 0;
+
+	hdd_notice("Starting vendor app based ACS");
+	timer_context = qdf_mem_malloc(sizeof(*timer_context));
+	timer_context->adapter = adapter;
+
+	set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
+	qdf_mc_timer_init(&adapter->sessionCtx.ap.vendor_acs_timer,
+		  QDF_TIMER_TYPE_SW,
+		  hdd_acs_response_timeout_handler, timer_context);
+	adapter->sessionCtx.ap.vendor_acs_timer_initialized = true;
+	return 0;
+}
+
 /**
  * __wlan_hdd_cfg80211_do_acs : CFG80211 handler function for DO_ACS Vendor CMD
  * @wiphy:  Linux wiphy struct pointer
@@ -1609,8 +1839,7 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 	int status = -EINVAL, i = 0;
 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
 	bool ht_enabled, ht40_enabled, vht_enabled;
-	uint8_t ch_width;
-	uint8_t weight_list[QDF_MAX_NUM_CHAN];
+	uint8_t ch_width, hw_mode;
 
 	/* ***Note*** Donot set SME config related to ACS operation here because
 	 * ACS operation is not synchronouse and ACS for Second AP may come when
@@ -1638,7 +1867,8 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 		return -EPERM;
 	}
 
-	if (hdd_ctx->config->force_sap_acs) {
+	if (hdd_ctx->config->force_sap_acs &&
+	    !hdd_ctx->config->vendor_acs_support) {
 		hdd_err("Hostapd ACS rejected as Driver ACS enabled");
 		return -EPERM;
 	}
@@ -1667,8 +1897,8 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 		hdd_err("Attr hw_mode failed");
 		goto out;
 	}
-	sap_config->acs_cfg.hw_mode = nla_get_u8(
-					tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+	hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+	sap_config->acs_cfg.hw_mode = hw_mode;
 
 	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED])
 		ht_enabled =
@@ -1768,7 +1998,8 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 	status = cds_get_pcl(CDS_SAP_MODE,
 				sap_config->acs_cfg.pcl_channels,
 				&sap_config->acs_cfg.pcl_ch_count,
-				weight_list, QDF_ARRAY_SIZE(weight_list));
+				sap_config->acs_cfg.pcl_channels_weight_list,
+				QDF_MAX_NUM_CHAN);
 	if (QDF_STATUS_SUCCESS != status)
 		hdd_err("Get PCL failed");
 
@@ -1782,8 +2013,10 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 					hdd_ctx->config->vhtChannelWidth;
 		/* No VHT80 in 2.4G so perform ACS accordingly */
 		if (sap_config->acs_cfg.end_ch <= 14 &&
-			sap_config->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ)
+		    sap_config->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ) {
 			sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_40MHZ;
+			ch_width = 40;
+		}
 	}
 
 	wlan_hdd_set_acs_ch_range(sap_config, ht_enabled, vht_enabled);
@@ -1793,6 +2026,9 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 		ch_width, ht_enabled, vht_enabled,
 		sap_config->acs_cfg.start_ch, sap_config->acs_cfg.end_ch);
 
+	sap_config->acs_cfg.is_ht_enabled = ht_enabled;
+	sap_config->acs_cfg.is_vht_enabled = vht_enabled;
+
 	if (sap_config->acs_cfg.ch_list_count) {
 		hdd_notice("ACS channel list: len: %d",
 					sap_config->acs_cfg.ch_list_count);
@@ -1810,10 +2046,26 @@ static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
 		 * for this long. So we split up the scanning part.
 		 */
 		set_bit(ACS_PENDING, &adapter->event_flags);
-		hdd_notice("ACS Pending for wlan%d", adapter->dev->ifindex);
+		hdd_notice("ACS Pending for %s", adapter->dev->name);
 		status = 0;
 	} else {
-		status = wlan_hdd_cfg80211_start_acs(adapter);
+		/* Check if vendor specific acs is enabled */
+		if (hdd_ctx->config->vendor_acs_support) {
+			sap_config->acs_cfg.hw_mode = hw_mode;
+			hdd_create_acs_timer(adapter);
+			hdd_update_acs_timer_reason(adapter,
+				QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT);
+			if (hdd_ctx->config->acs_support_for_dfs_ltecoex)
+				wlan_sap_set_vendor_acs(
+					WLAN_HDD_GET_SAP_CTX_PTR(adapter),
+					true);
+			else
+				wlan_sap_set_vendor_acs(
+					WLAN_HDD_GET_SAP_CTX_PTR(adapter),
+					false);
+
+		} else
+			status = wlan_hdd_cfg80211_start_acs(adapter);
 	}
 
 out:
@@ -8301,6 +8553,303 @@ static QDF_STATUS wlan_hdd_validate_acs_channel(hdd_adapter_t *adapter,
 
 }
 
+static void hdd_update_acs_sap_config(hdd_context_t *hdd_ctx,
+				     tsap_Config_t *sap_config,
+				     struct hdd_vendor_chan_info *channel_list)
+{
+	sap_config->channel = channel_list->pri_ch;
+
+	sap_config->ch_params.center_freq_seg0 =
+				channel_list->vht_seg0_center_ch;
+	sap_config->ch_params.center_freq_seg1 =
+				channel_list->vht_seg1_center_ch;
+
+	sap_config->ch_params.sec_ch_offset = channel_list->ht_sec_ch;
+	sap_config->ch_params.ch_width = channel_list->chan_width;
+	if (sap_config->channel >= 36)
+		sap_config->ch_width_orig =
+				hdd_ctx->config->vhtChannelWidth;
+	else
+		sap_config->ch_width_orig =
+			hdd_ctx->config->nChannelBondingMode24GHz ?
+			eHT_CHANNEL_WIDTH_40MHZ :
+			eHT_CHANNEL_WIDTH_20MHZ;
+
+	sap_config->acs_cfg.pri_ch = channel_list->pri_ch;
+	sap_config->acs_cfg.ch_width = channel_list->chan_width;
+	sap_config->acs_cfg.vht_seg0_center_ch =
+				channel_list->vht_seg0_center_ch;
+	sap_config->acs_cfg.vht_seg1_center_ch =
+				channel_list->vht_seg1_center_ch;
+	sap_config->acs_cfg.ht_sec_ch = channel_list->ht_sec_ch;
+}
+
+static int hdd_update_acs_channel(hdd_adapter_t *adapter, uint8_t reason,
+				  uint8_t channel_cnt,
+				  struct hdd_vendor_chan_info *channel_list)
+{
+	tsap_Config_t *sap_config;
+	hdd_ap_ctx_t *hdd_ap_ctx;
+	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
+	sap_config = &adapter->sessionCtx.ap.sapConfig;
+
+	if (QDF_TIMER_STATE_RUNNING ==
+	    qdf_mc_timer_get_current_state(&adapter->sessionCtx.
+					ap.vendor_acs_timer)) {
+		qdf_mc_timer_stop(&adapter->sessionCtx.ap.vendor_acs_timer);
+	}
+
+	if (channel_list && channel_list->pri_ch == 0) {
+		/* Check mode, set default channel */
+		channel_list->pri_ch = 6;
+		/*
+		 * sap_select_default_oper_chan(hdd_ctx->hHal,
+		 *      sap_config->acs_cfg.hw_mode);
+		 */
+	}
+
+	switch (reason) {
+	/* SAP init case */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT:
+		hdd_update_acs_sap_config(hdd_ctx, sap_config, channel_list);
+		/* Update Hostapd */
+		wlan_hdd_cfg80211_acs_ch_select_evt(adapter);
+		break;
+
+	/* DFS detected on current channel */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS:
+		wlan_sap_update_next_channel(
+				WLAN_HDD_GET_SAP_CTX_PTR(adapter),
+				channel_list->pri_ch,
+				channel_list->chan_width);
+		status = sme_update_new_channel_event(
+					WLAN_HDD_GET_HAL_CTX(adapter),
+					adapter->sessionId);
+		break;
+
+	/* LTE coex event on current channel */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX:
+		sap_config->acs_cfg.pri_ch = channel_list->pri_ch;
+		sap_config->acs_cfg.ch_width = channel_list->chan_width;
+		hdd_ap_ctx->sapConfig.ch_width_orig =
+				channel_list->chan_width;
+		hdd_restart_sap(adapter, sap_config->acs_cfg.pri_ch);
+		break;
+
+	default:
+		hdd_info("invalid reason for timer invoke");
+	}
+	qdf_mem_free(channel_list);
+	EXIT();
+	return status;
+}
+
+/**
+ * Define short name for vendor channel set config
+ */
+#define SET_CHAN_REASON QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
+#define SET_CHAN_CHANNEL_COUNT QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_COUNT
+#define SET_CHAN_CHAN_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST
+#define SET_CHAN_PRIMARY_CHANNEL \
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY
+#define SET_CHAN_SECONDARY_CHANNEL \
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY
+#define SET_CHAN_SEG0_CENTER_CHANNEL \
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0
+#define	SET_CHAN_SEG1_CENTER_CHANNEL \
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1
+#define	SET_CHAN_CHANNEL_WIDTH \
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+#define	SET_CHAN_MAX QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX
+
+/**
+ * hdd_parse_vendor_acs_chan_config() - API to parse vendor acs channel config
+ * @channel_list: pointer to hdd_vendor_chan_info
+ * @reason: channel change reason
+ * @channel_cnt: channel count
+ * @data: data
+ * @data_len: data len
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int hdd_parse_vendor_acs_chan_config(struct hdd_vendor_chan_info
+		**chan_list_ptr, uint8_t *reason, uint8_t *channel_cnt,
+		const void *data, int data_len)
+{
+	int rem, i = 0;
+	struct nlattr *tb[SET_CHAN_MAX + 1];
+	struct nlattr *tb2[SET_CHAN_MAX + 1];
+	struct nlattr *curr_attr;
+	struct hdd_vendor_chan_info *channel_list;
+
+	if (nla_parse(tb, SET_CHAN_MAX, data, data_len, NULL)) {
+		hdd_err("Invalid ATTR");
+		return -EINVAL;
+	}
+
+	if (tb[SET_CHAN_REASON])
+		*reason = nla_get_u8(tb[SET_CHAN_REASON]);
+
+	if (tb[SET_CHAN_CHANNEL_COUNT]) {
+		*channel_cnt = nla_get_u8(tb[
+				SET_CHAN_CHANNEL_COUNT]);
+		hdd_info("channel count %d", *channel_cnt);
+	}
+
+	if (!(*channel_cnt)) {
+		hdd_err("channel count is %d", *channel_cnt);
+		return -EINVAL;
+	}
+
+	channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) *
+					(*channel_cnt));
+
+	nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) {
+		if (nla_parse(tb2,
+			SET_CHAN_MAX,
+			nla_data(curr_attr), nla_len(curr_attr),
+			NULL)) {
+			hdd_err("nla_parse failed");
+			return -EINVAL;
+		}
+		/* Parse and Fetch allowed SSID list*/
+		if (tb2[SET_CHAN_PRIMARY_CHANNEL]) {
+			channel_list[i].pri_ch =
+				nla_get_u8(
+					tb2[SET_CHAN_PRIMARY_CHANNEL]);
+		}
+		if (tb2[SET_CHAN_SECONDARY_CHANNEL]) {
+			channel_list[i].ht_sec_ch =
+				nla_get_u8(tb2[SET_CHAN_SECONDARY_CHANNEL]);
+		}
+		if (tb2[SET_CHAN_SEG0_CENTER_CHANNEL]) {
+			channel_list[i].vht_seg0_center_ch =
+				nla_get_u8(tb2[SET_CHAN_SEG0_CENTER_CHANNEL]);
+		}
+		if (tb2[SET_CHAN_SEG1_CENTER_CHANNEL]) {
+			channel_list[i].vht_seg1_center_ch =
+				nla_get_u8(tb2[SET_CHAN_SEG1_CENTER_CHANNEL]);
+		}
+		if (tb2[SET_CHAN_CHANNEL_WIDTH]) {
+			channel_list[i].chan_width =
+				nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]);
+		}
+		hdd_info("index %d pri %d sec %d seg0 %d seg1 %d width %d",
+			i, channel_list[i].pri_ch,
+			channel_list[i].ht_sec_ch,
+			channel_list[i].vht_seg0_center_ch,
+			channel_list[i].vht_seg1_center_ch,
+			channel_list[i].chan_width);
+		i++;
+		if (i > *channel_cnt)
+			break;
+	}
+	*chan_list_ptr = channel_list;
+
+	return 0;
+}
+
+/**
+ * Undef short names for vendor set channel configuration
+ */
+#undef SET_CHAN_REASON
+#undef SET_CHAN_CHANNEL_COUNT
+#undef SET_CHAN_CHAN_LIST
+#undef SET_CHAN_PRIMARY_CHANNEL
+#undef SET_CHAN_SECONDARY_CHANNEL
+#undef SET_CHAN_SEG0_CENTER_CHANNEL
+#undef SET_CHAN_SEG1_CENTER_CHANNEL
+#undef SET_CHAN_CHANNEL_WIDTH
+#undef SET_CHAN_MAX
+
+/**
+ * __wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Length of @data
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int __wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy,
+		struct wireless_dev *wdev,
+		const void *data, int data_len)
+{
+	int ret_val;
+	QDF_STATUS qdf_status;
+	uint8_t channel_cnt = 0, reason = -1;
+	struct hdd_vendor_chan_info *channel_list = NULL;
+	hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
+	hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+
+	ENTER();
+
+	ret_val = wlan_hdd_validate_context(hdd_ctx);
+	if (ret_val)
+		return ret_val;
+
+	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EINVAL;
+	}
+
+	if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags))
+		clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
+	else {
+		hdd_err("already timeout happened for acs");
+		return -EINVAL;
+	}
+
+	ret_val = hdd_parse_vendor_acs_chan_config(&channel_list, &reason,
+					&channel_cnt, data, data_len);
+	if (ret_val)
+		return ret_val;
+
+	/* Validate channel to be set */
+	while (channel_cnt && channel_list) {
+		qdf_status = wlan_hdd_validate_acs_channel(adapter,
+					channel_list->pri_ch,
+					channel_list->chan_width);
+		if (qdf_status == QDF_STATUS_SUCCESS)
+			break;
+		channel_cnt--;
+		channel_list++;
+	}
+	if ((channel_cnt <= 0) || !channel_list) {
+		hdd_err("no available channel/chanlist %p", channel_list);
+		return -EINVAL;
+	}
+
+	qdf_status = hdd_update_acs_channel(adapter, reason,
+				      channel_cnt, channel_list);
+	return qdf_status_to_os_return(qdf_status);
+}
+
+/**
+ * wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Length of @data
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy,
+						struct wireless_dev *wdev,
+						const void *data, int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_update_vendor_channel(wiphy, wdev, data,
+								data_len);
+	cds_ssr_protect(__func__);
+
+	return ret;
+}
 
 /**
  * wlan_hdd_cfg80211_setband() - Wrapper to setband
@@ -9410,6 +9959,14 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
 		.doit = wlan_hdd_cfg80211_get_bus_size
 	},
+	{
+		.info.vendor_id = QCA_NL80211_VENDOR_ID,
+		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS,
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+			 WIPHY_VENDOR_CMD_NEED_NETDEV |
+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
+		.doit = wlan_hdd_cfg80211_update_vendor_channel
+	},
 	{
 		.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SETBAND,
 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |

+ 132 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -303,6 +303,8 @@ typedef enum {
  *	(SAR) power limits. A critical regulation for FCC compliance, OEMs
  *	require methods to set SAR limits on TX power of WLAN/WWAN.
  *	enum qca_vendor_attr_sar_limits attributes are used with this command.
+ * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: Vendor command used to get/set
+ *      configuration of vendor ACS.
  */
 
 enum qca_nl80211_vendor_subcmds {
@@ -455,6 +457,10 @@ enum qca_nl80211_vendor_subcmds {
 
 	/* Set Specific Absorption Rate(SAR) Power Limits */
 	QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
+
+	/* External Auto channel configuration setting */
+	QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147,
+
 	/* Set the trace level for QDF */
 	QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
 };
@@ -771,6 +777,7 @@ enum qca_nl80211_vendor_subcmds_index {
 #endif /* WLAN_FEATURE_NAN_DATAPATH */
 	QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX,
 	QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX,
+	QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG,
 };
 
 /**
@@ -2733,6 +2740,73 @@ enum qca_wlan_vendor_external_acs_event_chan_info_attr {
 		QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_vendor_attr_start_acs_config: attribute to vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_START_ACS. This will be triggered by host
+ * driver.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON: This reason refers to
+ * qca_wlan_vendor_acs_select_reason. This helps acs module to understand why
+ * ACS need to be started
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED: Does
+ * driver supports spectral scanning or not
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED: Is 11ac is
+ * offloaded to firmware.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT: Does driver
+ * provides additional channel capability as part of scan operation.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE: Operating mode of
+ * interface. It takes one of nl80211_iftype values.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_STATUS: Iface status: UP/Down
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH: This is the upper bound
+ * of chan width. ACS logic should try to get a channel with specified width
+ * if not found then look for lower values.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAC_ADDR: Interface MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND: nl80211_bands
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE: PHY/HW mode such as
+ * a/b/g/n/ac.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHANLIST: Supported channel list
+ * among which ACS should choose best channel.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL:Preferred Chan List by the
+ * driver which will have <channel(u8), weight(u8)> format as array of
+ * nested values.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO: Array of nested attribute
+ * for each channel. It takes attr as defined in
+ * qca_wlan_vendor_start_acs_config_chan_info_attr.
+ */
+enum qca_wlan_vendor_attr_start_acs_config {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 5,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_STATUS = 6,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAC_ADDR = 8,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 9,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 10,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHANLIST = 11,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 12,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 13,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX =
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_pcl_config: attribute to vendor sub-command
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL.
+ * @QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_INVALID: invalid value
+ * @QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_CHANNEL: pcl channel number
+ * @QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_WEIGHT: pcl channel weight
+ */
+enum qca_wlan_vendor_attr_pcl_config {
+	QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_CHANNEL = 1,
+	QCA_WLAN_VENDOR_ATTR_PCL_CONFIG_WEIGHT = 2,
+};
+
 /**
  * enum set_reset_packet_filter - set packet filter control commands
  * @QCA_WLAN_SET_PACKET_FILTER: Set Packet Filter
@@ -3378,6 +3452,64 @@ enum qca_vendor_attr_sar_limits {
 		QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1
 };
 
+/**
+ * qca_wlan_vendor_attr_external_acs_channels: attribute to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carry a list of channels
+ * in priority order as decided after acs operation in userspace.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON: One of reason code from
+ * qca_wlan_vendor_acs_select_reason.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_COUNT: Number of channels in
+ * this list
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST: Array of nested values
+ * for each channel with following attributes:
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
+ *     QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY: Primary channel (u8)
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY: Secondary channel (u8)
+ * required only for 160 / 80 + 80
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0: VHT seg0 channel (u8)
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1: VHT seg1 channel (u8)
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH:channel width (u8)
+ */
+enum qca_wlan_vendor_attr_external_acs_channels {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_COUNT = 2,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 3,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1
+};
+
+/**
+ * qca_wlan_vendor_acs_select_reason: This represents the different reasons why
+ * the ACS has to be triggered. These parameters are used by
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and
+ * QCA_NL80211_VENDOR_SUBCMD_ACS_SET_CHANNELS
+ * @QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT: Represents the reason that the
+ * ACS triggered during the AP start
+ * @QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS: Represents the reason that
+ * DFS found with current channel
+ * @QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX: Represents the reason that
+ * LTE CO-Exist in current band
+ */
+enum qca_wlan_vendor_acs_select_reason {
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT,
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
+};
+
 struct cfg80211_bss *wlan_hdd_cfg80211_update_bss_db(hdd_adapter_t *pAdapter,
 						tCsrRoamInfo *pRoamInfo);
 

+ 6 - 0
core/hdd/src/wlan_hdd_hostapd.c

@@ -2065,6 +2065,12 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
 		else
 			return QDF_STATUS_SUCCESS;
 
+	case eSAP_DFS_NEXT_CHANNEL_REQ:
+		hdd_notice("Sending next channel query to userspace");
+		hdd_update_acs_timer_reason(pHostapdAdapter,
+				QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS);
+		return QDF_STATUS_SUCCESS;
+
 	default:
 		hdd_notice("SAP message is not handled");
 		goto stopbss;

+ 115 - 3
core/hdd/src/wlan_hdd_main.c

@@ -6081,7 +6081,7 @@ static uint8_t hdd_get_safe_channel_from_pcl_and_acs_range(
  *
  * Return: None
  */
-static void hdd_restart_sap(hdd_adapter_t *adapter, uint8_t channel)
+void hdd_restart_sap(hdd_adapter_t *adapter, uint8_t channel)
 {
 	hdd_ap_ctx_t *hdd_ap_ctx;
 	tHalHandle *hal_handle;
@@ -6112,6 +6112,30 @@ static void hdd_restart_sap(hdd_adapter_t *adapter, uint8_t channel)
 
 	cds_change_sap_channel_with_csa(adapter, hdd_ap_ctx);
 }
+
+int hdd_update_acs_timer_reason(hdd_adapter_t *adapter, uint8_t reason)
+{
+	struct hdd_external_acs_timer_context *timer_context;
+
+	set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
+
+	if (QDF_TIMER_STATE_RUNNING ==
+	    qdf_mc_timer_get_current_state(&adapter->sessionCtx.
+					ap.vendor_acs_timer)) {
+		qdf_mc_timer_stop(&adapter->sessionCtx.ap.vendor_acs_timer);
+	}
+	timer_context = (struct hdd_external_acs_timer_context *)
+			adapter->sessionCtx.ap.vendor_acs_timer.user_data;
+	timer_context->reason = reason;
+	qdf_mc_timer_start(&adapter->sessionCtx.ap.vendor_acs_timer,
+				WLAN_VENDOR_ACS_WAIT_TIME);
+	/* Update config to application */
+	hdd_cfg80211_update_acs_config(adapter, reason);
+	hdd_notice("Updated ACS config to nl with reason %d", reason);
+
+	return 0;
+}
+
 /**
  * hdd_unsafe_channel_restart_sap() - restart sap if sap is on unsafe channel
  * @hdd_ctx: hdd context pointer
@@ -6166,8 +6190,14 @@ void hdd_unsafe_channel_restart_sap(hdd_context_t *hdd_ctxt)
 			goto next_adapater;
 		}
 
-		restart_chan =
-			hdd_get_safe_channel_from_pcl_and_acs_range(
+		if (hdd_ctxt->config->vendor_acs_support &&
+		    hdd_ctxt->config->acs_support_for_dfs_ltecoex) {
+			hdd_update_acs_timer_reason(adapter_temp,
+				QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX);
+			goto next_adapater;
+		} else
+			restart_chan =
+				hdd_get_safe_channel_from_pcl_and_acs_range(
 					adapter_temp);
 		if (!restart_chan) {
 			hdd_alert("fail to restart SAP");
@@ -6444,6 +6474,88 @@ void hdd_indicate_mgmt_frame(tSirSmeMgmtFrameInd *frame_ind)
 	return;
 }
 
+static void hdd_lte_coex_restart_sap(hdd_adapter_t *adapter,
+				     hdd_context_t *hdd_ctx)
+{
+	uint8_t restart_chan = 0;
+
+	restart_chan =
+		hdd_get_safe_channel_from_pcl_and_acs_range(adapter);
+	if (!restart_chan) {
+		hdd_alert("fail to restart SAP");
+	} else {
+		/* SAP restart due to unsafe channel. While restarting
+		 * the SAP, make sure to clear acs_channel, channel to
+		 * reset to 0. Otherwise these settings will override
+		 * the ACS while restart.
+		 */
+		hdd_ctx->acs_policy.acs_channel = AUTO_CHANNEL_SELECT;
+		adapter->sessionCtx.ap.sapConfig.channel =
+						AUTO_CHANNEL_SELECT;
+		hdd_info("sending coex indication");
+		wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
+				WLAN_SVC_LTE_COEX_IND, NULL, 0);
+		hdd_restart_sap(adapter, restart_chan);
+	}
+}
+
+void hdd_acs_response_timeout_handler(void *context)
+{
+	struct hdd_external_acs_timer_context *timer_context =
+			(struct hdd_external_acs_timer_context *)context;
+	hdd_adapter_t *adapter;
+	hdd_context_t *hdd_ctx;
+	uint8_t reason;
+
+	ENTER();
+	if (!timer_context) {
+		hdd_err("invlaid timer context");
+		return;
+	}
+	adapter = timer_context->adapter;
+	reason = timer_context->reason;
+
+
+	if ((!adapter) ||
+	    (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
+		hdd_err("invalid adapter or adapter has invalid magic");
+		return;
+	}
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return;
+
+	if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags))
+		clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
+	else
+		return;
+
+	hdd_err("ACS timeout happened for %s reason %d",
+				adapter->dev->name, reason);
+	switch (reason) {
+	/* SAP init case */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT:
+		wlan_sap_set_vendor_acs(WLAN_HDD_GET_SAP_CTX_PTR(adapter),
+					false);
+		wlan_hdd_cfg80211_start_acs(adapter);
+		break;
+	/* DFS detected on current channel */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS:
+		wlan_sap_update_next_channel(
+				WLAN_HDD_GET_SAP_CTX_PTR(adapter), 0, 0);
+		sme_update_new_channel_event(WLAN_HDD_GET_HAL_CTX(adapter),
+					     adapter->sessionId);
+		break;
+	/* LTE coex event on current channel */
+	case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX:
+		hdd_lte_coex_restart_sap(adapter, hdd_ctx);
+		break;
+	default:
+		hdd_info("invalid reason for timer invoke");
+
+	}
+}
+
 /**
  * wlan_hdd_disable_all_dual_mac_features() - Disable dual mac features
  * @hdd_ctx: HDD context

+ 17 - 0
core/hdd/src/wlan_hdd_scan.c

@@ -2037,6 +2037,18 @@ static inline void wlan_hdd_copy_bssid(struct cfg80211_scan_request *request,
 }
 #endif
 
+static void hdd_process_vendor_acs_response(hdd_adapter_t *adapter)
+{
+	if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags)) {
+		if (QDF_TIMER_STATE_RUNNING ==
+		    qdf_mc_timer_get_current_state(&adapter->sessionCtx.
+					ap.vendor_acs_timer)) {
+			qdf_mc_timer_stop(&adapter->sessionCtx.
+					ap.vendor_acs_timer);
+		}
+	}
+}
+
 /**
  * __wlan_hdd_cfg80211_vendor_scan() - API to process venor scan request
  * @wiphy: Pointer to wiphy
@@ -2061,6 +2073,7 @@ static int __wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy,
 	unsigned int len;
 	struct ieee80211_channel *chan;
 	hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+	hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
 	int ret;
 
 	ENTER_DEV(wdev->netdev);
@@ -2217,6 +2230,10 @@ static int __wlan_hdd_cfg80211_vendor_scan(struct wiphy *wiphy,
 		wlan_hdd_copy_bssid(request,
 			nla_data(tb[QCA_WLAN_VENDOR_ATTR_SCAN_BSSID]));
 	}
+
+	/* Check if external acs was requested on this adapter */
+	hdd_process_vendor_acs_response(adapter);
+
 	request->no_cck =
 		nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE]);
 	request->wdev = wdev;

+ 23 - 0
core/sap/inc/sap_api.h

@@ -182,6 +182,7 @@ typedef enum {
 	eSAP_ACS_SCAN_SUCCESS_EVENT,
 	eSAP_ACS_CHANNEL_SELECTED,
 	eSAP_ECSA_CHANGE_CHAN_IND,
+	eSAP_DFS_NEXT_CHANNEL_REQ,
 } eSapHddEvent;
 
 typedef enum {
@@ -497,6 +498,7 @@ struct sap_acs_cfg {
 
 	uint16_t   ch_width;
 	uint8_t    pcl_channels[QDF_MAX_NUM_CHAN];
+	uint8_t    pcl_channels_weight_list[QDF_MAX_NUM_CHAN];
 	uint32_t   pcl_ch_count;
 	uint8_t    is_ht_enabled;
 	uint8_t    is_vht_enabled;
@@ -904,6 +906,18 @@ bool wlansap_is_channel_leaking_in_nol(void *ctx, uint8_t channel,
 QDF_STATUS wlansap_start_bss(void *p_cds_gctx,
 	 tpWLAN_SAPEventCB pSapEventCallback,
 	 tsap_Config_t *pConfig, void *pUsrContext);
+
+/**
+ * wlan_sap_update_next_channel() - Update next channel configured using vendor
+ * command in SAP context
+ * @ctx: SAP context
+ * @channel: channel number
+ * @chan_bw: channel width
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_update_next_channel(void *ctx, uint8_t channel,
+				       enum phy_ch_width chan_bw);
 QDF_STATUS wlan_sap_set_pre_cac_status(void *ctx, bool status,
 		tHalHandle handle);
 QDF_STATUS wlan_sap_set_chan_before_pre_cac(void *ctx,
@@ -992,6 +1006,15 @@ void wlansap_extend_to_acs_range(uint8_t *startChannelNum,
 		uint8_t *bandEndChannel);
 QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx, uint8_t *nol, uint32_t *nol_len);
 QDF_STATUS wlansap_set_dfs_nol(void *pSapCtx, eSapDfsNolType conf);
+
+/**
+ * wlan_sap_set_vendor_acs() - Set vendor specific acs in sap context
+ * @pSapCtx: SAP context
+ * @is_vendor_acs: if vendor specific acs is enabled
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_set_vendor_acs(void *sap_ctx, bool is_vendor_acs);
 void wlansap_populate_del_sta_params(const uint8_t *mac,
 		uint16_t reason_code,
 		uint8_t subtype,

+ 62 - 1
core/sap/src/sap_api_link_cntl.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -821,6 +821,55 @@ wlansap_roam_process_infra_assoc_ind(ptSapContext sap_ctx,
 	return;
 }
 
+static void wlansap_update_vendor_acs_chan(tpAniSirGlobal mac_ctx,
+				ptSapContext sap_ctx)
+{
+	int intf;
+	tHalHandle hal;
+
+	hal = CDS_GET_HAL_CB(sap_ctx->p_cds_gctx);
+	mac_ctx->sap.SapDfsInfo.target_channel =
+				sap_ctx->dfs_vendor_channel;
+
+	mac_ctx->sap.SapDfsInfo.new_chanWidth =
+				sap_ctx->dfs_vendor_chan_bw;
+	mac_ctx->sap.SapDfsInfo.new_ch_params.ch_width =
+				sap_ctx->dfs_vendor_chan_bw;
+
+	if (mac_ctx->sap.SapDfsInfo.target_channel != 0) {
+		mac_ctx->sap.SapDfsInfo.cac_state =
+			eSAP_DFS_DO_NOT_SKIP_CAC;
+		sap_cac_reset_notify(hal);
+		/* set DFS-NOL back to keep it update-to-date in CNSS */
+		sap_signal_hdd_event(sap_ctx, NULL, eSAP_DFS_NOL_SET,
+				     (void *) eSAP_STATUS_SUCCESS);
+		return;
+	}
+	/* App failed to provide new channel, try random channel algo */
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
+		  FL("failed to get channel from userspace"));
+
+	/* Issue stopbss for each sapctx */
+	for (intf = 0; intf < SAP_MAX_NUM_SESSION; intf++) {
+		ptSapContext pSapContext;
+
+		if (((QDF_SAP_MODE ==
+		    mac_ctx->sap.sapCtxList[intf].sapPersona) ||
+		    (QDF_P2P_GO_MODE ==
+		    mac_ctx->sap.sapCtxList[intf].sapPersona)) &&
+		    mac_ctx->sap.sapCtxList[intf].pSapContext !=
+		    NULL) {
+			pSapContext =
+			    mac_ctx->sap.sapCtxList[intf].pSapContext;
+			QDF_TRACE(QDF_MODULE_ID_SAP,
+				  QDF_TRACE_LEVEL_ERROR,
+				  FL("sapdfs: no available channel for sapctx[%p], StopBss"),
+				  pSapContext);
+			wlansap_stop_bss(pSapContext);
+		}
+	}
+}
+
 /**
  * wlansap_roam_callback() - Callback for Roam (connection status) Events
  * @ctx             : pContext passed in with the roam request
@@ -948,6 +997,12 @@ wlansap_roam_callback(void *ctx, tCsrRoamInfo *csr_roam_info, uint32_t roamId,
 				     eSAP_MAC_TRIG_STOP_BSS_EVENT,
 				     (void *) eSAP_STATUS_SUCCESS);
 		break;
+	case eCSR_ROAM_CHANNEL_COMPLETE_IND:
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
+			  FL("Received new channel from app"));
+		wlansap_update_vendor_acs_chan(mac_ctx, sap_ctx);
+		break;
+
 	case eCSR_ROAM_DFS_RADAR_IND:
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
 			  FL("Received Radar Indication"));
@@ -984,6 +1039,12 @@ wlansap_roam_callback(void *ctx, tCsrRoamInfo *csr_roam_info, uint32_t roamId,
 			mac_ctx->sap.SapDfsInfo.user_provided_target_channel =
 			   0;
 		}
+		/* if external acs enabled */
+		if (sap_ctx->vendor_acs_enabled &&
+			!mac_ctx->sap.SapDfsInfo.target_channel) {
+			/* Return from here, processing will be done later */
+			return 0;
+		}
 		if (mac_ctx->sap.SapDfsInfo.target_channel != 0) {
 			mac_ctx->sap.SapDfsInfo.cac_state =
 				eSAP_DFS_DO_NOT_SKIP_CAC;

+ 16 - 0
core/sap/src/sap_fsm.c

@@ -3124,6 +3124,12 @@ QDF_STATUS sap_signal_hdd_event(ptSapContext sap_ctx,
 		sap_ap_event.sapevt.sap_chan_cng_ind.new_chan =
 					   csr_roaminfo->target_channel;
 		break;
+	case eSAP_DFS_NEXT_CHANNEL_REQ:
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
+				"In %s, SAP event callback event = %s",
+				__func__, "eSAP_DFS_NEXT_CHANNEL_REQ");
+		sap_ap_event.sapHddEventCode = eSAP_DFS_NEXT_CHANNEL_REQ;
+		break;
 	default:
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
 			  FL("SAP Unknown callback event = %d"),
@@ -4918,6 +4924,16 @@ uint8_t sap_indicate_radar(ptSapContext sapContext,
 	 * (5) Create the available channel list with the above rules
 	 */
 
+	if (sapContext->vendor_acs_enabled) {
+		/* Send event to hdd */
+		if (QDF_STATUS_SUCCESS ==
+			sap_signal_hdd_event(sapContext,
+					     NULL, eSAP_DFS_NEXT_CHANNEL_REQ,
+					     (void *) eSAP_STATUS_SUCCESS)) {
+			return 0;
+		}
+	}
+
 	target_channel = sap_random_channel_sel(sapContext);
 	if (0 == target_channel) {
 		sap_signal_hdd_event(sapContext, NULL,

+ 3 - 0
core/sap/src/sap_internal.h

@@ -267,6 +267,9 @@ typedef struct sSapContext {
 	qdf_event_t sap_session_opened_evt;
 	bool is_pre_cac_on;
 	bool pre_cac_complete;
+	bool vendor_acs_enabled;
+	uint8_t dfs_vendor_channel;
+	uint8_t dfs_vendor_chan_bw;
 	uint8_t chan_before_pre_cac;
 	uint8_t beacon_tx_rate;
 	tSirMacRateSet supp_rate_set;

+ 30 - 0
core/sap/src/sap_module.c

@@ -2381,6 +2381,23 @@ QDF_STATUS wlansap_cancel_remain_on_channel(void *pCtx,
 	return QDF_STATUS_E_FAULT;
 }
 
+QDF_STATUS wlan_sap_update_next_channel(void *ctx, uint8_t channel,
+				       enum phy_ch_width chan_bw)
+{
+	ptSapContext sap_ctx = CDS_GET_SAP_CB(ctx);
+
+	if (!sap_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid SAP pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	sap_ctx->dfs_vendor_channel = channel;
+	sap_ctx->dfs_vendor_chan_bw = chan_bw;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
  * wlan_sap_set_pre_cac_status() - Set the pre cac status
  * @ctx: SAP context
@@ -3325,6 +3342,19 @@ void wlansap_extend_to_acs_range(uint8_t *startChannelNum,
 	}
 }
 
+QDF_STATUS wlan_sap_set_vendor_acs(void *ctx, bool is_vendor_acs)
+{
+	ptSapContext sap_context = (ptSapContext) ctx;
+
+	if (!sap_context) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid SAP pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+	sap_context->vendor_acs_enabled = is_vendor_acs;
+
+	return QDF_STATUS_SUCCESS;
+}
 /**
  * wlansap_get_dfs_nol() - Get the DFS NOL
  * @pSapCtx: SAP context

+ 1 - 0
core/sme/inc/csr_api.h

@@ -514,6 +514,7 @@ typedef enum {
 	eCSR_ROAM_START,
 	eCSR_ROAM_ABORT,
 	eCSR_ROAM_NAPI_OFF,
+	eCSR_ROAM_CHANNEL_COMPLETE_IND,
 } eRoamCmdStatus;
 
 /* comment inside indicates what roaming callback gets */

+ 8 - 0
core/sme/inc/sme_api.h

@@ -1357,6 +1357,14 @@ QDF_STATUS sme_update_sta_inactivity_timeout(tHalHandle hal_handle,
 QDF_STATUS sme_set_lost_link_info_cb(tHalHandle hal,
 		void (*cb)(void *, struct sir_lost_link_info *));
 
+/**
+ * sme_update_new_channel_event() - update new channel event for sapFsm
+ * @hal: HAL handle
+ * @session_id: session id
+ *
+ * Return: QDF_STATUS_SUCCESS or non-zero on failure.
+ */
+QDF_STATUS sme_update_new_channel_event(tHalHandle hal, uint8_t session_id);
 #ifdef WLAN_POWER_DEBUGFS
 QDF_STATUS sme_power_debug_stats_req(tHalHandle hal, void (*callback_fn)
 				(struct  power_stats_response *response,

+ 25 - 0
core/sme/src/common/sme_api.c

@@ -1906,6 +1906,31 @@ QDF_STATUS dfs_msg_processor(tpAniSirGlobal pMac, uint16_t msgType, void *pMsgBu
 	return status;
 }
 
+QDF_STATUS sme_update_new_channel_event(tHalHandle hal, uint8_t session_id)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tpAniSirGlobal mac = PMAC_STRUCT(hal);
+	tCsrRoamInfo *roamInfo;
+	eRoamCmdStatus roamStatus;
+	eCsrRoamResult roamResult;
+
+	roamInfo = qdf_mem_malloc(sizeof(*roamInfo));
+	roamInfo->dfs_event.sessionId = session_id;
+
+	roamStatus = eCSR_ROAM_CHANNEL_COMPLETE_IND;
+	roamResult = eCSR_ROAM_RESULT_DFS_RADAR_FOUND_IND;
+	QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO_MED,
+		  "sapdfs: Updated new channel event");
+
+	/* Indicate channel Event to SAP */
+	csr_roam_call_callback(mac, session_id, roamInfo, 0,
+			       roamStatus, roamResult);
+
+	qdf_mem_free(roamInfo);
+	return status;
+}
+
+
 /**
  * sme_extended_change_channel_ind()- function to indicate ECSA
  * action frame is received in lim to SAP