Browse Source

qcacmn: Support 11d for non-offload platform

Support 11d for non-offload platform by maintaining
count of beacons encountered for each country code
and choosing country code with max votes as device's
country code.

Change-Id: I83b66e980854eded17e254386561fa32b1f8c4ac
CRs-Fixed: 2154048
Paul Zhang 7 years ago
parent
commit
ca6152167b

+ 5 - 0
qdf/inc/qdf_types.h

@@ -95,6 +95,11 @@ typedef struct qdf_sglist {
  */
 #define qdf_packed __qdf_packed
 
+/**
+ * qdf_toupper - char lower to upper.
+ */
+#define qdf_toupper __qdf_toupper
+
 typedef void *qdf_net_handle_t;
 
 typedef void *qdf_netlink_handle_t;

+ 3 - 1
qdf/linux/src/i_qdf_types.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -55,6 +55,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/types.h>
+#include <linux/ctype.h>
 #include <linux/sched.h>
 #include <linux/completion.h>
 #include <linux/string.h>
@@ -255,6 +256,7 @@ enum __qdf_net_wireless_evcode {
 #define __qdf_vprint              vprintk
 #define __qdf_snprint             snprintf
 #define __qdf_vsnprint            vsnprintf
+#define __qdf_toupper            toupper
 
 #define __QDF_DMA_BIDIRECTIONAL  DMA_BIDIRECTIONAL
 #define __QDF_DMA_TO_DEVICE      DMA_TO_DEVICE

+ 92 - 2
umac/regulatory/core/src/reg_services.c

@@ -843,7 +843,8 @@ QDF_STATUS reg_get_channel_list_with_power(struct wlan_objmgr_pdev *pdev,
 	reg_channels = pdev_priv_obj->cur_chan_list;
 
 	for (i = 0, count = 0; i < NUM_CHANNELS; i++) {
-		if (reg_channels[i].state) {
+		if (reg_channels[i].state &&
+		    reg_channels[i].state != REGULATORY_CHAN_DISABLED) {
 			ch_list[count].chan_num =
 				reg_channels[i].chan_num;
 			ch_list[count++].tx_power =
@@ -1376,6 +1377,22 @@ QDF_STATUS reg_set_default_country(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+bool reg_is_world_alpha2(uint8_t *alpha2)
+{
+	if ((alpha2[0] == '0') && (alpha2[1] == '0'))
+		return true;
+
+	return false;
+}
+
+bool reg_is_us_alpha2(uint8_t *alpha2)
+{
+	if ((alpha2[0] == 'U') && (alpha2[1] == 'S'))
+		return true;
+
+	return false;
+}
+
 QDF_STATUS reg_set_country(struct wlan_objmgr_pdev *pdev,
 			   uint8_t *country)
 {
@@ -1430,6 +1447,55 @@ QDF_STATUS reg_set_country(struct wlan_objmgr_pdev *pdev,
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS reg_set_11d_country(struct wlan_objmgr_pdev *pdev,
+			       uint8_t *country)
+{
+	struct wlan_regulatory_psoc_priv_obj *psoc_reg;
+	struct set_country country_code;
+	struct wlan_objmgr_psoc *psoc;
+	struct cc_regdmn_s rd;
+	QDF_STATUS status;
+
+	if (!country) {
+		reg_err("country code is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	psoc = wlan_pdev_get_psoc(pdev);
+	psoc_reg = reg_get_psoc_obj(psoc);
+	if (!IS_VALID_PSOC_REG_OBJ(psoc_reg)) {
+		reg_err("psoc reg component is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!qdf_mem_cmp(psoc_reg->cur_country,
+			 country, REG_ALPHA2_LEN)) {
+		reg_debug("country is not different");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	reg_info("programming new 11d country:%c%c to firmware",
+		 country[0], country[1]);
+
+	qdf_mem_copy(country_code.country,
+		     country, REG_ALPHA2_LEN + 1);
+	country_code.pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+
+	psoc_reg->new_11d_ctry_pending = true;
+
+	if (psoc_reg->offload_enabled) {
+		reg_err("reg offload, 11d offload too!");
+		status = QDF_STATUS_E_FAULT;
+	} else {
+		qdf_mem_copy(rd.cc.alpha, country, REG_ALPHA2_LEN + 1);
+		rd.flags = ALPHA_IS_SET;
+		reg_program_chan_list(pdev, &rd);
+		status = QDF_STATUS_SUCCESS;
+	}
+
+	return status;
+}
+
 QDF_STATUS reg_reset_country(struct wlan_objmgr_psoc *psoc)
 {
 	struct wlan_regulatory_psoc_priv_obj *psoc_reg;
@@ -1864,9 +1930,16 @@ static void reg_populate_band_channels(enum channel_enum start_chan,
 
 		if (found_rule_ptr) {
 			mas_chan_list[chan_enum].max_bw = bw;
-
 			reg_fill_channel_info(chan_enum, found_rule_ptr,
 					      mas_chan_list, min_bw);
+			/* Disable 2.4 Ghz channels that dont have 20 mhz bw */
+			if (start_chan == MIN_24GHZ_CHANNEL &&
+			    20 > mas_chan_list[chan_enum].max_bw) {
+				mas_chan_list[chan_enum].chan_flags |=
+						REGULATORY_CHAN_DISABLED;
+				mas_chan_list[chan_enum].state =
+						REGULATORY_CHAN_DISABLED;
+			}
 		}
 	}
 }
@@ -3967,6 +4040,23 @@ QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+bool reg_11d_original_enabled_on_host(struct wlan_objmgr_psoc *psoc)
+{
+	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
+
+	psoc_priv_obj =
+	     wlan_objmgr_psoc_get_comp_private_obj(psoc,
+						   WLAN_UMAC_COMP_REGULATORY);
+
+	if (NULL == psoc_priv_obj) {
+		reg_err("reg psoc private obj is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return (psoc_priv_obj->enable_11d_supp_original &&
+		!psoc_priv_obj->is_11d_offloaded);
+}
+
 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
 {
 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;

+ 33 - 0
umac/regulatory/core/src/reg_services.h

@@ -164,6 +164,22 @@ QDF_STATUS reg_read_current_country(struct wlan_objmgr_psoc *psoc,
 QDF_STATUS reg_set_default_country(struct wlan_objmgr_psoc *psoc,
 		uint8_t *country);
 
+/**
+ * reg_is_world_alpha2 - is reg world mode
+ * @alpha2: country code pointer
+ *
+ * Return: true or false
+ */
+bool reg_is_world_alpha2(uint8_t *alpha2);
+
+/**
+ * reg_is_us_alpha2 - is US country code
+ * @alpha2: country code pointer
+ *
+ * Return: true or false
+ */
+bool reg_is_us_alpha2(uint8_t *alpha2);
+
 /**
  * reg_set_country() - Set the current regulatory country
  * @pdev: pdev device for country information
@@ -173,6 +189,15 @@ QDF_STATUS reg_set_default_country(struct wlan_objmgr_psoc *psoc,
  */
 QDF_STATUS reg_set_country(struct wlan_objmgr_pdev *pdev, uint8_t *country);
 
+/**
+ * reg_set_11d_country() - Set the 11d regulatory country
+ * @pdev: pdev device for country information
+ * @country: country value
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS reg_set_11d_country(struct wlan_objmgr_pdev *pdev, uint8_t *country);
+
 /**
  * reg_reset_country() - Reset the regulatory country to default
  * @psoc: The physical SoC to reset country for
@@ -336,6 +361,14 @@ enum country_src reg_get_cc_and_src(struct wlan_objmgr_psoc *psoc,
 QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
 		uint8_t *country);
 
+/**
+ * reg_11d_original_enabled_on_host() - whether 11d original enabled on host
+ * @psoc: psoc ptr
+ *
+ * Return: bool
+ */
+bool reg_11d_original_enabled_on_host(struct wlan_objmgr_psoc *psoc);
+
 /**
  * reg_11d_enabled_on_host() - know whether 11d enabled on host
  * @psoc: psoc ptr

+ 35 - 1
umac/regulatory/dispatcher/inc/wlan_reg_services_api.h

@@ -372,6 +372,21 @@ uint32_t wlan_reg_freq_to_chan(struct wlan_objmgr_pdev *pdev,
  */
 uint32_t wlan_reg_chan_to_freq(struct wlan_objmgr_pdev *pdev,
 			       uint32_t chan);
+/**
+ * wlan_reg_is_world() - reg is world mode
+ * @country: The country information
+ *
+ * Return: true or false
+ */
+bool wlan_reg_is_world(uint8_t *country);
+
+/**
+ * wlan_reg_is_us() - reg is us country
+ * @country: The country information
+ *
+ * Return: true or false
+ */
+bool wlan_reg_is_us(uint8_t *country);
 
 /**
  * wlan_reg_set_country() - Set the current regulatory country
@@ -381,7 +396,17 @@ uint32_t wlan_reg_chan_to_freq(struct wlan_objmgr_pdev *pdev,
  * Return: QDF_STATUS
  */
 QDF_STATUS wlan_reg_set_country(struct wlan_objmgr_pdev *pdev,
-				       uint8_t *country);
+				uint8_t *country);
+
+/**
+ * wlan_reg_set_11d_country() - Set the 11d regulatory country
+ * @pdev: The physical dev to set current country for
+ * @country: The country information to configure
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_reg_set_11d_country(struct wlan_objmgr_pdev *pdev,
+				    uint8_t *country);
 
 /**
  * wlan_reg_register_chan_change_callback () - add chan change cbk
@@ -404,6 +429,15 @@ void wlan_reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
  */
 void wlan_reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
 					      reg_chan_change_callback cbk);
+
+/**
+ * wlan_reg_11d_original_enabled_on_host() - 11d original enabled don host
+ * @psoc: psoc ptr
+ *
+ * Return: bool
+ */
+bool wlan_reg_11d_original_enabled_on_host(struct wlan_objmgr_psoc *psoc);
+
 /**
  * wlan_reg_11d_enabled_on_host() - 11d enabled don host
  * @psoc: psoc ptr

+ 21 - 0
umac/regulatory/dispatcher/src/wlan_reg_services_api.c

@@ -451,6 +451,22 @@ QDF_STATUS wlan_reg_set_country(struct wlan_objmgr_pdev *pdev,
 	return reg_set_country(pdev, country);
 }
 
+QDF_STATUS wlan_reg_set_11d_country(struct wlan_objmgr_pdev *pdev,
+				    uint8_t *country)
+{
+	return reg_set_11d_country(pdev, country);
+}
+
+bool wlan_reg_is_world(uint8_t *country)
+{
+	return reg_is_world_alpha2(country);
+}
+
+bool wlan_reg_is_us(uint8_t *country)
+{
+	return reg_is_us_alpha2(country);
+}
+
 void wlan_reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
 					    reg_chan_change_callback cbk,
 					    void *arg)
@@ -465,6 +481,11 @@ void wlan_reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
 	reg_unregister_chan_change_callback(psoc, cbk);
 }
 
+bool wlan_reg_11d_original_enabled_on_host(struct wlan_objmgr_psoc *psoc)
+{
+	return reg_11d_original_enabled_on_host(psoc);
+}
+
 bool wlan_reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
 {
 	return reg_11d_enabled_on_host(psoc);

+ 354 - 0
umac/scan/core/src/wlan_scan_11d.c

@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * DOC: contains scan 11d api and functionality
+ */
+#include <qdf_status.h>
+#include <wlan_objmgr_psoc_obj.h>
+#include <wlan_objmgr_pdev_obj.h>
+#include <wlan_objmgr_vdev_obj.h>
+#include <wlan_scan_public_structs.h>
+#include <wlan_scan_utils_api.h>
+#include "wlan_scan_main.h"
+#include "wlan_scan_11d.h"
+#include "wlan_reg_services_api.h"
+#include "wlan_reg_ucfg_api.h"
+
+/**
+ * wlan_pdevid_get_cc_db() - private API to get cc db from pdev id
+ * @psoc: psoc object
+ * @pdev_id: pdev id
+ *
+ * Return: cc db for the pdev id
+ */
+static struct scan_country_code_db *
+wlan_pdevid_get_cc_db(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id)
+{
+	struct wlan_scan_obj *scan_obj;
+
+	if (pdev_id > WLAN_UMAC_MAX_PDEVS) {
+		scm_err("invalid pdev_id %d", pdev_id);
+		return NULL;
+	}
+
+	scan_obj = wlan_psoc_get_scan_obj(psoc);
+	if (!scan_obj)
+		return NULL;
+
+	return &scan_obj->cc_db[pdev_id];
+}
+
+/**
+ * wlan_pdev_get_cc_db() - private API to get cc db from pdev
+ * @psoc: psoc object
+ * @pdev: Pdev object
+ *
+ * Return: cc db for the pdev
+ */
+static struct scan_country_code_db *
+wlan_pdev_get_cc_db(struct wlan_objmgr_psoc *psoc,
+		    struct wlan_objmgr_pdev *pdev)
+{
+	uint8_t pdev_id;
+
+	if (!pdev) {
+		scm_err("pdev is NULL");
+		return NULL;
+	}
+	pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+
+	return wlan_pdevid_get_cc_db(psoc, pdev_id);
+}
+
+/**
+ * scm_11d_elected_country_algo_fcc - private api to get cc per fcc algo
+ * @cc_db: scan country code db
+ *
+ * Return: true or false
+ */
+static bool
+scm_11d_elected_country_algo_fcc(struct scan_country_code_db *cc_db)
+{
+	uint8_t i;
+	uint8_t country_idx;
+	uint16_t max_votes;
+	bool found = false;
+
+	if (!cc_db->num_country_codes) {
+		scm_err("No AP with 11d Country code is present in scan list");
+		return false;
+	}
+
+	max_votes = cc_db->votes[0].votes;
+	if (wlan_reg_is_us(cc_db->votes[0].cc)) {
+		found = true;
+		country_idx = 0;
+		goto algo_done;
+	} else if (max_votes >= MIN_11D_AP_COUNT) {
+		found = true;
+		country_idx = 0;
+	}
+
+	for (i = 1; i < cc_db->num_country_codes; i++) {
+		if (wlan_reg_is_us(cc_db->votes[i].cc)) {
+			found = true;
+			country_idx = i;
+			goto algo_done;
+		}
+
+		if ((max_votes < cc_db->votes[i].votes) &&
+		    (cc_db->votes[i].votes >= MIN_11D_AP_COUNT)) {
+			scm_debug("Votes for Country %c%c : %d",
+				  cc_db->votes[i].cc[0],
+				  cc_db->votes[i].cc[1],
+				  cc_db->votes[i].votes);
+			max_votes = cc_db->votes[i].votes;
+			country_idx = i;
+			found = true;
+		}
+	}
+
+algo_done:
+	if (found) {
+		qdf_mem_copy(cc_db->elected_cc,
+			     cc_db->votes[country_idx].cc,
+			     REG_ALPHA2_LEN + 1);
+
+		scm_debug("Selected Country is %c%c With count %d",
+			  cc_db->votes[country_idx].cc[0],
+			  cc_db->votes[country_idx].cc[1],
+			  cc_db->votes[country_idx].votes);
+	}
+
+	return found;
+}
+
+/**
+ * scm_11d_elected_country_info - private api to get cc
+ * @cc_db: scan country code db
+ *
+ * Return: true or false
+ */
+static bool
+scm_11d_elected_country_info(struct scan_country_code_db *cc_db)
+{
+	uint8_t i, j = 0;
+	uint8_t max_votes;
+
+	if (!cc_db->num_country_codes) {
+		scm_err("No AP with 11d Country code is present in scan list");
+		return false;
+	}
+
+	max_votes = cc_db->votes[0].votes;
+
+	for (i = 1; i < cc_db->num_country_codes; i++) {
+		/*
+		 * If we have a tie for max votes for 2 different country codes,
+		 * pick random.
+		 */
+		if (max_votes < cc_db->votes[i].votes) {
+			scm_debug("Votes for Country %c%c : %d",
+				  cc_db->votes[i].cc[0],
+				  cc_db->votes[i].cc[1],
+				  cc_db->votes[i].votes);
+
+			max_votes = cc_db->votes[i].votes;
+			j = i;
+		}
+	}
+
+	qdf_mem_copy(cc_db->elected_cc, cc_db->votes[j].cc,
+		     REG_ALPHA2_LEN + 1);
+
+	scm_debug("Selected Country is %c%c With count %d",
+		  cc_db->votes[j].cc[0],
+		  cc_db->votes[j].cc[1],
+		  cc_db->votes[j].votes);
+
+	return true;
+}
+
+/**
+ * scm_11d_set_country_code - private api to set cc per 11d learning
+ * @pdev: pdev object
+ * @elected_cc: elected country code
+ * @current_cc: current country code
+ *
+ * Return: true or false
+ */
+static bool
+scm_11d_set_country_code(struct wlan_objmgr_pdev *pdev,
+			 uint8_t *elected_cc, uint8_t *current_cc)
+{
+	scm_debug("elected country %c%c, current country %c%c",
+		  elected_cc[0], elected_cc[1], current_cc[0], current_cc[1]);
+
+	if (!qdf_mem_cmp(elected_cc, current_cc, REG_ALPHA2_LEN + 1))
+		return true;
+
+	wlan_reg_set_11d_country(pdev, elected_cc);
+	return true;
+}
+
+/**
+ * scm_11d_reset_cc_db - reset the country code db
+ * @cc_db: the pointer of country code db
+ *
+ * Return: void
+ */
+static void scm_11d_reset_cc_db(struct scan_country_code_db *cc_db)
+{
+	qdf_mem_zero(cc_db->votes, sizeof(cc_db->votes));
+	qdf_mem_zero(cc_db->elected_cc, sizeof(cc_db->elected_cc));
+	cc_db->num_country_codes = 0;
+}
+
+QDF_STATUS scm_11d_cc_db_init(struct wlan_objmgr_psoc *psoc)
+{
+	struct scan_country_code_db *cc_db;
+	struct wlan_scan_obj *scan_obj;
+
+	if (!psoc) {
+		scm_err("psoc is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	scan_obj = wlan_psoc_get_scan_obj(psoc);
+	if (!scan_obj) {
+		scm_err("scan_obj is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	cc_db = (struct scan_country_code_db *)qdf_mem_malloc(
+		   sizeof(struct scan_country_code_db) * WLAN_UMAC_MAX_PDEVS);
+	if (!cc_db) {
+		scm_err("alloc country code db error");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	qdf_mem_zero(cc_db,
+		     sizeof(struct scan_country_code_db) *
+			    WLAN_UMAC_MAX_PDEVS);
+
+	scan_obj->cc_db = cc_db;
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS scm_11d_cc_db_deinit(struct wlan_objmgr_psoc *psoc)
+{
+	struct wlan_scan_obj *scan_obj;
+
+	if (!psoc) {
+		scm_err("psoc is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	scan_obj = wlan_psoc_get_scan_obj(psoc);
+	if (!scan_obj) {
+		scm_err("scan_obj is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	qdf_mem_free(scan_obj->cc_db);
+	return QDF_STATUS_SUCCESS;
+}
+
+void scm_11d_handle_country_info(struct wlan_objmgr_psoc *psoc,
+				 struct wlan_objmgr_pdev *pdev,
+				 struct scan_cache_entry *scan_entry)
+{
+	uint8_t i;
+	bool match = false;
+	uint8_t num_country_codes;
+	struct scan_country_code_db *cc_db;
+	struct wlan_country_ie *cc_ie;
+
+	cc_ie = util_scan_entry_country(scan_entry);
+	if (!cc_ie)
+		return;
+
+	cc_db = wlan_pdev_get_cc_db(psoc, pdev);
+	if (!cc_db)
+		return;
+
+	/* just to be sure, convert to UPPER case here */
+	for (i = 0; i < 3; i++)
+		cc_ie->cc[i] = qdf_toupper(cc_ie->cc[i]);
+
+	num_country_codes = cc_db->num_country_codes;
+	for (i = 0; i < num_country_codes; i++) {
+		match = !qdf_mem_cmp(cc_db->votes[i].cc, cc_ie->cc,
+				     REG_ALPHA2_LEN);
+		if (match)
+			break;
+	}
+
+	if (match) {
+		cc_db->votes[i].votes++;
+		return;
+	}
+
+	if (num_country_codes >= SCAN_MAX_NUM_COUNTRY_CODE) {
+		scm_debug("country code db already full: %d",
+			  num_country_codes);
+		return;
+	}
+
+	/* add country code to end of the list */
+	qdf_mem_copy(cc_db->votes[num_country_codes].cc, cc_ie->cc,
+		     REG_ALPHA2_LEN + 1);
+	cc_db->votes[num_country_codes].votes = 1;
+	cc_db->num_country_codes++;
+}
+
+void scm_11d_decide_country_code(struct wlan_objmgr_vdev *vdev)
+{
+	uint8_t current_cc[REG_ALPHA2_LEN + 1];
+	bool found;
+	struct scan_country_code_db *cc_db;
+	struct wlan_objmgr_pdev *pdev = wlan_vdev_get_pdev(vdev);
+	struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev);
+
+	if (!wlan_reg_11d_enabled_on_host(psoc)) {
+		scm_err("11d is not enabled");
+		return;
+	}
+
+	if (SOURCE_UNKNOWN == ucfg_reg_get_cc_and_src(psoc, current_cc)) {
+		scm_err("fail to get current country code");
+		return;
+	}
+
+	cc_db = wlan_pdev_get_cc_db(psoc, pdev);
+	if (!cc_db) {
+		scm_err("scan_db is NULL");
+		return;
+	}
+
+	if (wlan_reg_is_us(current_cc) || wlan_reg_is_world(current_cc))
+		found = scm_11d_elected_country_algo_fcc(cc_db);
+	else
+		found = scm_11d_elected_country_info(cc_db);
+
+	if (found)
+		scm_11d_set_country_code(pdev, cc_db->elected_cc,
+					 current_cc);
+	scm_11d_reset_cc_db(cc_db);
+}

+ 95 - 0
umac/scan/core/src/wlan_scan_11d.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * DOC: contains scan 11d entry api
+ */
+
+#ifndef _WLAN_SCAN_11D_H_
+#define _WLAN_SCAN_11D_H_
+
+#define SCAN_MAX_NUM_COUNTRY_CODE 100
+#define MIN_11D_AP_COUNT 3
+
+/**
+ * struct scan_country_code_votes - votes to country code mapping structure
+ * @votes: votes
+ * @cc: country code
+ */
+struct scan_country_code_votes {
+	uint16_t votes;
+	uint8_t cc[REG_ALPHA2_LEN + 1];
+};
+
+/**
+ * struct scan_country_code_db - country code data base definition
+ * @elected_cc: elected country code
+ * @num_country_codes: number of country codes encountered
+ * @votes: votes to country code mapping array
+ */
+struct scan_country_code_db {
+	uint8_t elected_cc[REG_ALPHA2_LEN + 1];
+	uint8_t num_country_codes;
+	struct scan_country_code_votes votes[SCAN_MAX_NUM_COUNTRY_CODE];
+};
+
+/**
+ * scm_11d_cc_db_init() - API to init 11d country code db
+ * @psoc: psoc object
+ *
+ * Initialize the country code database.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS scm_11d_cc_db_init(struct wlan_objmgr_psoc *psoc);
+
+/**
+ * scm_11d_cc_db_deinit() - API to deinit 11d country code db
+ * @psoc: psoc object
+ *
+ * free the country code database.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS scm_11d_cc_db_deinit(struct wlan_objmgr_psoc *psoc);
+
+/**
+ * scm_11d_handle_country_info() - API to handle 11d country info
+ * @psoc: psoc object
+ * @pdev: pdev object
+ * @scan_entry: the pointer to scan entry
+ *
+ * Update the country code database per the country code from country IE.
+ *
+ * Return: void
+ */
+void scm_11d_handle_country_info(struct wlan_objmgr_psoc *psoc,
+				 struct wlan_objmgr_pdev *pdev,
+				 struct scan_cache_entry *scan_entry);
+
+/**
+ * scm_11d_decide_country_code() - API to decide the country code per 11d
+ * @vdev: vdev object
+ *
+ * Decide which country will be elected from the country database. If one
+ * cadidate country is found, then it set the country code.
+ *
+ * Return: void
+ */
+void scm_11d_decide_country_code(struct wlan_objmgr_vdev *vdev);
+#endif

+ 5 - 0
umac/scan/core/src/wlan_scan_cache_db.c

@@ -37,6 +37,8 @@
 #include <wlan_scan_utils_api.h>
 #include "wlan_scan_main.h"
 #include "wlan_scan_cache_db_i.h"
+#include "wlan_reg_services_api.h"
+#include "wlan_reg_ucfg_api.h"
 
 /**
  * scm_del_scan_node() - API to remove scan node from the list
@@ -692,6 +694,9 @@ QDF_STATUS scm_handle_bcn_probe(struct scheduler_msg *msg)
 
 		scan_entry = scan_node->entry;
 
+		if (wlan_reg_11d_enabled_on_host(psoc))
+			scm_11d_handle_country_info(psoc, pdev, scan_entry);
+
 		status = scm_add_update_entry(scan_db, scan_entry);
 		if (QDF_IS_STATUS_ERROR(status)) {
 			util_scan_free_cache_entry(scan_entry);

+ 3 - 2
umac/scan/core/src/wlan_scan_filter.c

@@ -893,6 +893,7 @@ bool scm_filter_match(struct wlan_objmgr_psoc *psoc,
 	bool match = false;
 	struct roam_filter_params *roam_params;
 	struct scan_default_params *def_param;
+	struct wlan_country_ie *cc_ie;
 
 	def_param = wlan_scan_psoc_get_def_params(psoc);
 	if (!def_param)
@@ -972,8 +973,8 @@ bool scm_filter_match(struct wlan_objmgr_psoc *psoc,
 	if (!scm_is_fils_config_match(filter, db_entry))
 		return false;
 
-	if (!util_country_code_match(filter->country,
-	   db_entry->ie_list.country))
+	cc_ie = util_scan_entry_country(db_entry);
+	if (!util_country_code_match(filter->country, cc_ie))
 		return false;
 
 	if (!util_mdie_match(filter->mobility_domain,

+ 3 - 0
umac/scan/core/src/wlan_scan_main.h

@@ -29,6 +29,7 @@
 #include <wlan_objmgr_vdev_obj.h>
 #include <wlan_scan_public_structs.h>
 #include "wlan_scan_cache_db.h"
+#include "wlan_scan_11d.h"
 
 #define scm_log(level, args...) \
 		QDF_TRACE(QDF_MODULE_ID_SCAN, level, ## args)
@@ -396,6 +397,7 @@ struct scan_cb {
  * struct wlan_scan_obj - scan object definition
  * @enable_scan: if scan is enabled
  * @scan_db:    scan cache data base
+ * @cc_db:      pointer of country code data base
  * @lock:       spin lock
  * @scan_def:   default scan parameters
  * @cb:         nif/sif function callbacks
@@ -414,6 +416,7 @@ struct wlan_scan_obj {
 	qdf_spinlock_t lock;
 	qdf_atomic_t scan_ids;
 	struct scan_dbs scan_db[WLAN_UMAC_MAX_PDEVS];
+	struct scan_country_code_db *cc_db;
 	struct scan_default_params scan_def;
 	struct scan_cb cb;
 	struct scan_requester_info requesters[WLAN_MAX_REQUESTORS];

+ 2 - 0
umac/scan/core/src/wlan_scan_manager.c

@@ -727,6 +727,8 @@ scm_scan_event_handler(struct scheduler_msg *msg)
 
 	switch (event->type) {
 	case SCAN_EVENT_TYPE_COMPLETED:
+		scm_11d_decide_country_code(vdev);
+		/* fall through to release the command */
 	case SCAN_EVENT_TYPE_START_FAILED:
 	case SCAN_EVENT_TYPE_DEQUEUED:
 		scm_release_serialization_command(vdev, event->scan_id);

+ 5 - 9
umac/scan/dispatcher/inc/wlan_scan_utils_api.h

@@ -339,11 +339,8 @@ static inline bool util_is_bss_type_match(enum wlan_bss_type bss_type,
  * Return: true if country match
  */
 static inline bool util_country_code_match(uint8_t *country,
-	uint8_t *country_ie)
+					   struct wlan_country_ie *cc)
 {
-	struct wlan_country_ie *cc =
-		(struct wlan_country_ie *)country;
-
 	if (!country || !country[0])
 		return true;
 
@@ -351,7 +348,7 @@ static inline bool util_country_code_match(uint8_t *country,
 		return false;
 
 	if (cc->cc[0] == country[0] &&
-		cc->cc[1] == country[1])
+	    cc->cc[1] == country[1])
 		return true;
 
 	return false;
@@ -1034,10 +1031,10 @@ util_scan_entry_vendor(struct scan_cache_entry *scan_entry)
  *
  * Return: countryie or NULL if ie is not present
  */
-static inline uint8_t*
+static inline struct wlan_country_ie*
 util_scan_entry_country(struct scan_cache_entry *scan_entry)
 {
-	return scan_entry->ie_list.country;
+	return (struct wlan_country_ie *)scan_entry->ie_list.country;
 }
 
 /**
@@ -1060,8 +1057,7 @@ util_scan_entry_copy_country(struct scan_cache_entry *scan_entry,
 	if (!cntry)
 		return QDF_STATUS_E_INVAL;
 
-	country_ie = (struct wlan_country_ie *)
-		util_scan_entry_country(scan_entry);
+	country_ie = util_scan_entry_country(scan_entry);
 
 	if (!country_ie)
 		return QDF_STATUS_E_NOMEM;

+ 4 - 0
umac/scan/dispatcher/src/wlan_scan_ucfg_api.c

@@ -1722,6 +1722,8 @@ ucfg_scan_psoc_enable(struct wlan_objmgr_psoc *psoc)
 	status = tgt_scan_register_ev_handler(psoc);
 	QDF_ASSERT(status == QDF_STATUS_SUCCESS);
 	scm_db_init(psoc);
+	if (wlan_reg_11d_original_enabled_on_host(psoc))
+		scm_11d_cc_db_init(psoc);
 	ucfg_scan_register_unregister_bcn_cb(psoc, true);
 	status = wlan_serialization_register_apply_rules_cb(psoc,
 				WLAN_SER_CMD_SCAN,
@@ -1744,6 +1746,8 @@ ucfg_scan_psoc_disable(struct wlan_objmgr_psoc *psoc)
 	status = tgt_scan_unregister_ev_handler(psoc);
 	QDF_ASSERT(status == QDF_STATUS_SUCCESS);
 	ucfg_scan_register_unregister_bcn_cb(psoc, false);
+	if (wlan_reg_11d_original_enabled_on_host(psoc))
+		scm_11d_cc_db_deinit(psoc);
 	scm_db_deinit(psoc);
 
 	return status;