/*
 * Copyright (c) 2012-2019, 2021 The Linux Foundation. All rights reserved.
 * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */


/*============================================================================
  FILE:         vos_nvitem.c
  OVERVIEW:     This source file contains definitions for vOS NV Item APIs
  DEPENDENCIES: NV, remote API client, WinCE REX
============================================================================*/
/*============================================================================
  EDIT HISTORY FOR MODULE
============================================================================*/
// the following is used to disable warning for having too many labels in
// the 'nv_items_enum_type'

/*----------------------------------------------------------------------------
 * Include Files
 * -------------------------------------------------------------------------*/
#include "vos_types.h"
#include "aniGlobal.h"
#include "vos_nvitem.h"
#include "vos_trace.h"
#include "vos_api.h"
#include "wlan_hdd_misc.h"
#include "vos_sched.h"
#include "sme_Api.h"
#include "wlan_hdd_main.h"
#include <net/cfg80211.h>
#include "regdomain.h"
#include "regdomain_common.h"
#include "vos_cnss.h"
#include "wma.h"
#ifdef CLD_REGDB
#include "regdb.h"
#include <net/regulatory.h>
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)) && !defined(WITH_BACKPORTS)
#define IEEE80211_CHAN_NO_80MHZ		1<<7
#endif

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR
#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR
#endif

static v_REGDOMAIN_t temp_reg_domain = REGDOMAIN_COUNT;
/* true if init happens thru init time driver hint */
static v_BOOL_t init_by_driver = VOS_FALSE;
/* true if init happens thru init time  callback from regulatory core.
   this should be set to true during driver reload */
static v_BOOL_t init_by_reg_core = VOS_FALSE;


/*----------------------------------------------------------------------------
 * Preprocessor Definitions and Constants
 * -------------------------------------------------------------------------*/
#define MAX_COUNTRY_COUNT        300
#define REG_WAIT_TIME            50
/*
 * This is a set of common rules used by our world regulatory domains.
 * We have 12 world regulatory domains. To save space we consolidate
 * the regulatory domains in 5 structures by frequency and change
 * the flags on our reg_notifier() on a case by case basis.
 */

#define REG_RULE_2412_2462    REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)

#define REG_RULE_2467_2472    REG_RULE(2467-10, 2472+10, 40, 0, 20, \
	NL80211_RRF_PASSIVE_SCAN)

#define REG_RULE_2484         REG_RULE(2484-10, 2484+10, 40, 0, 20, \
	NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)

#define REG_RULE_5180_5320    REG_RULE(5180-10, 5320+10, 80, 0, 20, \
	NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)

#define REG_RULE_5500_5720    REG_RULE(5500-10, 5720+10, 80, 0, 20, \
	NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)

#define REG_RULE_5745_5925    REG_RULE(5745-10, 5925+10, 80, 0, 20, \
	NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)

#define REG_RULE_2GHZ_CH01_11 REG_RULE_2412_2462

#define REG_RULE_2GHZ_CH12_13 REG_RULE_2467_2472

#define REG_RULE_2GHZ_ALL     REG_RULE_2412_2462,\
        REG_RULE_2467_2472,\
        REG_RULE_2484

#define REG_RULE_5GHZ_ALL     REG_RULE_5180_5320,\
        REG_RULE_5500_5720,\
        REG_RULE_5745_5925

#define REG_RULE_5GHZ_NO_MIDBAND   REG_RULE_5180_5320,\
        REG_RULE_5745_5925


#define WORLD_SKU_MASK          0x00F0
#define WORLD_SKU_PREFIX        0x0060

#define REG_SET_WAIT_MS        100

/**
 * struct bonded_chan
 * @start_ch: start channel
 * @end_ch: end channel
 */
struct bonded_chan {
	uint16_t start_ch;
	uint16_t end_ch;
};

static const struct bonded_chan bonded_chan_40mhz_array[] = {
	{36, 40},
	{44, 48},
	{52, 56},
	{60, 64},
	{100, 104},
	{108, 112},
	{116, 120},
	{124, 128},
	{132, 136},
	{140, 144},
	{149, 153},
	{157, 161},
	{165, 169}
};

static const struct bonded_chan bonded_chan_80mhz_array[] = {
	{36, 48},
	{52, 64},
	{100, 112},
	{116, 128},
	{132, 144},
	{149, 161}
};

static const enum phy_ch_width next_lower_bw[] = {
	[CH_WIDTH_80MHZ] = CH_WIDTH_40MHZ,
	[CH_WIDTH_40MHZ] = CH_WIDTH_20MHZ,
	[CH_WIDTH_20MHZ] = CH_WIDTH_10MHZ,
	[CH_WIDTH_10MHZ] = CH_WIDTH_5MHZ,
	[CH_WIDTH_5MHZ] = CH_WIDTH_INVALID
};

static const struct ieee80211_regdomain vos_world_regdom_60_61_62 = {
   .n_reg_rules = 6,
   .alpha2 =  "00",
   .reg_rules = {
      REG_RULE_2GHZ_ALL,
      REG_RULE_5GHZ_ALL,
   }
};

static const struct ieee80211_regdomain vos_world_regdom_63_65 = {
   .n_reg_rules = 4,
   .alpha2 =  "00",
   .reg_rules = {
      REG_RULE_2GHZ_CH01_11,
      REG_RULE_2GHZ_CH12_13,
      REG_RULE_5GHZ_NO_MIDBAND,
   }
};

static const struct ieee80211_regdomain vos_world_regdom_64 = {
   .n_reg_rules = 3,
   .alpha2 =  "00",
   .reg_rules = {
      REG_RULE_2GHZ_CH01_11,
      REG_RULE_5GHZ_NO_MIDBAND,
   }
};

static const struct ieee80211_regdomain vos_world_regdom_66_69 = {
   .n_reg_rules = 4,
   .alpha2 =  "00",
   .reg_rules = {
      REG_RULE_2GHZ_CH01_11,
      REG_RULE_5GHZ_ALL,
   }
};

static const struct ieee80211_regdomain vos_world_regdom_67_68_6A_6C = {
   .n_reg_rules = 5,
   .alpha2 =  "00",
   .reg_rules = {
      REG_RULE_2GHZ_CH01_11,
      REG_RULE_2GHZ_CH12_13,
      REG_RULE_5GHZ_ALL,
   }
};

/*----------------------------------------------------------------------------
 * Type Declarations
 * -------------------------------------------------------------------------*/
// this wrapper structure is identical to nv_cmd_type except the
// data_ptr type is changed void* to avoid exceeding the debug information
// module size as there are too many elements within nv_items_type union

// structure for code and regulatory domain of a single country
typedef struct
{
   v_U8_t            regDomain;
   v_COUNTRYCODE_t   countryCode;
} CountryInfo_t;
// structure of table to map country code and regulatory domain
typedef struct
{
   v_U16_t           countryCount;
   CountryInfo_t     countryInfo[MAX_COUNTRY_COUNT];
} CountryInfoTable_t;
/*----------------------------------------------------------------------------
 * Global Data Definitions
 * -------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Static Variable Definitions
 * -------------------------------------------------------------------------*/

static struct
chan_to_ht_40_index_map chan_to_ht_40_index[NUM_20MHZ_RF_CHANNELS] =
{
  /* ht_40_minus_index, ht_40_plus_index */
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_3},    //RF_CHAN_1,
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_4},    //RF_CHAN_2,
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_5},    //RF_CHAN_3,
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_6},    //RF_CHAN_4,
  {RF_CHAN_BOND_3, RF_CHAN_BOND_7},        //RF_CHAN_5,
  {RF_CHAN_BOND_4, RF_CHAN_BOND_8},        //RF_CHAN_6,
  {RF_CHAN_BOND_5, RF_CHAN_BOND_9},        //RF_CHAN_7,
  {RF_CHAN_BOND_6, RF_CHAN_BOND_10},       //RF_CHAN_8,
  {RF_CHAN_BOND_7, RF_CHAN_BOND_11},       //RF_CHAN_9,
  {RF_CHAN_BOND_8, INVALID_RF_CHANNEL},    //RF_CHAN_10,
  {RF_CHAN_BOND_9, INVALID_RF_CHANNEL},    //RF_CHAN_11,
  {RF_CHAN_BOND_10, INVALID_RF_CHANNEL},   //RF_CHAN_12,
  {RF_CHAN_BOND_11, INVALID_RF_CHANNEL},   //RF_CHAN_13,
  {INVALID_RF_CHANNEL, INVALID_RF_CHANNEL},//RF_CHAN_14,
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_38},   //RF_CHAN_36,
  {RF_CHAN_BOND_38, RF_CHAN_BOND_42},      //RF_CHAN_40,
  {RF_CHAN_BOND_42, RF_CHAN_BOND_46},      //RF_CHAN_44,
  {RF_CHAN_BOND_46, RF_CHAN_BOND_50},      //RF_CHAN_48,
  {RF_CHAN_BOND_50, RF_CHAN_BOND_54},      //RF_CHAN_52,
  {RF_CHAN_BOND_54, RF_CHAN_BOND_58},      //RF_CHAN_56,
  {RF_CHAN_BOND_58, RF_CHAN_BOND_62},      //RF_CHAN_60,
  {RF_CHAN_BOND_62, INVALID_RF_CHANNEL},   //RF_CHAN_64,
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_102},  //RF_CHAN_100,
  {RF_CHAN_BOND_102, RF_CHAN_BOND_106},    //RF_CHAN_104,
  {RF_CHAN_BOND_106, RF_CHAN_BOND_110},    //RF_CHAN_108,
  {RF_CHAN_BOND_110, RF_CHAN_BOND_114},    //RF_CHAN_112,
  {RF_CHAN_BOND_114, RF_CHAN_BOND_118},    //RF_CHAN_116,
  {RF_CHAN_BOND_118, RF_CHAN_BOND_122},    //RF_CHAN_120,
  {RF_CHAN_BOND_122, RF_CHAN_BOND_126},    //RF_CHAN_124,
  {RF_CHAN_BOND_126, RF_CHAN_BOND_130},    //RF_CHAN_128,
  {RF_CHAN_BOND_130, RF_CHAN_BOND_134},    //RF_CHAN_132,
  {RF_CHAN_BOND_134, RF_CHAN_BOND_138},    //RF_CHAN_136,
  {RF_CHAN_BOND_138, RF_CHAN_BOND_142},    //RF_CHAN_140,
#ifdef FEATURE_WLAN_CH144
  {RF_CHAN_BOND_142, INVALID_RF_CHANNEL},  //RF_CHAN_144,
#endif /* FEATURE_WLAN_CH144 */
  {INVALID_RF_CHANNEL, RF_CHAN_BOND_151},  //RF_CHAN_149,
  {RF_CHAN_BOND_151, RF_CHAN_BOND_155},    //RF_CHAN_153,
  {RF_CHAN_BOND_155, RF_CHAN_BOND_159},    //RF_CHAN_157,
  {RF_CHAN_BOND_159, RF_CHAN_BOND_163},    //RF_CHAN_161,
  {RF_CHAN_BOND_163, INVALID_RF_CHANNEL},  //RF_CHAN_165,
};

/*
 *  ETSI is updating EN 301 893, which specifies 5 GHz channel access
 *  in Europe
 */
static const v_COUNTRYCODE_t etsi_europe_country[] = {
	{'A','T'},
	{'B','E'},
	{'B','G'},
	{'C','Z'},
	{'D','K'},
	{'E','E'},
	{'F','R'},

	{'D','E'},
	{'I','S'},
	{'I','E'},
	{'I','T'},
	{'E','L'},
	{'E','S'},
	{'C','Y'},

	{'L','V'},
	{'L','I'},
	{'L','T'},
	{'L','U'},
	{'H','U'},
	{'M','T'},
	{'N','L'},

	{'N','O'},
	{'P','L'},
	{'P','T'},
	{'R','O'},
	{'S','I'},
	{'S','K'},
	{'T','R'},

	{'F','I'},
	{'S','E'},
	{'C','H'},
	{'U','K'},
	{'H','R'},
};
typedef struct nvEFSTable_s
{
   sHalNv     halnv;
} nvEFSTable_t;

static nvEFSTable_t *pnvEFSTable;

const tRfChannelProps rfChannels[NUM_RF_CHANNELS] =
{
    //RF_SUBBAND_2_4_GHZ
    //freq, chan#, band
    { 2412, 1  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_1,
    { 2417, 2  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_2,
    { 2422, 3  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_3,
    { 2427, 4  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_4,
    { 2432, 5  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_5,
    { 2437, 6  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_6,
    { 2442, 7  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_7,
    { 2447, 8  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_8,
    { 2452, 9  , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_9,
    { 2457, 10 , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_10,
    { 2462, 11 , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_11,
    { 2467, 12 , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_12,
    { 2472, 13 , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_13,
    { 2484, 14 , RF_SUBBAND_2_4_GHZ},        //RF_CHAN_14,
    { 5180, 36 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_36,
    { 5200, 40 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_40,
    { 5220, 44 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_44,
    { 5240, 48 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_48,
    { 5260, 52 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_52,
    { 5280, 56 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_56,
    { 5300, 60 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_60,
    { 5320, 64 , RF_SUBBAND_5_LOW_GHZ},      //RF_CHAN_64,
    { 5500, 100, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_100,
    { 5520, 104, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_104,
    { 5540, 108, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_108,
    { 5560, 112, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_112,
    { 5580, 116, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_116,
    { 5600, 120, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_120,
    { 5620, 124, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_124,
    { 5640, 128, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_128,
    { 5660, 132, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_132,
    { 5680, 136, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_136,
    { 5700, 140, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_140,
#ifdef FEATURE_WLAN_CH144
    { 5720, 144, RF_SUBBAND_5_MID_GHZ},      //RF_CHAN_144,
#endif /* FEATURE_WLAN_CH144 */
    { 5745, 149, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_149,
    { 5765, 153, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_153,
    { 5785, 157, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_157,
    { 5805, 161, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_161,
    { 5825, 165, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_165,
    { 5845, 169, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_169,

    /* 5.9GHz 10 MHz bandwidth (802.11p) */
    { 5852, 170, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_170,
    { 5855, 171, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_171,
    { 5860, 172, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_172,
    { 5865, 173, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_173,
    { 5870, 174, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_174,
    { 5875, 175, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_175,
    { 5880, 176, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_176,
    { 5885, 177, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_177,
    { 5890, 178, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_178,
    { 5895, 179, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_179,
    { 5900, 180, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_180,
    { 5905, 181, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_181,
    { 5910, 182, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_182,
    { 5915, 183, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_183,
    { 5920, 184, RF_SUBBAND_5_HIGH_GHZ},     //RF_CHAN_184,

    { 2422, 3  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_3,
    { 2427, 4  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_4,
    { 2432, 5  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_5,
    { 2437, 6  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_6,
    { 2442, 7  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_7,
    { 2447, 8  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_8,
    { 2452, 9  , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_9,
    { 2457, 10 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_10,
    { 2462, 11 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_11,
    { 5190, 38 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_38,
    { 5210, 42 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_42,
    { 5230, 46 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_46,
    { 5250, 50 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_50,
    { 5270, 54 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_54,
    { 5290, 58 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_58,
    { 5310, 62 , NUM_RF_SUBBANDS},           //RF_CHAN_BOND_62,
    { 5510, 102, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_102,
    { 5530, 106, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_106,
    { 5550, 110, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_110,
    { 5570, 114, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_114,
    { 5590, 118, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_118,
    { 5610, 122, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_122,
    { 5630, 126, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_126,
    { 5650, 130, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_130,
    { 5670, 134, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_134,
    { 5690, 138, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_138,
#ifdef FEATURE_WLAN_CH144
    { 5710, 142, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_142,
#endif /* FEATURE_WLAN_CH144 */
    { 5755, 151, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_151,
    { 5775, 155, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_155,
    { 5795, 159, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_159,
    { 5815, 163, NUM_RF_SUBBANDS},           //RF_CHAN_BOND_163,
};

#define VOS_IS_CHANNEL_5GHZ(chan_num) \
	((chan_num >= rfChannels[RF_CHAN_36].channelNum) && \
	 (chan_num <= rfChannels[RF_CHAN_184].channelNum))
#define VOS_IS_CHANNEL_24GHZ(chan_num) \
	((chan_num >= rfChannels[RF_CHAN_1].channelNum) && \
	 (chan_num <= rfChannels[RF_CHAN_14].channelNum))

extern const sHalNv nvDefaults;

const sRegulatoryChannel * regChannels = nvDefaults.tables.regDomains[0].channels;

static inline bool is_wwr_sku(u16 regd)
{
   return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
      (((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
       (regd == WORLD));
}

bool is_world_regd(u_int32_t regd)
{
   return is_wwr_sku(regd & ~WORLDWIDE_ROAMING_FLAG);
}

static const struct ieee80211_regdomain *vos_default_world_regdomain(void)
{
   /* this is the most restrictive */
    return &vos_world_regdom_64;
}

static const struct ieee80211_regdomain *vos_custom_world_regdomain(void)
{
   /* this is the most restrictive */
    return &vos_world_regdom_60_61_62;
}

/**
 * voss_DomainIdtoString(): converts Reg domain enum to string.
 * @domainIdCurrent: Reg domain enum value.
 */
const char * voss_DomainIdtoString(v_U8_t domainIdCurrent)
{
    switch (domainIdCurrent)
    {
        CASE_RETURN_STRING( REGDOMAIN_FCC );
        CASE_RETURN_STRING( REGDOMAIN_ETSI );
        CASE_RETURN_STRING( REGDOMAIN_JAPAN );
        CASE_RETURN_STRING( REGDOMAIN_WORLD );
        CASE_RETURN_STRING( REGDOMAIN_N_AMER_EXC_FCC );
        CASE_RETURN_STRING( REGDOMAIN_APAC );
        CASE_RETURN_STRING( REGDOMAIN_KOREA );
        CASE_RETURN_STRING( REGDOMAIN_HI_5GHZ );
        CASE_RETURN_STRING( REGDOMAIN_NO_5GHZ );
        CASE_RETURN_STRING( REGDOMAIN_COUNT );
        default:
            return "Regulation Domain Unknown";
    }
}

static const
struct ieee80211_regdomain *vos_world_regdomain(struct regulatory *reg)
{
   REG_DMN_PAIR_MAPPING *regpair;
   regpair = (REG_DMN_PAIR_MAPPING *)reg->regpair;
   switch (regpair->regDmnEnum) {
      case 0x60:
      case 0x61:
      case 0x62:
         return &vos_world_regdom_60_61_62;
      case 0x63:
      case 0x65:
         return &vos_world_regdom_63_65;
      case 0x64:
         return &vos_world_regdom_64;
      case 0x66:
      case 0x69:
         return &vos_world_regdom_66_69;
      case 0x67:
      case 0x68:
      case 0x6A:
      case 0x6C:
         return &vos_world_regdom_67_68_6A_6C;
      default:
         WARN_ON(1);
         return vos_default_world_regdomain();
   }
}

bool vos_is_etsi_europe_country(uint8_t *country)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(etsi_europe_country); i++) {
		if (country[0] == etsi_europe_country[i][0] &&
		    country[1] == etsi_europe_country[i][1])
			return true;
	}
	return false;
}
/**
 * vos_reset_global_reg_params - Reset global static reg params
 *
 * This function is helpful in static driver to reset
 * the global params.
 *
 * Return: void
 */
void vos_reset_global_reg_params()
{
	init_by_driver = false;
	init_by_reg_core = false;
}

static int regd_init_wiphy(hdd_context_t *pHddCtx, struct regulatory *reg,
			   struct wiphy *wiphy)
{
	const struct ieee80211_regdomain *regd;

	if (is_world_regd(reg->reg_domain)) {
		regd = vos_world_regdomain(reg);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
		wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#else
		wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif
	} else if (pHddCtx->cfg_ini->fRegChangeDefCountry) {
		regd = vos_custom_world_regdomain();
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
		wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#else
		wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif

	} else {
		regd = vos_default_world_regdomain();
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
		wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
#else
		wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
#endif
	}

	/*
	 * save the original driver regulatory flags
	 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
	pHddCtx->reg.reg_flags = wiphy->regulatory_flags;
#else
	pHddCtx->reg.reg_flags = wiphy->flags;
#endif
	/*
	 * add this to avoid the warning in kernel when invoking
	 * function wiphy_apply_custom_regulatory.
	 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#else
	wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
	wiphy_apply_custom_regulatory(wiphy, regd);
#endif

	/*
	 * restore the driver regulatory flags since
	 * wiphy_apply_custom_regulatory may have
	 * changed them
	 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
	wiphy->regulatory_flags = pHddCtx->reg.reg_flags;
#else
	wiphy->flags = pHddCtx->reg.reg_flags;
#endif

#ifdef CLD_REGDB
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
	wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
#endif
#endif
	return 0;
}

static int reg_init_from_eeprom(hdd_context_t *pHddCtx, struct regulatory *reg,
				struct wiphy *wiphy)
{
	int ret_val = 0;

	if((pHddCtx->cfg_ini->overrideCountryCode[0] != '0' )&&
	   (pHddCtx->cfg_ini->overrideCountryCode[1] != '0')) {
		reg->alpha2[0] = pHddCtx->cfg_ini->overrideCountryCode[0];
		reg->alpha2[1] = pHddCtx->cfg_ini->overrideCountryCode[1];
		reg->reg_domain = COUNTRY_ERD_FLAG;
		reg->reg_domain |= regdmn_find_ctry_by_name(reg->alpha2);
	}

	ret_val = regdmn_get_country_alpha2(reg);
	if (ret_val) {
		adf_os_print(KERN_ERR "Error in getting country code\n");
		return ret_val;
	}

	reg->cc_src = COUNTRY_CODE_SET_BY_DRIVER;

	/* update default country code */
	pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[0] =
		reg->alpha2[0];
	pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[1] =
		reg->alpha2[1];

	regd_init_wiphy(pHddCtx, reg, wiphy);

	return ret_val;
}

static void vos_update_reg_info(hdd_context_t *pHddCtx)
{
    u_int32_t country_code;
    country_code = regdmn_find_ctry_by_name(pHddCtx->reg.alpha2);
    pHddCtx->reg.reg_domain = COUNTRY_ERD_FLAG;
    pHddCtx->reg.reg_domain |= country_code;
    regdmn_get_country_alpha2(&pHddCtx->reg);
    return;
}

/**------------------------------------------------------------------------
  \brief vos_nv_open() - Open NV operation
         Read NV bin file and prepare NV common structure
  \return VOS_STATUS_SUCCESS - module is initialized successfully
          otherwise  - module is not initialized
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_open(void)
{
    /* Allocate memory to global NV table */
    pnvEFSTable = (nvEFSTable_t *)vos_mem_malloc(sizeof(nvEFSTable_t));
    if ( NULL == pnvEFSTable )
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "%s : failed to allocate memory for NV", __func__);
        return VOS_STATUS_E_NOMEM;
    }

    /*Copying the NV defaults */
    vos_mem_copy(&(pnvEFSTable->halnv), &nvDefaults, sizeof(sHalNv));

    return VOS_STATUS_SUCCESS;
}

VOS_STATUS vos_nv_close(void)
{
    vos_mem_free(pnvEFSTable);
    pnvEFSTable=NULL;
    return VOS_STATUS_SUCCESS;
}

/**
 * vos_search_5g_bonded_chan_array() - get ptr to bonded channel
 * @oper_ch: operating channel number
 * @bonded_chan_ar: bonded channel array
 * @bonded_chan_ptr_ptr: bonded channel ptr ptr
 *
 * Return: eNVChannelEnabledType
 */
static eNVChannelEnabledType vos_search_5g_bonded_chan_array(
	uint32_t oper_chan, const struct bonded_chan bonded_chan_ar[],
	uint16_t array_size, const struct bonded_chan
	**bonded_chan_ptr_ptr)
{
	int i;
	uint8_t chan_num;
	const struct bonded_chan *bonded_chan_ptr = NULL;
	eNVChannelEnabledType chan_state = NV_CHANNEL_INVALID;
	eNVChannelEnabledType temp_chan_state;

	for (i = 0; i < array_size; i++) {
		if ((oper_chan >= bonded_chan_ar[i].start_ch) &&
		    (oper_chan <= bonded_chan_ar[i].end_ch)) {
			bonded_chan_ptr =  &(bonded_chan_ar[i]);
			break;
		}
	}

	if (NULL == bonded_chan_ptr)
		return chan_state;

	*bonded_chan_ptr_ptr = bonded_chan_ptr;
	chan_num =  bonded_chan_ptr->start_ch;
	while (chan_num <= bonded_chan_ptr->end_ch) {
		temp_chan_state = vos_nv_getChannelEnabledState(chan_num);
		if (temp_chan_state < chan_state)
			chan_state = temp_chan_state;
		chan_num = chan_num + 4;
	}

	return chan_state;
}

/**
 * vos_search_5g_bonded_channel() - get the 5G bonded channel state
 * @chan_num: channel number
 * @ch_width: channel width
 * @bonded_chan_ptr_ptr: bonded channel ptr ptr
 *
 * Return: channel state
 */
static eNVChannelEnabledType vos_search_5g_bonded_channel(uint32_t chan_num,
						enum phy_ch_width ch_width,
						const struct bonded_chan
						**bonded_chan_ptr_ptr)
{

	if (CH_WIDTH_80MHZ == ch_width)
		return vos_search_5g_bonded_chan_array(chan_num,
			bonded_chan_80mhz_array,
			ARRAY_SIZE(bonded_chan_80mhz_array),
			bonded_chan_ptr_ptr);
	else if (CH_WIDTH_40MHZ == ch_width)
		return vos_search_5g_bonded_chan_array(chan_num,
			bonded_chan_40mhz_array,
			ARRAY_SIZE(bonded_chan_40mhz_array),
			bonded_chan_ptr_ptr);
	else
		return vos_nv_getChannelEnabledState(chan_num);
}

/**
 * vos_get_5g_bonded_channel_state() - get the 5G bonded channel state
 * @chan_num: channel number
 * @ch_width: channel width
 *
 * Return: channel state
 */
eNVChannelEnabledType vos_get_5g_bonded_channel_state(
	uint16_t chan_num,
	enum phy_ch_width ch_width,
	const struct bonded_chan *bonded_chan_ptr)
{
	bool bw_enabled = false;
	uint32_t flags;

	if (CH_WIDTH_80MHZ < ch_width)
		return NV_CHANNEL_INVALID;

	flags = vos_nv_get_channel_flags(chan_num);

	if (CH_WIDTH_5MHZ == ch_width) {
		bw_enabled = true;
	} else if (CH_WIDTH_10MHZ == ch_width) {
		bw_enabled = !(flags &
				IEEE80211_CHAN_NO_10MHZ);
	} else if (CH_WIDTH_20MHZ == ch_width) {
		bw_enabled = !(flags &
				IEEE80211_CHAN_NO_20MHZ);
	} else if (CH_WIDTH_40MHZ == ch_width) {
		if (chan_num == bonded_chan_ptr->start_ch)
			bw_enabled =
				!(flags & IEEE80211_CHAN_NO_HT40PLUS);
		else
			bw_enabled =
				!(flags & IEEE80211_CHAN_NO_HT40MINUS);
	} else if (CH_WIDTH_80MHZ == ch_width) {
		bw_enabled = !(flags &
				IEEE80211_CHAN_NO_80MHZ);
	}

	if (bw_enabled)
		return NV_CHANNEL_ENABLE;
	else
		return NV_CHANNEL_DISABLE;
}

/**
 * vos_set_sec_chan_offset_vht80() - set sec channel offset for vht80
 * @oper_ch: operating channel
 * @center_chan: center channel for operating channel
 * @ch_params: channel parameters
 *
 * Return: void
 */
static inline void vos_set_sec_chan_offset_vht80(uint16_t oper_ch,
	uint16_t center_chan, struct ch_params_s *ch_params)
{
	if ((oper_ch + 2 ) == center_chan)
		ch_params->sec_ch_offset =
			PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW;
	else if ((oper_ch + 6 ) == center_chan)
		ch_params->sec_ch_offset =
			PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW;
	else if ((oper_ch - 2 ) == center_chan)
		ch_params->sec_ch_offset =
			PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH;
	else if ((oper_ch - 6 ) == center_chan)
		ch_params->sec_ch_offset =
			PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH;
}

/**
 * vos_set_5g_channel_params() - set the 5G bonded channel parameters
 * @oper_ch: operating channel
 * @ch_params: channel parameters
 *
 * Return: void
 */
static void vos_set_5g_channel_params(uint16_t oper_ch,
				      struct ch_params_s *ch_params)
{
	eNVChannelEnabledType chan_state = NV_CHANNEL_ENABLE;
	const struct bonded_chan *bonded_chan_ptr = NULL;
	uint16_t center_chan;

	if (CH_WIDTH_MAX <= ch_params->ch_width)
		ch_params->ch_width = CH_WIDTH_80MHZ;

	while (ch_params->ch_width < CH_WIDTH_INVALID) {
		chan_state = vos_search_5g_bonded_channel(oper_ch,
				ch_params->ch_width, &bonded_chan_ptr);
		if (((NV_CHANNEL_ENABLE != chan_state) &&
		   (NV_CHANNEL_DFS != chan_state)))
			goto next;

		chan_state = vos_get_5g_bonded_channel_state(oper_ch,
				ch_params->ch_width, bonded_chan_ptr);
		if (((NV_CHANNEL_ENABLE != chan_state) &&
		   (NV_CHANNEL_DFS != chan_state)))
			goto next;

		if (CH_WIDTH_20MHZ >= ch_params->ch_width) {
			ch_params->sec_ch_offset
				= PHY_SINGLE_CHANNEL_CENTERED;
			break;
		} else if (CH_WIDTH_40MHZ == ch_params->ch_width) {
			if (oper_ch == bonded_chan_ptr->start_ch)
				ch_params->sec_ch_offset =
					PHY_DOUBLE_CHANNEL_LOW_PRIMARY;
			else
				ch_params->sec_ch_offset =
					PHY_DOUBLE_CHANNEL_HIGH_PRIMARY;
		}

		center_chan = (bonded_chan_ptr->start_ch +
			 bonded_chan_ptr->end_ch) / 2;
		ch_params->center_freq_seg0 =
			vos_chan_to_freq(center_chan);

		if (CH_WIDTH_80MHZ == ch_params->ch_width)
			vos_set_sec_chan_offset_vht80(oper_ch,
					center_chan, ch_params);

		break;
next:
		ch_params->ch_width = next_lower_bw[ch_params->ch_width];
	}
}

/**
 * vos_get_2g_bonded_channel_state() - get the 2G bonded channel state
 * @oper_ch: operating channel
 * @ch_width: channel width
 * @sec_ch: secondary channel
 *
 * Return: channel state
 */
eNVChannelEnabledType vos_get_2g_bonded_channel_state(uint16_t oper_ch,
						  enum phy_ch_width ch_width,
						  uint16_t sec_ch)
{
	eNVChannelEnabledType chan_state;
	bool bw_enabled = false;
	uint32_t flags;
	bool ht_40_plus = false;

	if (CH_WIDTH_40MHZ < ch_width)
		return NV_CHANNEL_INVALID;

	if (CH_WIDTH_40MHZ == ch_width) {
		if ((sec_ch + 4 != oper_ch) &&
		    (oper_ch + 4 != sec_ch))
			return NV_CHANNEL_INVALID;
		ht_40_plus = (oper_ch < sec_ch)? true : false;
	}

	chan_state = vos_nv_getChannelEnabledState(oper_ch);
	if ((NV_CHANNEL_INVALID == chan_state) ||
	    (NV_CHANNEL_DISABLE == chan_state))
		return chan_state;

	flags = vos_nv_get_channel_flags(oper_ch);

	if (CH_WIDTH_5MHZ == ch_width) {
		bw_enabled = true;
	} else if (CH_WIDTH_10MHZ == ch_width) {
		bw_enabled = !(flags &
				IEEE80211_CHAN_NO_10MHZ);
	} else if (CH_WIDTH_20MHZ == ch_width) {
		bw_enabled = !(flags &
				IEEE80211_CHAN_NO_20MHZ);
	} else if (CH_WIDTH_40MHZ == ch_width) {
		if (ht_40_plus)
			bw_enabled =
				!(flags & IEEE80211_CHAN_NO_HT40PLUS);
		else
			bw_enabled =
				!(flags & IEEE80211_CHAN_NO_HT40MINUS);
	}

	if (bw_enabled)
		return chan_state;
	else
		return NV_CHANNEL_DISABLE;
}

/**
 * vos_set_2g_channel_params() - set the 2.4G bonded channel parameters
 * @oper_ch: operating channel
 * @ch_params: channel parameters
 * @sec_ch_2g: 2.4G secondary channel
 *
 * Return: void
 */
static void vos_set_2g_channel_params(uint16_t oper_ch,
		struct ch_params_s *ch_params,
		uint16_t sec_ch_2g)
{
	eNVChannelEnabledType chan_state = NV_CHANNEL_ENABLE;

	if (CH_WIDTH_MAX <= ch_params->ch_width)
		ch_params->ch_width = CH_WIDTH_40MHZ;

	while (ch_params->ch_width < CH_WIDTH_INVALID) {
		chan_state = vos_get_2g_bonded_channel_state(oper_ch,
							    ch_params->ch_width,
							    sec_ch_2g);
		if (NV_CHANNEL_ENABLE == chan_state) {
			if (CH_WIDTH_40MHZ == ch_params->ch_width) {
				if (oper_ch < sec_ch_2g)
					ch_params->sec_ch_offset =
						PHY_DOUBLE_CHANNEL_LOW_PRIMARY;
				else
					ch_params->sec_ch_offset =
						PHY_DOUBLE_CHANNEL_HIGH_PRIMARY;
				ch_params->center_freq_seg0 =
					vos_chan_to_freq(
						(oper_ch + sec_ch_2g) / 2);
			} else {
				ch_params->sec_ch_offset =
					PHY_SINGLE_CHANNEL_CENTERED;
			}
			break;
		}

		ch_params->ch_width = next_lower_bw[ch_params->ch_width];
	}
}

/**
 * vos_set_channel_params() - set the bonded channel parameters
 * @oper_ch: operating channel
 * @sec_ch_2g: 2.4G secondary channel
 * @ch_params: chanel parameters
 *
 * Return: void
 */
void vos_set_channel_params(uint16_t oper_ch, uint16_t sec_ch_2g,
	struct ch_params_s *ch_params)
{
	if (VOS_IS_CHANNEL_5GHZ(oper_ch))
		vos_set_5g_channel_params(oper_ch, ch_params);
	else if (VOS_IS_CHANNEL_24GHZ(oper_ch))
		vos_set_2g_channel_params(oper_ch, ch_params, sec_ch_2g);
}

/**------------------------------------------------------------------------
  \brief vos_nv_getSupportedCountryCode() - get the list of supported
  country codes
  The \a vos_nv_getSupportedCountryCode() encodes the list of supported
  country codes with paddings in the provided buffer
  \param pBuffer     - pointer to buffer where supported country codes
                       and paddings are encoded; this may be set to NULL
                       if user wishes to query the required buffer size to
                       get the country code list
  \param pBufferSize - this is the provided buffer size on input;
                       this is the required or consumed buffer size on output
  \return VOS_STATUS_SUCCESS - country codes are successfully encoded
          VOS_STATUS_E_NOMEM - country codes are not encoded because either
                               the buffer is NULL or buffer size is
                               sufficient
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_getSupportedCountryCode( v_BYTE_t *pBuffer, v_SIZE_t *pBufferSize,
      v_SIZE_t paddingSize )
{
   v_SIZE_t providedBufferSize = *pBufferSize;
   int i;
   // pBufferSize now points to the required buffer size
   *pBufferSize = ol_regdmn_Rdt.allCountriesCt * (VOS_COUNTRY_CODE_LEN + paddingSize );
   if ( NULL == pBuffer || providedBufferSize < *pBufferSize )
   {
      VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
            ("Insufficient memory for country code list"));
      return VOS_STATUS_E_NOMEM;
   }
   for (i = 0; i < ol_regdmn_Rdt.allCountriesCt; i++) {
      vos_mem_copy(pBuffer, ol_regdmn_Rdt.allCountries[i].isoName,
                   VOS_COUNTRY_CODE_LEN);
      pBuffer += (VOS_COUNTRY_CODE_LEN + paddingSize );
   }
   return VOS_STATUS_SUCCESS;
}

/**------------------------------------------------------------------------
  \brief vos_nv_getChannelListWithPower() - function to return the list of
          supported channels with the power limit info too.
  \param pChannels20MHz - list of 20 Mhz channels
  \param pNum20MHzChannelsFound - number of 20 Mhz channels
  \param pChannels40MHz - list of 20 Mhz channels
  \param pNum40MHzChannelsFound - number of 20 Mhz channels
  \return status of the NV read operation
  \Note: 40Mhz not currently supported
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_getChannelListWithPower(tChannelListWithPower *channels20MHz /*[NUM_LEGIT_RF_CHANNELS] */,
                                          tANI_U8 *num20MHzChannelsFound,
                                          tChannelListWithPower *channels40MHz /*[NUM_CHAN_BOND_CHANNELS] */,
                                          tANI_U8 *num40MHzChannelsFound
                                          )
{
    VOS_STATUS status = VOS_STATUS_SUCCESS;
    int i, count;

    //TODO: Dont want to use pMac here...can we instead store the curRegDomain in NV
    // or pass it as a parameter to NV from SME?

    if( channels20MHz && num20MHzChannelsFound )
    {
        count = 0;
        for( i = 0; i <= RF_CHAN_14; i++ )
        {
            if( regChannels[i].enabled )
            {
                channels20MHz[count].chanId = rfChannels[i].channelNum;
                channels20MHz[count++].pwr  = regChannels[i].pwrLimit;
            }
        }
        for( i = RF_CHAN_36; i <= RF_CHAN_184; i++ )
        {
            if( regChannels[i].enabled )
            {
                channels20MHz[count].chanId = rfChannels[i].channelNum;
                channels20MHz[count++].pwr  = regChannels[i].pwrLimit;
            }
        }
        *num20MHzChannelsFound = (tANI_U8)count;
    }

    if( channels40MHz && num40MHzChannelsFound )
    {
        count = 0;
        //center channels for 2.4 Ghz 40 MHz channels
        for( i = RF_CHAN_BOND_3; i <= RF_CHAN_BOND_11; i++ )
        {

            if( regChannels[i].enabled )
            {
                channels40MHz[count].chanId = rfChannels[i].channelNum;
                channels40MHz[count++].pwr  = regChannels[i].pwrLimit;
            }
        }
        //center channels for 5 Ghz 40 MHz channels
        for( i = RF_CHAN_BOND_38; i <= RF_CHAN_BOND_163; i++ )
        {

            if( regChannels[i].enabled )
            {
                channels40MHz[count].chanId = rfChannels[i].channelNum;
                channels40MHz[count++].pwr  = regChannels[i].pwrLimit;
            }
        }
        *num40MHzChannelsFound = (tANI_U8)count;
    }
    return (status);
}

/**------------------------------------------------------------------------
  \brief vos_nv_getDefaultRegDomain() - return the default regulatory domain
  \return default regulatory domain
  \sa
  -------------------------------------------------------------------------*/

v_REGDOMAIN_t vos_nv_getDefaultRegDomain( void )
{
    return REGDOMAIN_WORLD;
}

/**------------------------------------------------------------------------
  \brief vos_nv_getSupportedChannels() - function to return the list of
          supported channels
  \param p20MhzChannels - list of 20 Mhz channels
  \param pNum20MhzChannels - number of 20 Mhz channels
  \param p40MhzChannels - list of 40 Mhz channels
  \param pNum40MhzChannels - number of 40 Mhz channels
  \return status of the NV read operation
  \Note: 40Mhz not currently supported
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_getSupportedChannels( v_U8_t *p20MhzChannels, int *pNum20MhzChannels,
                                        v_U8_t *p40MhzChannels, int *pNum40MhzChannels)
{
    VOS_STATUS status = VOS_STATUS_E_INVAL;
    int i, count = 0;

    if( p20MhzChannels && pNum20MhzChannels )
    {
        if( *pNum20MhzChannels >= NUM_RF_CHANNELS )
        {
            for( i = 0; i <= RF_CHAN_14; i++ )
            {
                p20MhzChannels[count++] = rfChannels[i].channelNum;
            }
            for( i = RF_CHAN_36; i <= RF_CHAN_184; i++ )
            {
                p20MhzChannels[count++] = rfChannels[i].channelNum;
            }
            status = VOS_STATUS_SUCCESS;
        }
        *pNum20MhzChannels = count;
    }

    return (status);
}

/**------------------------------------------------------------------------
  \brief vos_nv_readDefaultCountryTable() - return the default Country table
  \param table data - a union to return the default country table data in.
  \return status of the NV read operation
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_readDefaultCountryTable( uNvTables *tableData )
{

   VOS_STATUS status = VOS_STATUS_SUCCESS;
   vos_mem_copy(&tableData->defaultCountryTable,
                &pnvEFSTable->halnv.tables.defaultCountryTable,
                sizeof(sDefaultCountry));
   pr_info("DefaultCountry is %c%c\n",
            tableData->defaultCountryTable.countryCode[0],
            tableData->defaultCountryTable.countryCode[1]);
   return status;
}

/**
 * vos_nv_skip_dsrc_dfs_2g() - skip dsrc, dfs and 2g band channels
 * @rf_channel: input channel enum to know, whether to skip or add the channel
 * @skip_group: group to skip
 *
 * Return: true or false
 */
uint8_t vos_nv_skip_dsrc_dfs_2g(uint32_t rf_channel, int32_t skip_group)
{
	uint32_t channel_loop;
	eRfChannels channel_enum = INVALID_RF_CHANNEL;
	uint8_t ret = false;
	int32_t start_channel, end_channel;

	switch (skip_group){
	case NV_CHANNEL_SKIP_DSRC:
		start_channel = RF_CHAN_1;
		end_channel = RF_CHAN_165;
		break;
	case NV_CHANNEL_SKIP_2G:
		start_channel = RF_CHAN_36;
		end_channel = RF_CHAN_165;
		break;
	default:
		start_channel = RF_CHAN_1;
		end_channel = RF_CHAN_184;
		break;
	}

	for (channel_loop = start_channel;
	     channel_loop <= end_channel; channel_loop++) {
		if (rfChannels[channel_loop].channelNum == rf_channel) {
			channel_enum = (eRfChannels)channel_loop;
			break;
		}
	}

	if (INVALID_RF_CHANNEL == channel_enum) {
		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
			  FL("Invalid channel %d"), rf_channel);
		ret = true;
		goto exit_ok;
	}

	if (NV_CHANNEL_DFS == regChannels[channel_enum].enabled)
		ret = true;
exit_ok:
	return ret;
}

/**------------------------------------------------------------------------
  \brief vos_nv_getChannelEnabledState -
  \param rfChannel  - input channel enum to know evabled state
  \return eNVChannelEnabledType enabled state for channel
             * enabled
             * disabled
             * DFS
  \sa
  -------------------------------------------------------------------------*/
eNVChannelEnabledType vos_nv_getChannelEnabledState
(
   v_U32_t     rfChannel
)
{
   v_U32_t       channelLoop;
   eRfChannels   channelEnum = INVALID_RF_CHANNEL;

   for(channelLoop = 0; channelLoop <= RF_CHAN_184; channelLoop++)
   {
      if(rfChannels[channelLoop].channelNum == rfChannel)
      {
         channelEnum = (eRfChannels)channelLoop;
         break;
      }
   }

   if(INVALID_RF_CHANNEL == channelEnum)
   {
      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                "vos_nv_getChannelEnabledState, invalid channel %d", rfChannel);
      return NV_CHANNEL_INVALID;
   }

   return regChannels[channelEnum].enabled;
}

/**
 * vos_nv_get_channel_flags: Get channel flags
 * @rf_channel: Channel number.
 * This function is called to know associated flags with channel
 *
 * Return: updated Wiphy struct
 */
uint32_t vos_nv_get_channel_flags
(
   uint32_t  rf_channel
)
{
	uint32_t       channel_loop;
	eRfChannels   channel_enum = INVALID_RF_CHANNEL;

	for(channel_loop = 0; channel_loop <= RF_CHAN_184; channel_loop++) {
		if(rfChannels[channel_loop].channelNum == rf_channel) {
			channel_enum = (eRfChannels)channel_loop;
			break;
		}
	}

	if (INVALID_RF_CHANNEL == channel_enum) {
		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
			"vos_nv_get_channel_flags, invalid channel %d",
			rf_channel);
		return NV_CHANNEL_INVALID;
	}
	return regChannels[channel_enum].flags;
}

/******************************************************************
 Add CRDA regulatory support
*******************************************************************/

/**------------------------------------------------------------------------
  \brief vos_nv_setRegDomain -
  \param clientCtxt  - Client Context, Not used for PRIMA
              regId  - Regulatory Domain ID
              sendRegHint - send hint to nl80211
  \return status set REG domain operation
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_setRegDomain(void * clientCtxt, v_REGDOMAIN_t regId,
                                                v_BOOL_t sendRegHint)
{

    if (regId >= REGDOMAIN_COUNT)
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "VOS set reg domain, invalid REG domain ID %d", regId);
        return VOS_STATUS_E_INVAL;
    }

    /* Set correct channel information based on REG Domain */
    regChannels = pnvEFSTable->halnv.tables.regDomains[regId].channels;

    return VOS_STATUS_SUCCESS;
}

/* vos_nv_set_dfs_region() - set the dfs_region
 *
 * @dfs_region: the dfs_region to set
 *
 * Return: VOS_STATUS_SUCCESS if dfs_region set correctly
 *         VOS_STATUS_E_EXISTS if vos_context not found
 */
VOS_STATUS vos_nv_set_dfs_region(uint8_t dfs_region)
{
	v_CONTEXT_t vos_ctx_ptr = NULL;
	hdd_context_t *hdd_ctx_ptr= NULL;

	vos_ctx_ptr = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

	if (NULL == vos_ctx_ptr)
		return VOS_STATUS_E_EXISTS;

	hdd_ctx_ptr = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx_ptr);

	if (NULL == hdd_ctx_ptr)
		return VOS_STATUS_E_EXISTS;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)

	hdd_ctx_ptr->reg.dfs_region = dfs_region;

#else

	/* remap the ctl code to dfs region code */
	switch(hdd_ctx_ptr->reg.ctl_5g) {
	case FCC:
		hdd_ctx_ptr->reg.dfs_region = DFS_FCC_DOMAIN;
		break;
	case ETSI:
		hdd_ctx_ptr->reg.dfs_region = DFS_ETSI_DOMAIN;
		break;
	case MKK:
		hdd_ctx_ptr->reg.dfs_region = DFS_MKK4_DOMAIN;
		break;
	default:
		/* set default dfs_region to FCC */
		hdd_ctx_ptr->reg.dfs_region = DFS_FCC_DOMAIN;
		break;
	}
#endif
	return VOS_STATUS_SUCCESS;
}

/* vos_nv_get_dfs_region() - get the dfs_region
 *
 * @dfs_region: the dfs_region to return
 *
 * Return: VOS_STATUS_SUCCESS if dfs_region set correctly
 *         VOS_STATUS_E_EXISTS if vos_context not found
 */
VOS_STATUS vos_nv_get_dfs_region(uint8_t *dfs_region)
{
	v_CONTEXT_t vos_ctx_ptr = NULL;
	hdd_context_t *hdd_ctx_ptr = NULL;

	vos_ctx_ptr = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

	if (NULL == vos_ctx_ptr)
		return VOS_STATUS_E_EXISTS;

	hdd_ctx_ptr = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx_ptr);

	if (NULL == hdd_ctx_ptr)
		return VOS_STATUS_E_EXISTS;

	*dfs_region = hdd_ctx_ptr->reg.dfs_region;

	return VOS_STATUS_SUCCESS;
}

/**------------------------------------------------------------------------
  \brief vos_nv_getRegDomainFromCountryCode() - get the regulatory domain of
  a country given its country code
  The \a vos_nv_getRegDomainFromCountryCode() returns the regulatory domain of
  a country given its country code.  This is done from reading a cached
  copy of the binary file.
  \param pRegDomain  - pointer to regulatory domain
  \param countryCode - country code
  \param source      - source of the country code
  \return VOS_STATUS_SUCCESS - regulatory domain is found for the given country
          VOS_STATUS_E_FAULT - invalid pointer error
          VOS_STATUS_E_EMPTY - country code table is empty
          VOS_STATUS_E_EXISTS - given country code does not exist in table
  \sa
  -------------------------------------------------------------------------*/
VOS_STATUS vos_nv_getRegDomainFromCountryCode( v_REGDOMAIN_t *pRegDomain,
      const v_COUNTRYCODE_t country_code, v_CountryInfoSource_t source)
{

    v_CONTEXT_t pVosContext = NULL;
    hdd_context_t *pHddCtx = NULL;
    struct wiphy *wiphy = NULL;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) || !defined(CLD_REGDB)
    int wait_result;
#endif
#ifdef CLD_REGDB
    struct regulatory_request request;
#endif

    /* sanity checks */
    if (NULL == pRegDomain)
    {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("Invalid reg domain pointer") );
        return VOS_STATUS_E_FAULT;
    }

    *pRegDomain = REGDOMAIN_COUNT;

    if (NULL == country_code)
    {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("Country code array is NULL"));
        return VOS_STATUS_E_FAULT;
    }

    pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

    if (NULL != pVosContext)
        pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
    else
        return VOS_STATUS_E_EXISTS;

    if (NULL == pHddCtx)
    {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("Invalid pHddCtx pointer") );
        return VOS_STATUS_E_FAULT;
    }

    if (pHddCtx->isLogpInProgress) {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   (" SSR in progress, return") );
        *pRegDomain = temp_reg_domain;
         return VOS_STATUS_SUCCESS;
    }

    wiphy = pHddCtx->wiphy;

    if (false == wiphy->registered) {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("wiphy is not yet registered with the kernel") );
        return VOS_STATUS_E_FAULT;
    }

    /* lookup the country in the local database */
    regdmn_map_country_to_vos_regdmn(country_code, &temp_reg_domain);

    if (REGDOMAIN_COUNT == temp_reg_domain) {

        /*
         * The country was not found in the driver database
         * so we will return the REGDOMAIN_WORLD to SME/CSR
         */
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                 ("Country %c%c does not map to any Regulatory domain"),
                  country_code[0], country_code[1]);

        temp_reg_domain = REGDOMAIN_WORLD;
    }

    if (COUNTRY_QUERY == source) {
        *pRegDomain = temp_reg_domain;
         return VOS_STATUS_SUCCESS;
    }

    VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                   ("regdomain request"));

    VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_WARN,
               (" get country information from kernel db"));

    if ((COUNTRY_INIT == source) && (VOS_FALSE == init_by_reg_core)) {
        init_by_driver = VOS_TRUE;

        if (('0' != country_code[0]) || ('0' != country_code[1])) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) || !defined(CLD_REGDB)
            INIT_COMPLETION(pHddCtx->reg_init);
            regulatory_hint(wiphy, country_code);
            wait_for_completion_timeout(&pHddCtx->reg_init,
                                        msecs_to_jiffies(REG_WAIT_TIME));
#endif
#ifdef CLD_REGDB
	    request.alpha2[0] = pHddCtx->reg.alpha2[0];
	    request.alpha2[1] = pHddCtx->reg.alpha2[1];
	    request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
	    request.dfs_region = 0;
	    wiphy->reg_notifier(wiphy, &request);
#endif
        }

    } else if (COUNTRY_IE == source || COUNTRY_USER == source) {
        if (COUNTRY_USER == source)
            vos_set_cc_source(CNSS_SOURCE_USER);
        else
            vos_set_cc_source(CNSS_SOURCE_11D);

#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) || !defined(CLD_REGDB)
        INIT_COMPLETION(pHddCtx->reg_init);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
        regulatory_hint_user(country_code, NL80211_USER_REG_HINT_USER);
#else
        regulatory_hint_user(country_code);
#endif
        wait_result = wait_for_completion_interruptible_timeout(
                               &pHddCtx->reg_init,
                               msecs_to_jiffies(REG_WAIT_TIME));
        /*
         * if the country information does not exist with the kernel,
         * then the driver callback would not be called
         */

        if (wait_result >= 0)
        {
           VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                       "runtime country code : %c%c is found in kernel db",
                        country_code[0], country_code[1]);
           *pRegDomain = temp_reg_domain;
        }
        else
        {
            VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_WARN,
                       "runtime country code : %c%c is not found"
                       " in kernel db",
                        country_code[0], country_code[1]);

            return VOS_STATUS_E_EXISTS;
        }
#endif
    }

    *pRegDomain = temp_reg_domain;
    return VOS_STATUS_SUCCESS;
}

/* vos_is_fcc_regdomian() - is the regdomain FCC
 *
 * Return: true if FCC regdomain
 *         false otherwise
 */
bool vos_is_fcc_regdomain(void)
{
	v_CONTEXT_t pVosContext = NULL;
	hdd_context_t *pHddCtx = NULL;
	v_REGDOMAIN_t domainId;

	pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

	if (!pVosContext)
		return false;
	pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
	if (!pHddCtx) {
		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
				("Invalid pHddCtx pointer"));
		return false;
	}
	vos_nv_getRegDomainFromCountryCode(&domainId,
			pHddCtx->reg.alpha2, COUNTRY_QUERY);
	if (REGDOMAIN_FCC == domainId)
		return true;
	return false;
}

#ifdef FEATURE_STATICALLY_ADD_11P_CHANNELS
#define DEFAULT_11P_POWER (30)
#endif

/* vos_is_dsrc_channel() - is the channel DSRC
 *
 * @center_freq: center freq of the channel
 *
 * Return: true if dsrc channel
 *         false otherwise
 */
bool vos_is_dsrc_channel(uint16_t center_freq)
{
    switch (center_freq) {
    case 5852:
    case 5860:
    case 5870:
    case 5880:
    case 5890:
    case 5900:
    case 5910:
    case 5920:
    case 5875:
    case 5905:
        return 1;
    }
    return 0;
}

/**
 * vos_is_channel_support_sub20() - check channel
 * support sub20 channel width
 * @oper_ch: operating channel
 * @ch_width: channel width
 * @sec_ch: secondary channel
 *
 * Return: true or false
 */
bool vos_is_channel_support_sub20(uint16_t operation_channel,
				  enum phy_ch_width channel_width,
				  uint16_t secondary_channel)
{
	eNVChannelEnabledType channel_state;

	if (VOS_IS_CHANNEL_5GHZ(operation_channel)) {
		const struct bonded_chan *bonded_chan_ptr = NULL;

		channel_state =
		    vos_search_5g_bonded_channel(operation_channel,
						 channel_width,
						 &bonded_chan_ptr);
		if (NV_CHANNEL_DISABLE == channel_state)
			return false;

		channel_state =
		    vos_get_5g_bonded_channel_state(operation_channel,
						    channel_width,
						    bonded_chan_ptr);
		if (NV_CHANNEL_DISABLE == channel_state)
			return false;

	} else if (VOS_IS_CHANNEL_24GHZ(operation_channel)) {
		channel_state =
		    vos_get_2g_bonded_channel_state(operation_channel,
						    channel_width,
						    secondary_channel);
		if (NV_CHANNEL_DISABLE == channel_state)
			return false;
	}

	return true;
}

/**
 * vos_phy_channel_width_to_sub20: convert phy channel width
 * to sub20 channel width
 * @channel_width:  phy channel width
 * Return: sub20 channel width
 */
uint8_t vos_phy_channel_width_to_sub20(enum phy_ch_width channel_width)
{
	if (channel_width == CH_WIDTH_5MHZ)
		return SUB20_MODE_5MHZ;
	else if (channel_width == CH_WIDTH_10MHZ)
		return SUB20_MODE_10MHZ;
	else
		return SUB20_MODE_NONE;
}

/**
 * vos_update_band: Update the band
 * @eBand: Band value
 *
 * This function is called from the supplicant through a
 * private ioctl to change the band value.
 *
 * Return: updated Wiphy struct
 */
int vos_update_band(v_U8_t  band_capability)
{
	v_CONTEXT_t vos_ctx = NULL;
	hdd_context_t *hdd_ctx = NULL;
	struct wiphy *wiphy = NULL;
	int i, j;
	eNVChannelEnabledType channel_enabled_state;
	uint32_t flags;
	ENTER();
	vos_ctx = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

	if (NULL != vos_ctx)
		hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
	else
		return VOS_STATUS_E_EXISTS;

	if (NULL == hdd_ctx) {
		VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
				("Invalid hdd_ctx pointer") );
		return VOS_STATUS_E_FAULT;
	}

	if (hdd_ctx->isLogpInProgress) {
		VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
				(" SSR in progress, return") );
		return VOS_STATUS_SUCCESS;
	}

	wiphy = hdd_ctx->wiphy;

	if (false == wiphy->registered) {
		VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
			("wiphy is not yet registered with the kernel"));
		return VOS_STATUS_E_FAULT;
	}

	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
		if (NULL == wiphy->bands[i])
			continue;

		for (j = 0; j < wiphy->bands[i]->n_channels; j++) {
			struct ieee80211_supported_band *band = wiphy->bands[i];

			flags = vos_nv_get_channel_flags(
					band->channels[j].hw_value);
			channel_enabled_state =
				vos_nv_getChannelEnabledState(
						band->channels[j].hw_value);
			/* 5G only */
			if (IEEE80211_BAND_2GHZ == i &&
					eCSR_BAND_5G == band_capability) {
#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY
				/* Enable Social channels for P2P */
				if (WLAN_HDD_IS_SOCIAL_CHANNEL(
					band->channels[j].center_freq) &&
					NV_CHANNEL_ENABLE ==
					channel_enabled_state)
					band->channels[j].flags &=
						~IEEE80211_CHAN_DISABLED;
				else
#endif
					band->channels[j].flags |=
						IEEE80211_CHAN_DISABLED;
				continue;
			} else if (IEEE80211_BAND_5GHZ == i &&
					eCSR_BAND_24 == band_capability) {
				/* 2.4G only */
				band->channels[j].flags |=
					IEEE80211_CHAN_DISABLED;
				continue;
			}
			if (NV_CHANNEL_DISABLE != channel_enabled_state)
				band->channels[j].flags = flags;

		}
	}
	return 0;
}

#ifdef CLD_REGDB
static const struct ieee80211_regdomain *
vos_search_regd(const char* alpha2)
{
	const struct ieee80211_regdomain *regdomain;
	uint32_t i;

	for (i = 0; i < reg_regdb_size; i++) {
		regdomain = reg_regdb[i];
		if (vos_mem_compare(alpha2,
				    regdomain->alpha2,
				    2))
			return regdomain;
	}

	return NULL;
}

#define VOS_ONE_GHZ_IN_KHZ 1000000
static bool
vos_freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
		      uint32_t freq)
{
	uint32_t limit;

	limit = freq > 45 * VOS_ONE_GHZ_IN_KHZ ?
			20 * VOS_ONE_GHZ_IN_KHZ :
			2 * VOS_ONE_GHZ_IN_KHZ;

	if ((abs(freq - freq_range->start_freq_khz) <= limit) ||
	    (abs(freq - freq_range->end_freq_khz) <= limit))
		return true;

	return false;
}

static bool
vos_reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
		    uint32_t center_freq,
		    uint32_t bw)
{
	uint32_t freq_start;
	uint32_t freq_end;

	freq_start = center_freq - (bw / 2);
	freq_end = center_freq + (bw / 2);

	if ((freq_start >= freq_range->start_freq_khz) &&
	    (freq_end <= freq_range->end_freq_khz))
		return true;

	return false;
}

static const struct ieee80211_reg_rule *
vos_freq_reg_info(struct wiphy *wiphy,
                  uint32_t center_freq,
                  const struct ieee80211_regdomain *regd)
{
	uint32_t i;
	const struct ieee80211_reg_rule *reg_rule;
	const struct ieee80211_freq_range *freq_range;
	bool freq_in_band;
	bool bw_fit;

	if (!regd)
		return NULL;

	for (i = 0; i < regd->n_reg_rules; i++) {
		reg_rule = &regd->reg_rules[i];
		freq_range = &reg_rule->freq_range;

		freq_in_band = vos_freq_in_rule_band(freq_range,
						     center_freq);
		bw_fit = vos_reg_does_bw_fit(freq_range,
					     center_freq,
					     MHZ_TO_KHZ(20));

		if (freq_in_band && bw_fit)
			return reg_rule;
	}

	return ERR_PTR(-EINVAL);
}

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
static struct ieee80211_regdomain *
vos_copy_regd(const struct ieee80211_regdomain *regd)
{
	struct ieee80211_regdomain *regd_dup;
	uint32_t regd_dup_size;
	uint32_t i;

	regd_dup_size = sizeof(struct ieee80211_regdomain) +
			regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
	regd_dup = vos_mem_malloc(regd_dup_size);
	if (!regd_dup)
		return ERR_PTR(-ENOMEM);
	vos_mem_zero(regd_dup, regd_dup_size);

	vos_mem_copy(regd_dup, regd, sizeof(struct ieee80211_regdomain));
	for (i = 0; i < regd->n_reg_rules; i++)
		vos_mem_copy(&regd_dup->reg_rules[i],
			     &regd->reg_rules[i],
			     sizeof(struct ieee80211_reg_rule));

	return regd_dup;
}
#endif
#endif
/* create_linux_regulatory_entry to populate internal structures from wiphy */
static int create_linux_regulatory_entry(struct wiphy *wiphy,
                                         v_U8_t nBandCapability,
                                         bool reset)
{
    int i, j, m;
    int k = 0, n = 0;
    v_CONTEXT_t pVosContext = NULL;
    hdd_context_t *pHddCtx = NULL;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)) && !defined(WITH_BACKPORTS)
	 int err;
#endif
    const struct ieee80211_reg_rule *reg_rule;
#ifdef CLD_REGDB
    const struct ieee80211_regdomain *regd;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && defined(CLD_REGDB)
    struct ieee80211_regdomain *regd_dup;
    bool rtnl_locked = false;
    int ret;
#endif
#endif
    pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

    if (NULL != pVosContext)
    {
        pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
        if (NULL == pHddCtx)
        {
           VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                       ("Invalid pHddCtx pointer") );
           return -1;
        }
        else
        {
           pHddCtx->isVHT80Allowed = 0;
        }
    }
    else
    {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  ("Invalid pVosContext pointer") );
       return -1;
    }

    /* 20MHz channels */
    if (nBandCapability == eCSR_BAND_24)
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                  "BandCapability is set to 2G only");

    if (pnvEFSTable == NULL)
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "error: pnvEFSTable is NULL, probably not parsed nv.bin yet");
        return -1;
    }
    vos_mem_zero(pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels,
		 NUM_RF_CHANNELS * sizeof(sRegulatoryChannel));

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && defined(CLD_REGDB)
    wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
    regd = vos_search_regd(pHddCtx->reg.alpha2);
    if (!regd) {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "unknown alpha2 %c%c",
                  pHddCtx->reg.alpha2[0], pHddCtx->reg.alpha2[1]);
        return -1;
    }

    regd_dup = vos_copy_regd(regd);
    if (!rtnl_is_locked()) {
        rtnl_lock();
        rtnl_locked = true;
    }
    ret = regulatory_set_wiphy_regd_sync_rtnl(wiphy, regd_dup);
    if (ret)
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "regulatory set wiphy regd err:%d", ret);
    if (rtnl_locked)
        rtnl_unlock();
    vos_mem_free(regd_dup);
#endif

    for (i = 0, m = 0; i<IEEE80211_NUM_BANDS; i++)
    {
        if (wiphy->bands[i] == NULL)
            continue;

        /* internal channels[] is one continous array for both 2G and 5G bands
           m is internal starting channel index for each band */

        if (i == 0)
            m = 0;
        else
            m = wiphy->bands[i-1]->n_channels + m;

        for (j = 0; j < wiphy->bands[i]->n_channels; j++)
        {
            /* k = (m + j) is internal current channel index for 20MHz channel
              n is internal channel index for corresponding 40MHz channel */

            k = m + j;

            /* If the regulatory rules for a country do not explicilty
             * require a passive scan on a frequency, lift the passive
             * scan restriction
             * When getting the regulatory rule, specify the smallest bandwidth.
             * That's 5 MHz. A larger bandwidth may not fit into the frequency range. */

            if ((!reset) &&
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
                (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) {
#else
                (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) {
#endif

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
#ifdef CLD_REGDB
                wiphy->bands[i]->channels[j].flags |= IEEE80211_CHAN_DISABLED;
                regd = vos_search_regd(pHddCtx->reg.alpha2);
                reg_rule = vos_freq_reg_info(wiphy,
                                             MHZ_TO_KHZ(wiphy->bands[i]->
                                             channels[j].center_freq),
                                             regd);
#else
                reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(wiphy->bands[i]->
                                         channels[j].center_freq));
#endif
#else
                err = freq_reg_info(wiphy, MHZ_TO_KHZ(wiphy->bands[i]->
                                    channels[j].center_freq),
                                    0, &reg_rule);
#endif

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
                if (!IS_ERR(reg_rule)) {
#else
                if (0 == err) {
#endif
                    wiphy->bands[i]->channels[j].flags &= ~IEEE80211_CHAN_DISABLED;

                    if (!(reg_rule->flags & NL80211_RRF_DFS))
                    {
                        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                                  "%s: Remove passive scan restriction for %u",
                                  __func__, wiphy->bands[i]->channels[j].center_freq);
                        wiphy->bands[i]->channels[j].flags &= ~IEEE80211_CHAN_RADAR;
                    }

                    if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
                    {
                        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                                  "%s: Remove passive scan restriction for %u",
                                  __func__, wiphy->bands[i]->channels[j].center_freq);
                        wiphy->bands[i]->channels[j].flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
                    }

                    if (!(reg_rule->flags & NL80211_RRF_NO_IBSS))
                    {
                        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                                  "%s: Remove no ibss restriction for %u",
                                  __func__, wiphy->bands[i]->channels[j].center_freq);
                        wiphy->bands[i]->channels[j].flags &= ~IEEE80211_CHAN_NO_IBSS;
                    }

                    wiphy->bands[i]->channels[j].max_power =
                        (int) MBM_TO_DBM(reg_rule->power_rule.max_eirp);
                }
            }

#ifdef FEATURE_STATICALLY_ADD_11P_CHANNELS
            if (vos_is_dsrc_channel(wiphy->bands[i]->channels[j].center_freq))
            {
                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                    channels[k].enabled = NV_CHANNEL_ENABLE;

                /* max_power is in dBm */
                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[k].pwrLimit =
                    DEFAULT_11P_POWER;
            }
            else
#endif
            if (wiphy->bands[i]->channels[j].flags & IEEE80211_CHAN_DISABLED)
            {
                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[k].enabled =
                    NV_CHANNEL_DISABLE;
                n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                        chan_to_ht_40_index[k].ht_40_plus_index;
                if (n != INVALID_RF_CHANNEL)
                    pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[n].enabled =
                        NV_CHANNEL_DISABLE;

                n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                       chan_to_ht_40_index[k].ht_40_minus_index;
                if (n != INVALID_RF_CHANNEL)
                    pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[n].enabled =
                        NV_CHANNEL_DISABLE;
            } else if ((wiphy->bands[i]->channels[j].flags &
                     (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN)) ||
                      ((pHddCtx->cfg_ini->indoor_channel_support == FALSE) &&
                      (wiphy->bands[i]->channels[j].flags &
                       IEEE80211_CHAN_INDOOR_ONLY))) {
                /* nv cannot distinguish between DFS and passive channels */

                if ((wiphy->bands[i]->channels[j].flags &
                    IEEE80211_CHAN_INDOOR_ONLY) &&
                    (FALSE ==
                    pHddCtx->cfg_ini->indoor_channel_support))
                    wiphy->bands[i]->channels[j].flags |=
                        IEEE80211_CHAN_PASSIVE_SCAN;

                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[k].enabled =
                    NV_CHANNEL_DFS;

                /* max_power is in mBm = 100 * dBm */
                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels[k].pwrLimit =
                    (tANI_S8) ((wiphy->bands[i]->channels[j].max_power));

                /* Disable the center channel if neither HT40+ nor HT40- is allowed
                 */
                if ((wiphy->bands[i]->channels[j].flags &
                        IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40)
                {
                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                        chan_to_ht_40_index[k].ht_40_plus_index;
                    if (n != INVALID_RF_CHANNEL)
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DISABLE;

                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                       chan_to_ht_40_index[k].ht_40_minus_index;
                    if (n != INVALID_RF_CHANNEL)
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DISABLE;
                } else {
                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                        chan_to_ht_40_index[k].ht_40_plus_index;
                    if (!(wiphy->bands[i]->channels[j].flags &
                            IEEE80211_CHAN_NO_HT40PLUS) &&
                            (n != INVALID_RF_CHANNEL)) {
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DFS;
                        /* 40MHz channel power is half of 20MHz (-3dB) ?? */
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].pwrLimit = (tANI_S8)
                               (((wiphy->bands[i]->channels[j].max_power)) - 3);
                    }

                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                       chan_to_ht_40_index[k].ht_40_minus_index;
                    if (!(wiphy->bands[i]->channels[j].flags &
                            IEEE80211_CHAN_NO_HT40MINUS) &&
                            (n != INVALID_RF_CHANNEL)) {
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DFS;
                        /* 40MHz channel power is half of 20MHz (-3dB) ?? */
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].pwrLimit = (tANI_S8)
                               (((wiphy->bands[i]->channels[j].max_power)) - 3);
                    }

                }

                if ((wiphy->bands[i]->channels[j].flags & IEEE80211_CHAN_NO_80MHZ) == 0)
                {
                   if (NULL == pHddCtx)
                   {
                      VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                                  ("Invalid pHddCtx pointer") );
                   }
                   else
                   {
                      pHddCtx->isVHT80Allowed = 1;
                   }
                }
            } else {
                /* there are 14 channel in hdd_channels_2_4_GHZ,
                 * there are 24/25 hdd_channels_5_GHZ,
                 * there are 2 channel in hdd_etsi_srd_chan
                 * the last element of wiphy->bands[i]->channels[j]
                 * is channel 173, and the index is 39/40.*/
                if((!pHddCtx->cfg_ini->dot11p_mode) && (k > RF_CHAN_169)) {
                   pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                       channels[RF_CHAN_173].enabled = NV_CHANNEL_ENABLE;
                   pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                       channels[RF_CHAN_173].pwrLimit =
                       (tANI_S8) ((wiphy->bands[i]->channels[j].max_power));
                } else {
                    /* Enable is only last flag we support */
                    pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                        channels[k].enabled = NV_CHANNEL_ENABLE;

                    /* max_power is in dBm */
                    pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                        channels[k].pwrLimit =
                        (tANI_S8) ((wiphy->bands[i]->channels[j].max_power));
                }

                /* Disable the center channel if neither HT40+ nor HT40- is allowed
                 */
                if ((wiphy->bands[i]->channels[j].flags &
                             IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40)
                {
                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                        chan_to_ht_40_index[k].ht_40_plus_index;
                    if (n != INVALID_RF_CHANNEL)
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DISABLE;

                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                       chan_to_ht_40_index[k].ht_40_minus_index;
                    if (n != INVALID_RF_CHANNEL)
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_DISABLE;
                } else {
                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                        chan_to_ht_40_index[k].ht_40_plus_index;
                    if (!(wiphy->bands[i]->channels[j].flags &
                            IEEE80211_CHAN_NO_HT40PLUS) &&
                            (n != INVALID_RF_CHANNEL)) {
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_ENABLE;
                        /* 40MHz channel power is half of 20MHz (-3dB) ?? */
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].pwrLimit = (tANI_S8)
                               (((wiphy->bands[i]->channels[j].max_power)) - 3);
                    }

                    n = (k > RF_CHAN_165)? INVALID_RF_CHANNEL :
                                       chan_to_ht_40_index[k].ht_40_minus_index;
                    if (!(wiphy->bands[i]->channels[j].flags &
                            IEEE80211_CHAN_NO_HT40MINUS) &&
                            (n != INVALID_RF_CHANNEL)) {
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].enabled = NV_CHANNEL_ENABLE;
                        /* 40MHz channel power is half of 20MHz (-3dB) ?? */
                        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                            channels[n].pwrLimit = (tANI_S8)
                               (((wiphy->bands[i]->channels[j].max_power)) - 3);
                    }
                }

                if ((wiphy->bands[i]->channels[j].flags & IEEE80211_CHAN_NO_80MHZ) == 0)
                {
                   if (NULL == pHddCtx)
                   {
                      VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                                  ("Invalid pHddCtx pointer") );
                   }
                   else
                   {
                      pHddCtx->isVHT80Allowed = 1;
                   }
                }

            }
            /* Copy wiphy flags in nv table */
            if (n != INVALID_RF_CHANNEL)
                pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                    channels[n].flags = wiphy->bands[i]->channels[j].flags;
            pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
                channels[k].flags = wiphy->bands[i]->channels[j].flags;
        }
    }

#ifdef FEATURE_WLAN_CH144
     /* Disable  RF_CHAN_144 entry if FW does not support channel 144. */
     if (pHddCtx &&
        (0 == (pHddCtx->reg.eeprom_rd_ext & (1 << WHAL_REG_EXT_FCC_CH_144)))) {
        pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].
           channels[RF_CHAN_144].enabled = NV_CHANNEL_DISABLE;
     }
#endif

    if (k == 0)
       return -1;

    vos_update_band(nBandCapability);
    return 0;
}

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) && !defined(WITH_BACKPORTS)
/* restore_custom_reg_settings() - restore custom reg settings
 *
 * @wiphy: wiphy structure
 *
 * Return: void
 */
static void restore_custom_reg_settings(struct wiphy *wiphy)
{

	struct ieee80211_supported_band *sband;
	enum ieee80211_band band;
	struct ieee80211_channel *chan;
	int i;

	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
		sband = wiphy->bands[band];
		if (!sband)
			continue;
		for (i = 0; i < sband->n_channels; i++) {
			chan = &sband->channels[i];
			chan->flags = chan->orig_flags;
			chan->max_antenna_gain = chan->orig_mag;
			chan->max_power = chan->orig_mpwr;
		}
	}
}
#endif

static void hdd_debug_cc_timer_expired_handler(void *arg)
{
	hdd_context_t *pHddCtx;
	VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
		  ("%s ENTER "), __func__);

	if (!arg)
		return;
	pHddCtx = (hdd_context_t *)arg;
	vos_timer_destroy(&(pHddCtx->reg.reg_set_timer));
	regdmn_set_regval(&pHddCtx->reg);
}

/*
 * Function: wlan_hdd_linux_reg_notifier
 * This function is called from cfg80211 core to provide regulatory settings
 * after new country is requested or intersected (init, user input or 11d)
 * This function is used to create a CRDA regulatory settings entry into internal
 * regulatory setting table.
 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
void __wlan_hdd_linux_reg_notifier(struct wiphy *wiphy,
                struct regulatory_request *request)
#else
int __wlan_hdd_linux_reg_notifier(struct wiphy *wiphy,
                struct regulatory_request *request)
#endif
{
    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
    eCsrBand nBandCapability = eCSR_BAND_ALL;
    v_COUNTRYCODE_t country_code;
    v_BOOL_t isVHT80Allowed;
    bool reset = false;
    VOS_TIMER_STATE timer_status;

    VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
              FL("country: %c%c, initiator %d, dfs_region: %d"),
              request->alpha2[0],
              request->alpha2[1],
              request->initiator,
              request->dfs_region);

    if (TRUE == isWDresetInProgress())
    {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("SSR is in progress") );
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
       return;
#else
       return 0;
#endif
    }

    if (NULL == pHddCtx)
    {
       VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("Invalid pHddCtx pointer") );
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
       return;
#else
       return 0;
#endif
    }

    if (pHddCtx->isUnloadInProgress ||
        pHddCtx->isLogpInProgress)
    {
        VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "%s: Unloading or SSR in Progress, Ignore!!!", __func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
            return;
#else
            return 0;
#endif
    }

    if (pHddCtx->isWiphySuspended == TRUE) {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                  "system/cfg80211 is already suspend");
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
        return;
#else
        return 0;
#endif
    }

    sme_GetFreqBand(pHddCtx->hHal, &nBandCapability);

    /* first check if this callback is in response to the driver callback */

    switch (request->initiator)
    {
    case NL80211_REGDOM_SET_BY_DRIVER:
    case NL80211_REGDOM_SET_BY_CORE:
    case NL80211_REGDOM_SET_BY_USER:

        if ((VOS_FALSE == init_by_driver) &&
            (VOS_FALSE == init_by_reg_core)) {

            if (NL80211_REGDOM_SET_BY_CORE == request->initiator) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
                return;
#else
                return 0;
#endif
            }
            init_by_reg_core = VOS_TRUE;
        }

        if ((NL80211_REGDOM_SET_BY_DRIVER == request->initiator) &&
            (VOS_TRUE == init_by_driver)) {

            /*
             * restore the driver regulatory flags since
             * regulatory_hint may have
             * changed them
             */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
            wiphy->regulatory_flags = pHddCtx->reg.reg_flags;
#else
            wiphy->flags = pHddCtx->reg.reg_flags;
#endif
        }
        if (NL80211_REGDOM_SET_BY_CORE == request->initiator) {
            pHddCtx->reg.cc_src = COUNTRY_CODE_SET_BY_CORE;
            vos_set_cc_source(CNSS_SOURCE_CORE);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
            if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
#else
            if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
#endif
                reset = true;
        } else if (NL80211_REGDOM_SET_BY_DRIVER == request->initiator) {
            pHddCtx->reg.cc_src = COUNTRY_CODE_SET_BY_DRIVER;
        } else {
            if (vos_get_cc_source() == CNSS_SOURCE_11D)
                pHddCtx->reg.cc_src = COUNTRY_CODE_SET_BY_11D;
            else
                pHddCtx->reg.cc_src = COUNTRY_CODE_SET_BY_USER;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) && !defined(WITH_BACKPORTS)
            if ((request->alpha2[0] == '0') &&
                (request->alpha2[1] == '0') &&
                (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
            {
                restore_custom_reg_settings(wiphy);
                reset = true;
            }
#endif
        }

        /* first lookup the country in the local database */
        country_code[0] = request->alpha2[0];
        country_code[1] = request->alpha2[1];

        pHddCtx->reg.alpha2[0] = request->alpha2[0];
        pHddCtx->reg.alpha2[1] = request->alpha2[1];

        vos_update_reg_info(pHddCtx);

        regdmn_map_country_to_vos_regdmn(country_code, &temp_reg_domain);

        if (REGDOMAIN_COUNT == temp_reg_domain)
            temp_reg_domain = REGDOMAIN_WORLD;

        isVHT80Allowed = pHddCtx->isVHT80Allowed;
        regChannels =
            pnvEFSTable->halnv.tables.regDomains[temp_reg_domain].channels;
        if (create_linux_regulatory_entry(wiphy,
                                          nBandCapability,
                                          reset) == 0)
        {
            VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
                      (" regulatory entry created"));
        }

        if (pHddCtx->isVHT80Allowed != isVHT80Allowed)
            hdd_checkandupdate_phymode( pHddCtx);

        /* now pass the new country information to sme */
        if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
        {
            sme_GenericChangeCountryCode(pHddCtx->hHal, country_code,
                                         REGDOMAIN_COUNT);
        }
        else
        {
            sme_GenericChangeCountryCode(pHddCtx->hHal, country_code,
                                         temp_reg_domain);
        }

        if (pHddCtx->cfg_ini->sta_change_cc_via_beacon) {
            /* Due the firmware process, host side need to send
             * WMI_SCAN_CHAN_LIST_CMDID before WMI_PDEV_SET_REGDOMAIN_CMDID, so
             * that tx-power setting for operation channel can be applied,
             * so use timer to postpone SET_REGDOMAIN_CMDID
             */
            if (pHddCtx->reg.reg_set_timer.state == 0)
                timer_status = VOS_TIMER_STATE_UNUSED;
            else {
                do {
                    timer_status =
                    vos_timer_getCurrentState(&(pHddCtx->reg.reg_set_timer));
                } while(timer_status != VOS_TIMER_STATE_UNUSED);
            }
            vos_timer_init(&(pHddCtx->reg.reg_set_timer), VOS_TIMER_TYPE_SW,
                           hdd_debug_cc_timer_expired_handler,
                           (void *)pHddCtx);
            vos_timer_start(&(pHddCtx->reg.reg_set_timer), REG_SET_WAIT_MS);
        } else {
            regdmn_set_regval(&pHddCtx->reg);
        }

        /* set dfs_region info */
        vos_nv_set_dfs_region(request->dfs_region);

        regdmn_set_dfs_region(&pHddCtx->reg);

        hdd_set_dfs_regdomain(pHddCtx,false);

        if ((NL80211_REGDOM_SET_BY_DRIVER == request->initiator) ||
            (NL80211_REGDOM_SET_BY_USER == request->initiator))
            complete(&pHddCtx->reg_init);

    default:
        break;
    }

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)) || defined(WITH_BACKPORTS)
    return;
#else
    return 0;
#endif
}

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0))
void wlan_hdd_linux_reg_notifier(struct wiphy *wiphy,
                struct regulatory_request *request)
{
    vos_ssr_protect(__func__);
    __wlan_hdd_linux_reg_notifier(wiphy, request);
    vos_ssr_unprotect(__func__);
    return;
}
#else
int wlan_hdd_linux_reg_notifier(struct wiphy *wiphy,
                struct regulatory_request *request)
{
    int ret;
    vos_ssr_protect(__func__);
    ret = __wlan_hdd_linux_reg_notifier(wiphy, request);
    vos_ssr_unprotect(__func__);
    return ret;
}
#endif

/* initialize wiphy from EEPROM */
VOS_STATUS vos_init_wiphy_from_eeprom(void)
{
   v_CONTEXT_t pVosContext = NULL;
   hdd_context_t *pHddCtx = NULL;
   struct wiphy *wiphy = NULL;

   pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

   if (!pVosContext)
      return VOS_STATUS_E_EXISTS;

   pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
   if (!pHddCtx)
   {
      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
            ("Invalid pHddCtx pointer"));
      return VOS_STATUS_E_FAULT;
   }

   wiphy = pHddCtx->wiphy;

   if (reg_init_from_eeprom(pHddCtx, &pHddCtx->reg, wiphy))
   {
      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
            ("Error during regulatory init from EEPROM"));
      return VOS_STATUS_E_FAULT;
   }

   if (is_world_regd(pHddCtx->reg.reg_domain)) {
      temp_reg_domain = REGDOMAIN_WORLD;
      if (create_linux_regulatory_entry(wiphy,
                                        pHddCtx->cfg_ini->nBandCapability,
                                        true) != 0) {
         VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
               ("Error while creating regulatory entry"));
         return VOS_STATUS_E_FAULT;
      }
   }

   init_completion(&pHddCtx->reg_init);

   /* send CTL info to firmware */
   regdmn_set_regval(&pHddCtx->reg);

   return VOS_STATUS_SUCCESS;
}

/* initialize wiphy from NV.bin */
VOS_STATUS vos_init_wiphy_from_nv_bin(void)
{
    int i, j, m;
    int k = 0;
    v_REGDOMAIN_t reg_domain;
    v_CONTEXT_t pVosContext = NULL;
    hdd_context_t *pHddCtx = NULL;
    struct wiphy *wiphy = NULL;

    pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);

    if (NULL != pVosContext)
        pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
    else
        return VOS_STATUS_E_EXISTS;

    if (NULL == pHddCtx)
    {
        VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
                   ("Invalid pHddCtx pointer") );
        return VOS_STATUS_E_FAULT;
    }

    wiphy = pHddCtx->wiphy;

    /* Update regulatory structure in HDD */
    pHddCtx->reg.alpha2[0] =
       pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[0];
    pHddCtx->reg.alpha2[1] =
       pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[1];
    pHddCtx->reg.cc_src = COUNTRY_CODE_SET_BY_DRIVER;

    vos_update_reg_info(pHddCtx);

    if  (('0' == pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[0])
         &&
         ('0' == pnvEFSTable->halnv.tables.defaultCountryTable.countryCode[1]))
    {
        /* default country is world roaming */

        reg_domain = REGDOMAIN_WORLD;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
        wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#else
        wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif
    }
    else if (REGDOMAIN_WORLD ==
         pnvEFSTable->halnv.tables.defaultCountryTable.regDomain) {

        reg_domain = pnvEFSTable->halnv.tables.defaultCountryTable.regDomain;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
        wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#else
        wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif
    }
    else {

        reg_domain = pnvEFSTable->halnv.tables.defaultCountryTable.regDomain;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
        wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
#else
        wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
#endif
    }
    m = 0;
    for (i = 0; i < IEEE80211_NUM_BANDS; i++)
    {

        if (wiphy->bands[i] == NULL)
            continue;

        /* internal channels[] is one continous array for both 2G and 5G bands
           m is internal starting channel index for each band */

        for (j = 0; j < wiphy->bands[i]->n_channels; j++)
        {
            /* k = (m + j) is internal current channel index */
            k = m + j;

            if (pnvEFSTable->halnv.tables.regDomains[reg_domain].channels[k].enabled ==
                NV_CHANNEL_DISABLE)
                wiphy->bands[i]->channels[j].flags |= IEEE80211_CHAN_DISABLED;

            else if (pnvEFSTable->halnv.tables.regDomains[reg_domain].channels[k].enabled ==
                     NV_CHANNEL_DFS) {

                wiphy->bands[i]->channels[j].flags |= IEEE80211_CHAN_PASSIVE_SCAN;

                wiphy->bands[i]->channels[j].max_power =
                    (pnvEFSTable->halnv.tables.regDomains[reg_domain].channels[k].pwrLimit)*100;
            }

            else if (pnvEFSTable->halnv.tables.regDomains[reg_domain].channels[k].enabled ==
                     NV_CHANNEL_ENABLE) {

                wiphy->bands[i]->channels[j].max_power =
                    (pnvEFSTable->halnv.tables.regDomains[reg_domain].channels[k].pwrLimit)*100;
            }
        }

        m += wiphy->bands[i]->n_channels;
    }

    return VOS_STATUS_SUCCESS;
}

