blob: ed7a04823b3f5446566b6ec9a6699ff4cc8f97af [file] [log] [blame]
/*
* Copyright (c) 2012-2017 The Linux Foundation. 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"
#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}
};
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,
};
// cache of country info table;
// this is re-initialized from data on binary file
// loaded on driver initialization if available
static CountryInfoTable_t countryInfoTable =
{
/* the first entry in the table is always the world domain */
141,
{
{REGDOMAIN_WORLD, {'0', '0'}}, // WORLD DOMAIN
{REGDOMAIN_FCC, {'A', 'D'}}, // ANDORRA
{REGDOMAIN_ETSI, {'A', 'E'}}, //UAE
{REGDOMAIN_ETSI, {'A', 'L'}}, //ALBANIA
{REGDOMAIN_ETSI, {'A', 'M'}}, //ARMENIA
{REGDOMAIN_ETSI, {'A', 'N'}}, //NETHERLANDS ANTILLES
{REGDOMAIN_FCC, {'A', 'R'}}, //ARGENTINA
{REGDOMAIN_FCC, {'A', 'S'}}, //AMERICAN SOMOA
{REGDOMAIN_ETSI, {'A', 'T'}}, //AUSTRIA
{REGDOMAIN_FCC, {'A', 'U'}}, //AUSTRALIA
{REGDOMAIN_ETSI , {'A', 'W'}}, //ARUBA
{REGDOMAIN_ETSI, {'A', 'Z'}}, //AZERBAIJAN
{REGDOMAIN_ETSI, {'B', 'A'}}, //BOSNIA AND HERZEGOVINA
{REGDOMAIN_FCC, {'B', 'B'}}, //BARBADOS
{REGDOMAIN_ETSI, {'B', 'D'}}, //BANGLADESH
{REGDOMAIN_ETSI, { 'B', 'E'}}, //BELGIUM
{REGDOMAIN_ETSI, {'B', 'G'}}, //BULGARIA
{REGDOMAIN_ETSI, {'B', 'H'}}, //BAHRAIN
{REGDOMAIN_ETSI, {'B', 'L'}}, //
{REGDOMAIN_FCC, {'B', 'M'}}, //BERMUDA
{REGDOMAIN_ETSI, {'B', 'N'}}, //BRUNEI DARUSSALAM
{REGDOMAIN_ETSI, {'B', 'O'}}, //BOLIVIA
{REGDOMAIN_ETSI, {'B', 'R'}}, //BRAZIL
{REGDOMAIN_FCC, {'B', 'S'}}, //BAHAMAS
{REGDOMAIN_ETSI, {'B', 'Y'}}, //BELARUS
{REGDOMAIN_ETSI, {'B', 'Z'}}, //BELIZE
{REGDOMAIN_FCC, {'C', 'A'}}, //CANADA
{REGDOMAIN_ETSI, {'C', 'H'}}, //SWITZERLAND
{REGDOMAIN_ETSI, {'C', 'L'}}, //CHILE
{REGDOMAIN_FCC, {'C', 'N'}}, //CHINA
{REGDOMAIN_FCC, {'C', 'O'}}, //COLOMBIA
{REGDOMAIN_ETSI, {'C', 'R'}}, //COSTA RICA
{REGDOMAIN_ETSI, {'C', 'S'}},
{REGDOMAIN_ETSI, {'C', 'Y'}}, //CYPRUS
{REGDOMAIN_ETSI, {'C', 'Z'}}, //CZECH REPUBLIC
{REGDOMAIN_ETSI, {'D', 'E'}}, //GERMANY
{REGDOMAIN_ETSI, {'D', 'K'}}, //DENMARK
{REGDOMAIN_FCC, {'D', 'M'}}, //DOMINICA
{REGDOMAIN_FCC, {'D', 'O'}}, //DOMINICAN REPUBLIC
{REGDOMAIN_ETSI, {'D', 'Z'}}, //ALGERIA
{REGDOMAIN_ETSI, {'E', 'C'}}, //ECUADOR
{REGDOMAIN_ETSI, {'E', 'E'}}, //ESTONIA
{REGDOMAIN_ETSI, {'E', 'G'}}, //EGYPT
{REGDOMAIN_ETSI, {'E', 'S'}}, //SPAIN
{REGDOMAIN_ETSI, {'F', 'I'}}, //FINLAND
{REGDOMAIN_ETSI, {'F', 'R'}}, //FRANCE
{REGDOMAIN_ETSI, {'G', 'B'}}, //UNITED KINGDOM
{REGDOMAIN_FCC, {'G', 'D'}}, //GRENADA
{REGDOMAIN_ETSI, {'G', 'E'}}, //GEORGIA
{REGDOMAIN_ETSI, {'G', 'F'}}, //FRENCH GUIANA
{REGDOMAIN_ETSI, {'G', 'L'}}, //GREENLAND
{REGDOMAIN_ETSI, {'G', 'P'}}, //GUADELOUPE
{REGDOMAIN_ETSI, {'G', 'R'}}, //GREECE
{REGDOMAIN_FCC, {'G', 'T'}}, //GUATEMALA
{REGDOMAIN_FCC, {'G', 'U'}}, //GUAM
{REGDOMAIN_ETSI, {'H', 'U'}}, //HUNGARY
{REGDOMAIN_ETSI, {'I', 'D'}}, //INDONESIA
{REGDOMAIN_ETSI, {'I', 'E'}}, //IRELAND
{REGDOMAIN_ETSI, {'I', 'L'}}, //ISRAEL
{REGDOMAIN_ETSI, {'I', 'N'}}, //INDIA
{REGDOMAIN_ETSI, {'I', 'R'}}, //IRAN, ISLAMIC REPUBLIC OF
{REGDOMAIN_ETSI, {'I', 'S'}}, //ICELNAD
{REGDOMAIN_ETSI, {'I', 'T'}}, //ITALY
{REGDOMAIN_FCC, {'J', 'M'}}, //JAMAICA
{REGDOMAIN_JAPAN, {'J', 'P'}}, //JAPAN
{REGDOMAIN_ETSI, {'J', 'O'}}, //JORDAN
{REGDOMAIN_ETSI, {'K', 'E'}}, //KENYA
{REGDOMAIN_ETSI, {'K', 'H'}}, //CAMBODIA
{REGDOMAIN_ETSI, {'K', 'P'}}, //KOREA, DEMOCRATIC PEOPLE's REPUBLIC OF
{REGDOMAIN_ETSI, {'K', 'R'}}, //KOREA, REPUBLIC OF
{REGDOMAIN_ETSI, {'K', 'W'}}, //KUWAIT
{REGDOMAIN_ETSI, {'K', 'Z'}}, //KAZAKHSTAN
{REGDOMAIN_ETSI, {'L', 'B'}}, //LEBANON
{REGDOMAIN_ETSI, {'L', 'I'}}, //LIECHTENSTEIN
{REGDOMAIN_ETSI, {'L', 'K'}}, //SRI-LANKA
{REGDOMAIN_ETSI, {'L', 'T'}}, //LITHUANIA
{REGDOMAIN_ETSI, {'L', 'U'}}, //LUXEMBOURG
{REGDOMAIN_ETSI, {'L','V'}}, //LATVIA
{REGDOMAIN_ETSI, {'M', 'A'}}, //MOROCCO
{REGDOMAIN_ETSI, {'M', 'C'}}, //MONACO
{REGDOMAIN_ETSI, {'M', 'K'}}, //MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF
{REGDOMAIN_FCC, {'M','N'}}, //MONGOLIA
{REGDOMAIN_FCC, {'M', 'O'}}, //MACAO
{REGDOMAIN_FCC, {'M', 'P'}}, //NORTHERN MARIANA ISLANDS
{REGDOMAIN_ETSI, {'M', 'Q'}}, //MARTINIQUE
{REGDOMAIN_FCC, {'M', 'T'}}, //MALTA
{REGDOMAIN_ETSI, {'M', 'U'}}, //MAURITIUS
{REGDOMAIN_ETSI, {'M', 'W'}}, //MALAWI
{REGDOMAIN_FCC, {'M', 'X'}}, //MEXICO
{REGDOMAIN_ETSI, {'M', 'Y'}}, //MALAYSIA
{REGDOMAIN_ETSI, {'N', 'A'}}, //NAMIBIA
{REGDOMAIN_ETSI, {'N', 'G'}}, //NIGERIA
{REGDOMAIN_FCC, {'N', 'I'}}, //NICARAGUA
{REGDOMAIN_ETSI, {'N', 'L'}}, //NETHERLANDS
{REGDOMAIN_ETSI, {'N', 'O'}}, //NORWAY
{REGDOMAIN_ETSI, {'N', 'P'}}, //NEPAL
{REGDOMAIN_FCC, {'N', 'Z'}}, //NEW-ZEALAND
{REGDOMAIN_FCC, {'O', 'M'}}, //OMAN
{REGDOMAIN_FCC, {'P', 'A'}}, //PANAMA
{REGDOMAIN_ETSI, {'P', 'E'}}, //PERU
{REGDOMAIN_ETSI, {'P', 'F'}}, //FRENCH POLYNESIA
{REGDOMAIN_ETSI, {'P', 'G'}}, //PAPUA NEW GUINEA
{REGDOMAIN_FCC, {'P', 'H'}}, //PHILIPPINES
{REGDOMAIN_ETSI, {'P', 'K'}}, //PAKISTAN
{REGDOMAIN_ETSI, {'P', 'L'}}, //POLAND
{REGDOMAIN_FCC, {'P', 'R'}}, //PUERTO RICO
{REGDOMAIN_FCC, {'P', 'S'}}, //PALESTINIAN TERRITORY, OCCUPIED
{REGDOMAIN_ETSI, {'P', 'T'}}, //PORTUGAL
{REGDOMAIN_FCC, {'P', 'Y'}}, //PARAGUAY
{REGDOMAIN_ETSI, {'Q', 'A'}}, //QATAR
{REGDOMAIN_ETSI, {'R', 'E'}}, //REUNION
{REGDOMAIN_ETSI, {'R', 'O'}}, //ROMAINIA
{REGDOMAIN_ETSI, {'R', 'S'}}, //SERBIA
{REGDOMAIN_ETSI, {'R', 'U'}}, //RUSSIA
{REGDOMAIN_FCC, {'R', 'W'}}, //RWANDA
{REGDOMAIN_ETSI, {'S', 'A'}}, //SAUDI ARABIA
{REGDOMAIN_ETSI, {'S', 'E'}}, //SWEDEN
{REGDOMAIN_ETSI, {'S', 'G'}}, //SINGAPORE
{REGDOMAIN_ETSI, {'S', 'I'}}, //SLOVENNIA
{REGDOMAIN_ETSI, {'S', 'K'}}, //SLOVAKIA
{REGDOMAIN_ETSI, {'S', 'V'}}, //EL SALVADOR
{REGDOMAIN_ETSI, {'S', 'Y'}}, //SYRIAN ARAB REPUBLIC
{REGDOMAIN_ETSI, {'T', 'H'}}, //THAILAND
{REGDOMAIN_ETSI, {'T', 'N'}}, //TUNISIA
{REGDOMAIN_ETSI, {'T', 'R'}}, //TURKEY
{REGDOMAIN_ETSI, {'T', 'T'}}, //TRINIDAD AND TOBAGO
{REGDOMAIN_FCC, {'T', 'W'}}, //TAIWAN, PRIVINCE OF CHINA
{REGDOMAIN_ETSI, {'T', 'Z'}}, //TANZANIA, UNITED REPUBLIC OF
{REGDOMAIN_ETSI, {'U', 'A'}}, //UKRAINE
{REGDOMAIN_ETSI, {'U', 'G'}}, //UGANDA
{REGDOMAIN_FCC, {'U', 'S'}}, //USA
{REGDOMAIN_ETSI, {'U', 'Y'}}, //URUGUAY
{REGDOMAIN_ETSI, {'U', 'Z'}}, //UZBEKISTAN
{REGDOMAIN_ETSI, {'V', 'E'}}, //VENEZUELA
{REGDOMAIN_FCC, {'V', 'I'}}, //VIRGIN ISLANDS, US
{REGDOMAIN_ETSI, {'V', 'N'}}, //VIETNAM
{REGDOMAIN_ETSI, {'Y', 'E'}}, //YEMEN
{REGDOMAIN_ETSI, {'Y', 'T'}}, //MAYOTTE
{REGDOMAIN_ETSI, {'Z', 'A'}}, //SOUTH AFRICA
{REGDOMAIN_ETSI, {'Z', 'W'}}, //ZIMBABWE
{REGDOMAIN_JAPAN, {'X', 'A'}}, //JAPAN PASSIVE
}
};
/*
* 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,
/* 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
wiphy_apply_custom_regulatory(wiphy, regd);
/*
* 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
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 = countryInfoTable.countryCount * (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 < countryInfoTable.countryCount; i++) {
vos_mem_copy(pBuffer, countryInfoTable.countryInfo[i].countryCode,
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 countryInfoTable.countryInfo[0].regDomain;
}
/**------------------------------------------------------------------------
\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;
int i;
int wait_result;
/* 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;
}
if (0 == countryInfoTable.countryCount)
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
("Reg domain table is empty") );
return VOS_STATUS_E_EMPTY;
}
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;
}
temp_reg_domain = REGDOMAIN_COUNT;
/* lookup the country in the local database */
for (i = 0; i < countryInfoTable.countryCount &&
REGDOMAIN_COUNT == temp_reg_domain; i++)
{
if (memcmp(country_code, countryInfoTable.countryInfo[i].countryCode,
VOS_COUNTRY_CODE_LEN) == 0)
{
/* country code is found */
/* record the temporary regulatory_domain as well */
temp_reg_domain = countryInfoTable.countryInfo[i].regDomain;
break;
}
}
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])) {
INIT_COMPLETION(pHddCtx->reg_init);
regulatory_hint(wiphy, country_code);
wait_for_completion_timeout(&pHddCtx->reg_init,
msecs_to_jiffies(REG_WAIT_TIME));
}
} 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);
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;
}
}
*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;
}
/* 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;
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");
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;
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;
}
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)
reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(wiphy->bands[i]->
channels[j].center_freq));
#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 {
/* 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 != -1)
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;
int i;
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);
temp_reg_domain = REGDOMAIN_COUNT;
for (i = 0; i < countryInfoTable.countryCount &&
REGDOMAIN_COUNT == temp_reg_domain; i++)
{
if (memcmp(country_code, countryInfoTable.countryInfo[i].countryCode,
VOS_COUNTRY_CODE_LEN) == 0)
{
/* country code is found */
/* record the temporary regulatory_domain as well */
temp_reg_domain = countryInfoTable.countryInfo[i].regDomain;
break;
}
}
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;
}